Repository: jonasschmedtmann/complete-javascript-course Branch: master Commit: 76b8b79b2994 Files: 154 Total size: 2.4 MB Directory structure: gitextract_6_nnlknq/ ├── 01-Fundamentals-Part-1/ │ ├── final/ │ │ ├── index.html │ │ └── script.js │ └── starter/ │ └── index.html ├── 02-Fundamentals-Part-2/ │ ├── final/ │ │ ├── index.html │ │ └── script.js │ └── starter/ │ ├── index.html │ └── script.js ├── 03-Developer-Skills/ │ ├── final/ │ │ ├── .prettierrc │ │ ├── index.html │ │ └── script.js │ └── starter/ │ ├── index.html │ └── script.js ├── 04-HTML-CSS/ │ └── final/ │ ├── index.html │ └── style.css ├── 05-Guess-My-Number/ │ ├── final/ │ │ ├── .prettierrc │ │ ├── index.html │ │ ├── script.js │ │ └── style.css │ └── starter/ │ ├── .prettierrc │ ├── index.html │ ├── script.js │ └── style.css ├── 06-Modal/ │ ├── final/ │ │ ├── .prettierrc │ │ ├── index.html │ │ ├── script.js │ │ └── style.css │ └── starter/ │ ├── .prettierrc │ ├── index.html │ ├── script.js │ └── style.css ├── 07-Pig-Game/ │ ├── final/ │ │ ├── .prettierrc │ │ ├── index.html │ │ ├── script.js │ │ └── style.css │ └── starter/ │ ├── .prettierrc │ ├── index.html │ ├── script.js │ └── style.css ├── 08-Behind-the-Scenes/ │ ├── final/ │ │ ├── .prettierrc │ │ ├── index.html │ │ └── script.js │ └── starter/ │ ├── .prettierrc │ ├── index.html │ └── script.js ├── 09-Data-Structures-Operators/ │ ├── final/ │ │ ├── .prettierrc │ │ ├── index.html │ │ └── script.js │ └── starter/ │ ├── .prettierrc │ ├── index.html │ └── script.js ├── 10-Functions/ │ ├── final/ │ │ ├── .prettierrc │ │ ├── index.html │ │ └── script.js │ └── starter/ │ ├── .prettierrc │ ├── index.html │ └── script.js ├── 11-Arrays-Bankist/ │ ├── final/ │ │ ├── .prettierrc │ │ ├── index.html │ │ ├── script.js │ │ └── style.css │ └── starter/ │ ├── .prettierrc │ ├── index.html │ ├── script.js │ └── style.css ├── 12-Numbers-Dates-Timers-Bankist/ │ ├── final/ │ │ ├── .prettierrc │ │ ├── index.html │ │ ├── script.js │ │ └── style.css │ └── starter/ │ ├── .prettierrc │ ├── index.html │ ├── script.js │ └── style.css ├── 13-Advanced-DOM-Bankist/ │ ├── final/ │ │ ├── .prettierrc │ │ ├── index.html │ │ ├── script.js │ │ └── style.css │ └── starter/ │ ├── .prettierrc │ ├── index.html │ ├── script.js │ └── style.css ├── 14-OOP/ │ ├── final/ │ │ ├── .prettierrc │ │ ├── index.html │ │ └── script.js │ └── starter/ │ ├── .prettierrc │ ├── index.html │ └── script.js ├── 15-Mapty/ │ ├── final/ │ │ ├── .prettierrc │ │ ├── index.html │ │ ├── other.js │ │ ├── script.js │ │ └── style.css │ └── starter/ │ ├── .prettierrc │ ├── index.html │ ├── script.js │ └── style.css ├── 16-Asynchronous/ │ ├── final/ │ │ ├── .prettierrc │ │ ├── index.html │ │ ├── script.js │ │ └── style.css │ └── starter/ │ ├── .prettierrc │ ├── index.html │ ├── script.js │ └── style.css ├── 17-Modern-JS-Modules-Tooling/ │ ├── final/ │ │ ├── .prettierrc │ │ ├── clean.js │ │ ├── dist/ │ │ │ ├── index.html │ │ │ ├── index.js │ │ │ ├── script.0b6e4fd3.js │ │ │ └── script.75da7f30.js │ │ ├── index.html │ │ ├── package.json │ │ ├── script.js │ │ └── shoppingCart.js │ └── starter/ │ ├── .prettierrc │ ├── clean.js │ ├── index.html │ └── script.js ├── 18-forkify/ │ ├── final/ │ │ ├── .gitignore │ │ ├── .prettierrc │ │ ├── README.md │ │ ├── index.html │ │ ├── package.json │ │ └── src/ │ │ ├── js/ │ │ │ ├── config.js │ │ │ ├── controller.js │ │ │ ├── helpers.js │ │ │ ├── model.js │ │ │ └── views/ │ │ │ ├── View.js │ │ │ ├── addRecipeView.js │ │ │ ├── bookmarksView.js │ │ │ ├── paginationView.js │ │ │ ├── previewView.js │ │ │ ├── recipeView.js │ │ │ ├── resultsView.js │ │ │ └── searchView.js │ │ └── sass/ │ │ ├── _base.scss │ │ ├── _components.scss │ │ ├── _header.scss │ │ ├── _preview.scss │ │ ├── _recipe.scss │ │ ├── _searchResults.scss │ │ ├── _upload.scss │ │ └── main.scss │ └── starter/ │ ├── .prettierrc │ ├── index.html │ └── src/ │ ├── js/ │ │ └── controller.js │ └── sass/ │ ├── _base.scss │ ├── _components.scss │ ├── _header.scss │ ├── _preview.scss │ ├── _recipe.scss │ ├── _searchResults.scss │ ├── _upload.scss │ └── main.scss └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: 01-Fundamentals-Part-1/final/index.html ================================================ JavaScript Fundamentals – Part 1

JavaScript Fundamentals – Part 1

================================================ FILE: 01-Fundamentals-Part-1/final/script.js ================================================ /* //////////////////////////////////// // Linking a JavaScript File let js = "amazing"; console.log(40 + 8 + 23 - 10); //////////////////////////////////// // Values and Variables console.log("Jonas"); console.log(23); let firstName = "Matilda"; console.log(firstName); console.log(firstName); console.log(firstName); // Variable name conventions let jonas_matilda = "JM"; let $function = 27; let person = "jonas"; let PI = 3.1415; let myFirstJob = "Coder"; let myCurrentJob = "Teacher"; let job1 = "programmer"; let job2 = "teacher"; console.log(myFirstJob); //////////////////////////////////// // Data Types let javascriptIsFun = true; console.log(javascriptIsFun); // console.log(typeof true); console.log(typeof javascriptIsFun); // console.log(typeof 23); // console.log(typeof 'Jonas'); javascriptIsFun = 'YES!'; console.log(typeof javascriptIsFun); let year; console.log(year); console.log(typeof year); year = 1991; console.log(typeof year); console.log(typeof null); //////////////////////////////////// // let, const and var let age = 30; age = 31; const birthYear = 1991; // birthYear = 1990; // const job; var job = 'programmer'; job = 'teacher' lastName = 'Schmedtmann'; console.log(lastName); //////////////////////////////////// // Basic Operators // Math operators const now = 2037; const ageJonas = now - 1991; const ageSarah = now - 2018; console.log(ageJonas, ageSarah); console.log(ageJonas * 2, ageJonas / 10, 2 ** 3); // 2 ** 3 means 2 to the power of 3 = 2 * 2 * 2 const firstName = 'Jonas'; const lastName = 'Schmedtmann'; console.log(firstName + ' ' + lastName); // Assignment operators let x = 10 + 5; // 15 x += 10; // x = x + 10 = 25 x *= 4; // x = x * 4 = 100 x++; // x = x + 1 x--; x--; console.log(x); // Comparison operators console.log(ageJonas > ageSarah); // >, <, >=, <= console.log(ageSarah >= 18); const isFullAge = ageSarah >= 18; console.log(now - 1991 > now - 2018); //////////////////////////////////// // Operator Precedence const now = 2037; const ageJonas = now - 1991; const ageSarah = now - 2018; console.log(now - 1991 > now - 2018); let x, y; x = y = 25 - 10 - 5; // x = y = 10, x = 10 console.log(x, y); const averageAge = (ageJonas + ageSarah) / 2; console.log(ageJonas, ageSarah, averageAge); */ //////////////////////////////////// // Coding Challenge #1 /* Mark and John are trying to compare their BMI (Body Mass Index), which is calculated using the formula: BMI = mass / height ** 2 = mass / (height * height). (mass in kg and height in meter). 1. Store Mark's and John's mass and height in variables 2. Calculate both their BMIs using the formula (you can even implement both versions) 3. Create a boolean variable 'markHigherBMI' containing information about whether Mark has a higher BMI than John. TEST DATA 1: Marks weights 78 kg and is 1.69 m tall. John weights 92 kg and is 1.95 m tall. TEST DATA 2: Marks weights 95 kg and is 1.88 m tall. John weights 85 kg and is 1.76 m tall. GOOD LUCK 😀 */ // const massMark = 78; // const heightMark = 1.69; // const massJohn = 92; // const heightJohn = 1.95; /* const massMark = 95; const heightMark = 1.88; const massJohn = 85; const heightJohn = 1.76; const BMIMark = massMark / heightMark ** 2; const BMIJohn = massJohn / (heightJohn * heightJohn); const markHigherBMI = BMIMark > BMIJohn; console.log(BMIMark, BMIJohn, markHigherBMI); //////////////////////////////////// // Strings and Template Literals const firstName = 'Jonas'; const job = 'teacher'; const birthYear = 1991; const year = 2037; const jonas = "I'm " + firstName + ', a ' + (year - birthYear) + ' year old ' + job + '!'; console.log(jonas); const jonasNew = `I'm ${firstName}, a ${year - birthYear} year old ${job}!`; console.log(jonasNew); console.log(`Just a regular string...`); console.log('String with \n\ multiple \n\ lines'); console.log(`String multiple lines`); //////////////////////////////////// // Taking Decisions: if / else Statements const age = 15; if (age >= 18) { console.log('Sarah can start driving license 🚗'); } else { const yearsLeft = 18 - age; console.log(`Sarah is too young. Wait another ${yearsLeft} years :)`); } const birthYear = 2012; let century; if (birthYear <= 2000) { century = 20; } else { century = 21; } console.log(century); */ //////////////////////////////////// // Coding Challenge #2 /* Use the BMI example from Challenge #1, and the code you already wrote, and improve it: 1. Print a nice output to the console, saying who has the higher BMI. The message can be either "Mark's BMI is higher than John's!" or "John's BMI is higher than Mark's!" 2. Use a template literal to include the BMI values in the outputs. Example: "Mark's BMI (28.3) is higher than John's (23.9)!" HINT: Use an if/else statement 😉 GOOD LUCK 😀 */ /* const massMark = 78; const heightMark = 1.69; const massJohn = 92; const heightJohn = 1.95; // const massMark = 95; // const heightMark = 1.88; // const massJohn = 85; // const heightJohn = 1.76; const BMIMark = massMark / heightMark ** 2; const BMIJohn = massJohn / (heightJohn * heightJohn); console.log(BMIMark, BMIJohn); if (BMIMark > BMIJohn) { console.log(`Mark's BMI (${BMIMark}) is higher than John's (${BMIJohn})!`) } else { console.log(`John's BMI (${BMIJohn}) is higher than Marks's (${BMIMark})!`) } //////////////////////////////////// // Type Conversion and Coercion // type conversion const inputYear = '1991'; console.log(Number(inputYear), inputYear); console.log(Number(inputYear) + 18); console.log(Number('Jonas')); console.log(typeof NaN); console.log(String(23), 23); // type coercion console.log('I am ' + 23 + ' years old'); console.log('23' - '10' - 3); console.log('23' / '2'); let n = '1' + 1; // '11' n = n - 1; console.log(n); //////////////////////////////////// // Truthy and Falsy Values // 5 falsy values: 0, '', undefined, null, NaN console.log(Boolean(0)); console.log(Boolean(undefined)); console.log(Boolean('Jonas')); console.log(Boolean({})); console.log(Boolean('')); const money = 100; if (money) { console.log("Don't spend it all ;)"); } else { console.log('You should get a job!'); } let height = 0; if (height) { console.log('YAY! Height is defined'); } else { console.log('Height is UNDEFINED'); } //////////////////////////////////// // Equality Operators: == vs. === const age = '18'; if (age === 18) console.log('You just became an adult :D (strict)'); if (age == 18) console.log('You just became an adult :D (loose)'); const favourite = Number(prompt("What's your favourite number?")); console.log(favourite); console.log(typeof favourite); if (favourite === 23) { // 22 === 23 -> FALSE console.log('Cool! 23 is an amzaing number!') } else if (favourite === 7) { console.log('7 is also a cool number') } else if (favourite === 9) { console.log('9 is also a cool number') } else { console.log('Number is not 23 or 7 or 9') } if (favourite !== 23) console.log('Why not 23?'); //////////////////////////////////// // Logical Operators const hasDriversLicense = true; // A const hasGoodVision = true; // B console.log(hasDriversLicense && hasGoodVision); console.log(hasDriversLicense || hasGoodVision); console.log(!hasDriversLicense); // if (hasDriversLicense && hasGoodVision) { // console.log('Sarah is able to drive!'); // } else { // console.log('Someone else should drive...'); // } const isTired = false; // C console.log(hasDriversLicense && hasGoodVision && isTired); if (hasDriversLicense && hasGoodVision && !isTired) { console.log('Sarah is able to drive!'); } else { console.log('Someone else should drive...'); } */ //////////////////////////////////// // Coding Challenge #3 /* There are two gymnastics teams, Dolphins and Koalas. They compete against each other 3 times. The winner with the highest average score wins the a trophy! 1. Calculate the average score for each team, using the test data below 2. Compare the team's average scores to determine the winner of the competition, and print it to the console. Don't forget that there can be a draw, so test for that as well (draw means they have the same average score). 3. BONUS 1: Include a requirement for a minimum score of 100. With this rule, a team only wins if it has a higher score than the other team, and the same time a score of at least 100 points. HINT: Use a logical operator to test for minimum score, as well as multiple else-if blocks 😉 4. BONUS 2: Minimum score also applies to a draw! So a draw only happens when both teams have the same score and both have a score greater or equal 100 points. Otherwise, no team wins the trophy. TEST DATA: Dolphins score 96, 108 and 89. Koalas score 88, 91 and 110 TEST DATA BONUS 1: Dolphins score 97, 112 and 101. Koalas score 109, 95 and 123 TEST DATA BONUS 2: Dolphins score 97, 112 and 101. Koalas score 109, 95 and 106 GOOD LUCK 😀 */ /* // const scoreDolphins = (96 + 108 + 89) / 3; // const scoreKoalas = (88 + 91 + 110) / 3; // console.log(scoreDolphins, scoreKoalas); // if (scoreDolphins > scoreKoalas) { // console.log('Dolphins win the trophy 🏆'); // } else if (scoreKoalas > scoreDolphins) { // console.log('Koalas win the trophy 🏆'); // } else if (scoreDolphins === scoreKoalas) { // console.log('Both win the trophy!'); // } // BONUS 1 const scoreDolphins = (97 + 112 + 80) / 3; const scoreKoalas = (109 + 95 + 50) / 3; console.log(scoreDolphins, scoreKoalas); if (scoreDolphins > scoreKoalas && scoreDolphins >= 100) { console.log('Dolphins win the trophy 🏆'); } else if (scoreKoalas > scoreDolphins && scoreKoalas >= 100) { console.log('Koalas win the trophy 🏆'); } else if (scoreDolphins === scoreKoalas && scoreDolphins >= 100 && scoreKoalas >= 100) { console.log('Both win the trophy!'); } else { console.log('No one wins the trophy 😭'); } //////////////////////////////////// // The switch Statement const day = 'friday'; switch (day) { case 'monday': // day === 'monday' console.log('Plan course structure'); console.log('Go to coding meetup'); break; case 'tuesday': console.log('Prepare theory videos'); break; case 'wednesday': case 'thursday': console.log('Write code examples'); break; case 'friday': console.log('Record videos'); break; case 'saturday': case 'sunday': console.log('Enjoy the weekend :D'); break; default: console.log('Not a valid day!'); } if (day === 'monday') { console.log('Plan course structure'); console.log('Go to coding meetup'); } else if (day === 'tuesday') { console.log('Prepare theory videos'); } else if (day === 'wednesday' || day === 'thursday') { console.log('Write code examples'); } else if (day === 'friday') { console.log('Record videos'); } else if (day === 'saturday' || day === 'sunday') { console.log('Enjoy the weekend :D'); } else { console.log('Not a valid day!'); } //////////////////////////////////// // Statements and Expressions 3 + 4 1991 true && false && !false if (23 > 10) { const str = '23 is bigger'; } const me = 'Jonas'; console.log(`I'm ${2037 - 1991} years old ${me}`); //////////////////////////////////// // The Conditional (Ternary) Operator const age = 23; // age >= 18 ? console.log('I like to drink wine 🍷') : console.log('I like to drink water 💧'); const drink = age >= 18 ? 'wine 🍷' : 'water 💧'; console.log(drink); let drink2; if (age >= 18) { drink2 = 'wine 🍷'; } else { drink2 = 'water 💧'; } console.log(drink2); console.log(`I like to drink ${age >= 18 ? 'wine 🍷' : 'water 💧'}`); */ //////////////////////////////////// // Coding Challenge #4 /* Steven wants to build a very simple tip calculator for whenever he goes eating in a resturant. In his country, it's usual to tip 15% if the bill value is between 50 and 300. If the value is different, the tip is 20%. 1. Your task is to caluclate the tip, depending on the bill value. Create a variable called 'tip' for this. It's not allowed to use an if/else statement 😅 (If it's easier for you, you can start with an if/else statement, and then try to convert it to a ternary operator!) 2. Print a string to the console containing the bill value, the tip, and the final value (bill + tip). Example: 'The bill was 275, the tip was 41.25, and the total value 316.25' TEST DATA: Test for bill values 275, 40 and 430 HINT: To calculate 20% of a value, simply multiply it by 20/100 = 0.2 HINT: Value X is between 50 and 300, if it's >= 50 && <= 300 😉 GOOD LUCK 😀 */ /* const bill = 430; const tip = bill <= 300 && bill >= 50 ? bill * 0.15 : bill * 0.2; console.log(`The bill was ${bill}, the tip was ${tip}, and the total value ${bill + tip}`); */ ================================================ FILE: 01-Fundamentals-Part-1/starter/index.html ================================================ JavaScript Fundamentals – Part 1

JavaScript Fundamentals – Part 1

================================================ FILE: 02-Fundamentals-Part-2/final/index.html ================================================ JavaScript Fundamentals – Part 2

JavaScript Fundamentals – Part 2

================================================ FILE: 02-Fundamentals-Part-2/final/script.js ================================================ 'use strict'; /* /////////////////////////////////////// // Activating Strict Mode let hasDriversLicense = false; const passTest = true; if (passTest) hasDriversLicense = true; if (hasDriversLicense) console.log('I can drive :D'); // const interface = 'Audio'; // const private = 534; /////////////////////////////////////// // Functions function logger() { console.log('My name is Jonas'); } // calling / running / invoking function logger(); logger(); logger(); function fruitProcessor(apples, oranges) { const juice = `Juice with ${apples} apples and ${oranges} oranges.`; return juice; } const appleJuice = fruitProcessor(5, 0); console.log(appleJuice); const appleOrangeJuice = fruitProcessor(2, 4); console.log(appleOrangeJuice); const num = Number('23'); /////////////////////////////////////// // Function Declarations vs. Expressions // Function declaration function calcAge1(birthYeah) { return 2037 - birthYeah; } const age1 = calcAge1(1991); // Function expression const calcAge2 = function (birthYeah) { return 2037 - birthYeah; } const age2 = calcAge2(1991); console.log(age1, age2); /////////////////////////////////////// // Arrow functions const calcAge3 = birthYeah => 2037 - birthYeah; const age3 = calcAge3(1991); console.log(age3); const yearsUntilRetirement = (birthYeah, firstName) => { const age = 2037 - birthYeah; const retirement = 65 - age; // return retirement; return `${firstName} retires in ${retirement} years`; } console.log(yearsUntilRetirement(1991, 'Jonas')); console.log(yearsUntilRetirement(1980, 'Bob')); /////////////////////////////////////// // Functions Calling Other Functions function cutFruitPieces(fruit) { return fruit * 4; } function fruitProcessor(apples, oranges) { const applePieces = cutFruitPieces(apples); const orangePieces = cutFruitPieces(oranges); const juice = `Juice with ${applePieces} piece of apple and ${orangePieces} pieces of orange.`; return juice; } console.log(fruitProcessor(2, 3)); /////////////////////////////////////// // Reviewing Functions const calcAge = function (birthYeah) { return 2037 - birthYeah; } const yearsUntilRetirement = function (birthYeah, firstName) { const age = calcAge(birthYeah); const retirement = 65 - age; if (retirement > 0) { console.log(`${firstName} retires in ${retirement} years`); return retirement; } else { console.log(`${firstName} has already retired 🎉`); return -1; } } console.log(yearsUntilRetirement(1991, 'Jonas')); console.log(yearsUntilRetirement(1950, 'Mike')); */ /////////////////////////////////////// // Coding Challenge #1 /* Back to the two gymnastics teams, the Dolphins and the Koalas! There is a new gymnastics discipline, which works differently. Each team competes 3 times, and then the average of the 3 scores is calculated (so one average score per team). A team ONLY wins if it has at least DOUBLE the average score of the other team. Otherwise, no team wins! 1. Create an arrow function 'calcAverage' to calculate the average of 3 scores 2. Use the function to calculate the average for both teams 3. Create a function 'checkWinner' that takes the average score of each team as parameters ('avgDolhins' and 'avgKoalas'), and then logs the winner to the console, together with the victory points, according to the rule above. Example: "Koalas win (30 vs. 13)". 4. Use the 'checkWinner' function to determine the winner for both DATA 1 and DATA 2. 5. Ignore draws this time. TEST DATA 1: Dolphins score 44, 23 and 71. Koalas score 65, 54 and 49 TEST DATA 2: Dolphins score 85, 54 and 41. Koalas score 23, 34 and 27 HINT: To calculate average of 3 values, add them all together and divide by 3 HINT: To check if number A is at least double number B, check for A >= 2 * B. Apply this to the team's average scores 😉 GOOD LUCK 😀 */ /* const calcAverage = (a, b, c) => (a + b + c) / 3; console.log(calcAverage(3, 4, 5)); // Test 1 let scoreDolphins = calcAverage(44, 23, 71); let scoreKoalas = calcAverage(65, 54, 49); console.log(scoreDolphins, scoreKoalas); const checkWinner = function (avgDolphins, avgKoalas) { if (avgDolphins >= 2 * avgKoalas) { console.log(`Dolphins win 🏆 (${avgDolphins} vs. ${avgKoalas})`); } else if (avgKoalas >= 2 * avgDolphins) { console.log(`Koalas win 🏆 (${avgKoalas} vs. ${avgDolphins})`); } else { console.log('No team wins...'); } } checkWinner(scoreDolphins, scoreKoalas); checkWinner(576, 111); // Test 2 scoreDolphins = calcAverage(85, 54, 41); scoreKoalas = calcAverage(23, 34, 27); console.log(scoreDolphins, scoreKoalas); checkWinner(scoreDolphins, scoreKoalas); /////////////////////////////////////// // Introduction to Arrays const friend1 = 'Michael'; const friend2 = 'Steven'; const friend3 = 'Peter'; const friends = ['Michael', 'Steven', 'Peter']; console.log(friends); const y = new Array(1991, 1984, 2008, 2020); console.log(friends[0]); console.log(friends[2]); console.log(friends.length); console.log(friends[friends.length - 1]); friends[2] = 'Jay'; console.log(friends); // friends = ['Bob', 'Alice'] const firstName = 'Jonas'; const jonas = [firstName, 'Schmedtmann', 2037 - 1991, 'teacher', friends]; console.log(jonas); console.log(jonas.length); // Exercise const calcAge = function (birthYeah) { return 2037 - birthYeah; } const years = [1990, 1967, 2002, 2010, 2018]; const age1 = calcAge(years[0]); const age2 = calcAge(years[1]); const age3 = calcAge(years[years.length - 1]); console.log(age1, age2, age3); const ages = [calcAge(years[0]), calcAge(years[1]), calcAge(years[years.length - 1])]; console.log(ages); /////////////////////////////////////// // Basic Array Operations (Methods) const friends = ['Michael', 'Steven', 'Peter']; // Add elements const newLength = friends.push('Jay'); console.log(friends); console.log(newLength); friends.unshift('John'); console.log(friends); // Remove elements friends.pop(); // Last const popped = friends.pop(); console.log(popped); console.log(friends); friends.shift(); // First console.log(friends); console.log(friends.indexOf('Steven')); console.log(friends.indexOf('Bob')); friends.push(23); console.log(friends.includes('Steven')); console.log(friends.includes('Bob')); console.log(friends.includes(23)); if (friends.includes('Steven')) { console.log('You have a friend called Steven'); } */ /////////////////////////////////////// // Coding Challenge #2 /* Steven is still building his tip calculator, using the same rules as before: Tip 15% of the bill if the bill value is between 50 and 300, and if the value is different, the tip is 20%. 1. Write a function 'calcTip' that takes any bill value as an input and returns the corresponding tip, calculated based on the rules above (you can check out the code from first tip calculator challenge if you need to). Use the function type you like the most. Test the function using a bill value of 100. 2. And now let's use arrays! So create an array 'bills' containing the test data below. 3. Create an array 'tips' containing the tip value for each bill, calculated from the function you created before. 4. BONUS: Create an array 'total' containing the total values, so the bill + tip. TEST DATA: 125, 555 and 44 HINT: Remember that an array needs a value in each position, and that value can actually be the returned value of a function! So you can just call a function as array values (so don't store the tip values in separate variables first, but right in the new array) 😉 GOOD LUCK 😀 */ /* const calcTip = function (bill) { return bill >= 50 && bill <= 300 ? bill * 0.15 : bill * 0.2; } // const calcTip = bill => bill >= 50 && bill <= 300 ? bill * 0.15 : bill * 0.2; const bills = [125, 555, 44]; const tips = [calcTip(bills[0]), calcTip(bills[1]), calcTip(bills[2])]; const totals = [bills[0] + tips[0], bills[1] + tips[1], bills[2] + tips[2]]; console.log(bills, tips, totals); /////////////////////////////////////// // Introduction to Objects const jonasArray = [ 'Jonas', 'Schmedtmann', 2037 - 1991, 'teacher', ['Michael', 'Peter', 'Steven'] ]; const jonas = { firstName: 'Jonas', lastName: 'Schmedtmann', age: 2037 - 1991, job: 'teacher', friends: ['Michael', 'Peter', 'Steven'] }; /////////////////////////////////////// // Dot vs. Bracket Notation const jonas = { firstName: 'Jonas', lastName: 'Schmedtmann', age: 2037 - 1991, job: 'teacher', friends: ['Michael', 'Peter', 'Steven'] }; console.log(jonas); console.log(jonas.lastName); console.log(jonas['lastName']); const nameKey = 'Name'; console.log(jonas['first' + nameKey]); console.log(jonas['last' + nameKey]); // console.log(jonas.'last' + nameKey) const interestedIn = prompt('What do you want to know about Jonas? Choose between firstName, lastName, age, job, and friends'); if (jonas[interestedIn]) { console.log(jonas[interestedIn]); } else { console.log('Wrong request! Choose between firstName, lastName, age, job, and friends'); } jonas.location = 'Portugal'; jonas['twitter'] = '@jonasschmedtman'; console.log(jonas); // Challenge // "Jonas has 3 friends, and his best friend is called Michael" console.log(`${jonas.firstName} has ${jonas.friends.length} friends, and his best friend is called ${jonas.friends[0]}`); /////////////////////////////////////// // Object Methods const jonas = { firstName: 'Jonas', lastName: 'Schmedtmann', birthYeah: 1991, job: 'teacher', friends: ['Michael', 'Peter', 'Steven'], hasDriversLicense: true, // calcAge: function (birthYeah) { // return 2037 - birthYeah; // } // calcAge: function () { // // console.log(this); // return 2037 - this.birthYeah; // } calcAge: function () { this.age = 2037 - this.birthYeah; return this.age; }, getSummary: function () { return `${this.firstName} is a ${this.calcAge()}-year old ${jonas.job}, and he has ${this.hasDriversLicense ? 'a' : 'no'} driver's license.` } }; console.log(jonas.calcAge()); console.log(jonas.age); console.log(jonas.age); console.log(jonas.age); // Challenge // "Jonas is a 46-year old teacher, and he has a driver's license" console.log(jonas.getSummary()); */ /////////////////////////////////////// // Coding Challenge #3 /* Let's go back to Mark and John comparing their BMIs! This time, let's use objects to implement the calculations! Remember: BMI = mass / height ** 2 = mass / (height * height). (mass in kg and height in meter) 1. For each of them, create an object with properties for their full name, mass, and height (Mark Miller and John Smith) 2. Create a 'calcBMI' method on each object to calculate the BMI (the same method on both objects). Store the BMI value to a property, and also return it from the method. 3. Log to the console who has the higher BMI, together with the full name and the respective BMI. Example: "John Smith's BMI (28.3) is higher than Mark Miller's (23.9)!" TEST DATA: Marks weights 78 kg and is 1.69 m tall. John weights 92 kg and is 1.95 m tall. GOOD LUCK 😀 */ /* const mark = { fullName: 'Mark Miller', mass: 78, height: 1.69, calcBMI: function () { this.bmi = this.mass / this.height ** 2; return this.bmi; } }; const john = { fullName: 'John Smith', mass: 92, height: 1.95, calcBMI: function () { this.bmi = this.mass / this.height ** 2; return this.bmi; } }; mark.calcBMI(); john.calcBMI(); console.log(mark.bmi, john.bmi); // "John Smith's BMI (28.3) is higher than Mark Miller's (23.9)!" if (mark.bmi > john.bmi) { console.log(`${mark.fullName}'s BMI (${mark.bmi}) is higher than ${john.fullName}'s BMI (${john.bmi})`) } else if (john.bmi > mark.bmi) { console.log(`${john.fullName}'s BMI (${john.bmi}) is higher than ${mark.fullName}'s BMI (${mark.bmi})`) } /////////////////////////////////////// // Iteration: The for Loop // console.log('Lifting weights repetition 1 🏋️‍♀️'); // console.log('Lifting weights repetition 2 🏋️‍♀️'); // console.log('Lifting weights repetition 3 🏋️‍♀️'); // console.log('Lifting weights repetition 4 🏋️‍♀️'); // console.log('Lifting weights repetition 5 🏋️‍♀️'); // console.log('Lifting weights repetition 6 🏋️‍♀️'); // console.log('Lifting weights repetition 7 🏋️‍♀️'); // console.log('Lifting weights repetition 8 🏋️‍♀️'); // console.log('Lifting weights repetition 9 🏋️‍♀️'); // console.log('Lifting weights repetition 10 🏋️‍♀️'); // for loop keeps running while condition is TRUE for (let rep = 1; rep <= 30; rep++) { console.log(`Lifting weights repetition ${rep} 🏋️‍♀️`); } /////////////////////////////////////// // Looping Arrays, Breaking and Continuing const jonas = [ 'Jonas', 'Schmedtmann', 2037 - 1991, 'teacher', ['Michael', 'Peter', 'Steven'], true ]; const types = []; // console.log(jonas[0]) // console.log(jonas[1]) // ... // console.log(jonas[4]) // jonas[5] does NOT exist for (let i = 0; i < jonas.length; i++) { // Reading from jonas array console.log(jonas[i], typeof jonas[i]); // Filling types array // types[i] = typeof jonas[i]; types.push(typeof jonas[i]); } console.log(types); const years = [1991, 2007, 1969, 2020]; const ages = []; for (let i = 0; i < years.length; i++) { ages.push(2037 - years[i]); } console.log(ages); // continue and break console.log('--- ONLY STRINGS ---') for (let i = 0; i < jonas.length; i++) { if (typeof jonas[i] !== 'string') continue; console.log(jonas[i], typeof jonas[i]); } console.log('--- BREAK WITH NUMBER ---') for (let i = 0; i < jonas.length; i++) { if (typeof jonas[i] === 'number') break; console.log(jonas[i], typeof jonas[i]); } /////////////////////////////////////// // Looping Backwards and Loops in Loops const jonas = [ 'Jonas', 'Schmedtmann', 2037 - 1991, 'teacher', ['Michael', 'Peter', 'Steven'], true ]; // 0, 1, ..., 4 // 4, 3, ..., 0 for (let i = jonas.length - 1; i >= 0; i--) { console.log(i, jonas[i]); } for (let exercise = 1; exercise < 4; exercise++) { console.log(`-------- Starting exercise ${exercise}`); for (let rep = 1; rep < 6; rep++) { console.log(`Exercise ${exercise}: Lifting weight repetition ${rep} 🏋️‍♀️`); } } /////////////////////////////////////// // The while Loop for (let rep = 1; rep <= 10; rep++) { console.log(`Lifting weights repetition ${rep} 🏋️‍♀️`); } let rep = 1; while (rep <= 10) { // console.log(`WHILE: Lifting weights repetition ${rep} 🏋️‍♀️`); rep++; } let dice = Math.trunc(Math.random() * 6) + 1; while (dice !== 6) { console.log(`You rolled a ${dice}`); dice = Math.trunc(Math.random() * 6) + 1; if (dice === 6) console.log('Loop is about to end...'); } */ /////////////////////////////////////// // Coding Challenge #4 /* Let's improve Steven's tip calculator even more, this time using loops! 1. Create an array 'bills' containing all 10 test bill values 2. Create empty arrays for the tips and the totals ('tips' and 'totals') 3. Use the 'calcTip' function we wrote before (no need to repeat) to calculate tips and total values (bill + tip) for every bill value in the bills array. Use a for loop to perform the 10 calculations! TEST DATA: 22, 295, 176, 440, 37, 105, 10, 1100, 86 and 52 HINT: Call calcTip in the loop and use the push method to add values to the tips and totals arrays 😉 4. BONUS: Write a function 'calcAverage' which takes an array called 'arr' as an argument. This function calculates the average of all numbers in the given array. This is a DIFFICULT challenge (we haven't done this before)! Here is how to solve it: 4.1. First, you will need to add up all values in the array. To do the addition, start by creating a variable 'sum' that starts at 0. Then loop over the array using a for loop. In each iteration, add the current value to the 'sum' variable. This way, by the end of the loop, you have all values added together 4.2. To calculate the average, divide the sum you calculated before by the length of the array (because that's the number of elements) 4.3. Call the function with the 'totals' array GOOD LUCK 😀 */ /* const calcTip = function (bill) { return bill >= 50 && bill <= 300 ? bill * 0.15 : bill * 0.2; } const bills = [22, 295, 176, 440, 37, 105, 10, 1100, 86, 52]; const tips = []; const totals = []; for (let i = 0; i < bills.length; i++) { const tip = calcTip(bills[i]); tips.push(tip); totals.push(tip + bills[i]); } console.log(bills, tips, totals); const calcAverage = function (arr) { let sum = 0; for (let i = 0; i < arr.length; i++) { // sum = sum + arr[i]; sum += arr[i]; } return sum / arr.length; } console.log(calcAverage([2, 3, 7])); console.log(calcAverage(totals)); console.log(calcAverage(tips)); */ ================================================ FILE: 02-Fundamentals-Part-2/starter/index.html ================================================ JavaScript Fundamentals – Part 2

JavaScript Fundamentals – Part 2

================================================ FILE: 02-Fundamentals-Part-2/starter/script.js ================================================ ================================================ FILE: 03-Developer-Skills/final/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 03-Developer-Skills/final/index.html ================================================ Developer Skills & Editor Setup

Developer Skills & Editor Setup

================================================ FILE: 03-Developer-Skills/final/script.js ================================================ // Remember, we're gonna use strict mode in all scripts now! 'use strict'; /* /////////////////////////////////////// // Using Google, StackOverflow and MDN // PROBLEM 1: // We work for a company building a smart home thermometer. Our most recent task is this: "Given an array of temperatures of one day, calculate the temperature amplitude. Keep in mind that sometimes there might be a sensor error." const temperatures = [3, -2, -6, -1, 'error', 9, 13, 17, 15, 14, 9, 5]; // 1) Understanding the problem // - What is temp amplitude? Answer: difference between highest and lowest temp // - How to compute max and min temperatures? // - What's a sensor error? And what do do? // 2) Breaking up into sub-problems // - How to ignore errors? // - Find max value in temp array // - Find min value in temp array // - Subtract min from max (amplitude) and return it const calcTempAmplitude = function (temps) { let max = temps[0]; let min = temps[0]; for (let i = 0; i < temps.length; i++) { const curTemp = temps[i]; if (typeof curTemp !== 'number') continue; if (curTemp > max) max = curTemp; if (curTemp < min) min = curTemp; } console.log(max, min); return max - min; }; const amplitude = calcTempAmplitude(temperatures); console.log(amplitude); // PROBLEM 2: // Function should now receive 2 arrays of temps // 1) Understanding the problem // - With 2 arrays, should we implement functionality twice? NO! Just merge two arrays // 2) Breaking up into sub-problems // - Merge 2 arrays const calcTempAmplitudeNew = function (t1, t2) { const temps = t1.concat(t2); console.log(temps); let max = temps[0]; let min = temps[0]; for (let i = 0; i < temps.length; i++) { const curTemp = temps[i]; if (typeof curTemp !== 'number') continue; if (curTemp > max) max = curTemp; if (curTemp < min) min = curTemp; } console.log(max, min); return max - min; }; const amplitudeNew = calcTempAmplitudeNew([3, 5, 1], [9, 0, 5]); console.log(amplitudeNew); /////////////////////////////////////// // Debugging with the Console and Breakpoints const measureKelvin = function () { const measurement = { type: 'temp', unit: 'celsius', // C) FIX // value: Number(prompt('Degrees celsius:')), value: 10, }; // B) FIND console.table(measurement); // console.log(measurement.value); // console.warn(measurement.value); // console.error(measurement.value); const kelvin = measurement.value + 273; return kelvin; }; // A) IDENTIFY console.log(measureKelvin()); // Using a debugger const calcTempAmplitudeBug = function (t1, t2) { const temps = t1.concat(t2); console.log(temps); let max = 0; let min = 0; for (let i = 0; i < temps.length; i++) { const curTemp = temps[i]; if (typeof curTemp !== 'number') continue; if (curTemp > max) max = curTemp; if (curTemp < min) min = curTemp; } console.log(max, min); return max - min; }; const amplitudeBug = calcTempAmplitudeBug([3, 5, 1], [9, 4, 5]); // A) IDENTIFY console.log(amplitudeBug); */ /////////////////////////////////////// // Coding Challenge #1 /* Given an array of forecasted maximum temperatures, the thermometer displays a string with these temperatures. Example: [17, 21, 23] will print "... 17ºC in 1 days ... 21ºC in 2 days ... 23ºC in 3 days ..." Create a function 'printForecast' which takes in an array 'arr' and logs a string like the above to the console. Use the problem-solving framework: Understand the problem and break it up into sub-problems! TEST DATA 1: [17, 21, 23] TEST DATA 2: [12, 5, -5, 0, 4] */ /* // 1) Understanding the problem // - Array transformed to string, separated by ... // - What is the X days? Answer: index + 1 // 2) Breaking up into sub-problems // - Transform array into string // - Transform each element to string with ºC // - Strings needs to contain day (index + 1) // - Add ... between elements and start and end of string // - Log string to console const data1 = [17, 21, 23]; const data2 = [12, 5, -5, 0, 4]; console.log(`... ${data1[0]}ºC ... ${data1[1]}ºC ... ${data1[2]}ºC ...`); const printForecast = function (arr) { let str = ''; for (let i = 0; i < arr.length; i++) { str += `${arr[i]}ºC in ${i + 1} days ... `; } console.log('...' + str); }; printForecast(data1); */ /////////////////////////////////////// // Coding Challenge #2 With AI /* Let's say you're building a time tracking application for freelancers. At some point in building this app, you need a function that receives daily work hours for a certain week, and returns: 1. Total hours worked 2. Average daily hours 3. The day with the most hours worked 4. Number of days worked 5. Whether the week was full-time (worked 35 hours or more) TEST DATA: [7.5, 8, 6.5, 0, 8.5, 4, 0] */ /* // Written by ChatGPT function analyzeWorkWeek(dailyHours) { const daysOfWeek = [ 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday', ]; // Validate that the input array has exactly 7 elements if (!Array.isArray(dailyHours) || dailyHours.length !== 7) { throw new Error('Input must be an array of exactly 7 daily work hours.'); } // Calculate total hours worked const totalHours = dailyHours.reduce((sum, hours) => sum + hours, 0); // Calculate average daily hours, rounded to one decimal place const averageHours = Math.round((totalHours / dailyHours.length) * 10) / 10; // Find the day with the most hours worked const maxHours = Math.max(...dailyHours); const maxDayIndex = dailyHours.indexOf(maxHours); const maxDay = daysOfWeek[maxDayIndex]; // Convert index to day name // Count the number of days worked const daysWorked = dailyHours.filter(hours => hours > 0).length; // Check if the week was full-time (35 hours or more) const isFullTime = totalHours >= 35; // Return the result object return { totalHours, averageHours, maxDay, // The name of the day with the most hours daysWorked, isFullTime, }; } const weeklyHours = [7.5, 8, 6.5, 0, 8.5, 5, 0]; const analysis = analyzeWorkWeek(weeklyHours); console.log(analysis); const weeklyHours2 = [7.5, 8, 6.5, 0, 8.5]; const analysis2 = analyzeWorkWeek(weeklyHours2); console.log(analysis2); */ ================================================ FILE: 03-Developer-Skills/starter/index.html ================================================ Developer Skills & Editor Setup

Developer Skills & Editor Setup

================================================ FILE: 03-Developer-Skills/starter/script.js ================================================ // Remember, we're gonna use strict mode in all scripts now! 'use strict'; ================================================ FILE: 04-HTML-CSS/final/index.html ================================================ Learning HTML & CSS

JavaScript is fun, but so is HTML & CSS!

You can learn JavaScript without HTML and CSS, but for DOM manipulation it's useful to have some basic ideas of HTML & CSS. You can learn more about it on Udemy.

Another heading

Just another paragraph

Your name here

Please fill in this form :)

================================================ FILE: 04-HTML-CSS/final/style.css ================================================ * { margin: 0; padding: 0; box-sizing: border-box; } body { background-color: rgb(255, 247, 201); font-family: Arial; font-size: 20px; padding: 50px; } h1 { font-size: 35px; margin-bottom: 25px; } h2 { margin-bottom: 20px; text-align: center; } p { margin-bottom: 20px; } .first { color: red; } #your-name { background-color: rgb(255, 220, 105); border: 5px solid #444; width: 400px; padding: 25px; margin-top: 30px; } input, button { padding: 10px; font-size: 16px; } a { background-color: yellowgreen; } #course-image { width: 300px; } #your-name h2 { color: olivedrab; } ================================================ FILE: 05-Guess-My-Number/final/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 05-Guess-My-Number/final/index.html ================================================ Guess My Number!

Guess My Number!

(Between 1 and 20)

?

Start guessing...

💯 Score: 20

🥇 Highscore: 0

================================================ FILE: 05-Guess-My-Number/final/script.js ================================================ 'use strict'; /* console.log(document.querySelector('.message').textContent); document.querySelector('.message').textContent = '🎉 Correct Number!'; document.querySelector('.number').textContent = 13; document.querySelector('.score').textContent = 10; document.querySelector('.guess').value = 23; console.log(document.querySelector('.guess').value); */ let secretNumber = Math.trunc(Math.random() * 20) + 1; let score = 20; let highscore = 0; const displayMessage = function (message) { document.querySelector('.message').textContent = message; }; document.querySelector('.check').addEventListener('click', function () { const guess = Number(document.querySelector('.guess').value); console.log(guess, typeof guess); // When there is no input if (!guess) { // document.querySelector('.message').textContent = '⛔️ No number!'; displayMessage('⛔️ No number!'); // When player wins } else if (guess === secretNumber) { // document.querySelector('.message').textContent = '🎉 Correct Number!'; displayMessage('🎉 Correct Number!'); document.querySelector('.number').textContent = secretNumber; document.querySelector('body').style.backgroundColor = '#60b347'; document.querySelector('.number').style.width = '30rem'; if (score > highscore) { highscore = score; document.querySelector('.highscore').textContent = highscore; } // When guess is wrong } else if (guess !== secretNumber) { if (score > 1) { // document.querySelector('.message').textContent = // guess > secretNumber ? '📈 Too high!' : '📉 Too low!'; displayMessage(guess > secretNumber ? '📈 Too high!' : '📉 Too low!'); score--; document.querySelector('.score').textContent = score; } else { // document.querySelector('.message').textContent = '💥 You lost the game!'; displayMessage('💥 You lost the game!'); document.querySelector('.score').textContent = 0; } } // // When guess is too high // } else if (guess > secretNumber) { // if (score > 1) { // document.querySelector('.message').textContent = '📈 Too high!'; // score--; // document.querySelector('.score').textContent = score; // } else { // document.querySelector('.message').textContent = '💥 You lost the game!'; // document.querySelector('.score').textContent = 0; // } // // When guess is too low // } else if (guess < secretNumber) { // if (score > 1) { // document.querySelector('.message').textContent = '📉 Too low!'; // score--; // document.querySelector('.score').textContent = score; // } else { // document.querySelector('.message').textContent = '💥 You lost the game!'; // document.querySelector('.score').textContent = 0; // } // } }); document.querySelector('.again').addEventListener('click', function () { score = 20; secretNumber = Math.trunc(Math.random() * 20) + 1; // document.querySelector('.message').textContent = 'Start guessing...'; displayMessage('Start guessing...'); document.querySelector('.score').textContent = score; document.querySelector('.number').textContent = '?'; document.querySelector('.guess').value = ''; document.querySelector('body').style.backgroundColor = '#222'; document.querySelector('.number').style.width = '15rem'; }); /////////////////////////////////////// // Coding Challenge #1 /* Implement a game rest functionality, so that the player can make a new guess! Here is how: 1. Select the element with the 'again' class and attach a click event handler 2. In the handler function, restore initial values of the score and secretNumber variables 3. Restore the initial conditions of the message, number, score and guess input field 4. Also restore the original background color (#222) and number width (15rem) GOOD LUCK 😀 */ ================================================ FILE: 05-Guess-My-Number/final/style.css ================================================ @import url('https://fonts.googleapis.com/css?family=Press+Start+2P&display=swap'); * { margin: 0; padding: 0; box-sizing: inherit; } html { font-size: 62.5%; box-sizing: border-box; } body { font-family: 'Press Start 2P', sans-serif; color: #eee; background-color: #222; /* background-color: #60b347; */ } /* LAYOUT */ header { position: relative; height: 35vh; border-bottom: 7px solid #eee; } main { height: 65vh; color: #eee; display: flex; align-items: center; justify-content: space-around; } .left { width: 52rem; display: flex; flex-direction: column; align-items: center; } .right { width: 52rem; font-size: 2rem; } /* ELEMENTS STYLE */ h1 { font-size: 4rem; text-align: center; position: absolute; width: 100%; top: 52%; left: 50%; transform: translate(-50%, -50%); } .number { background: #eee; color: #333; font-size: 6rem; width: 15rem; padding: 3rem 0rem; text-align: center; position: absolute; bottom: 0; left: 50%; transform: translate(-50%, 50%); } .between { font-size: 1.4rem; position: absolute; top: 2rem; right: 2rem; } .again { position: absolute; top: 2rem; left: 2rem; } .guess { background: none; border: 4px solid #eee; font-family: inherit; color: inherit; font-size: 5rem; padding: 2.5rem; width: 25rem; text-align: center; display: block; margin-bottom: 3rem; } .btn { border: none; background-color: #eee; color: #222; font-size: 2rem; font-family: inherit; padding: 2rem 3rem; cursor: pointer; } .btn:hover { background-color: #ccc; } .message { margin-bottom: 8rem; height: 3rem; } .label-score { margin-bottom: 2rem; } ================================================ FILE: 05-Guess-My-Number/starter/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 05-Guess-My-Number/starter/index.html ================================================ Guess My Number!

Guess My Number!

(Between 1 and 20)

?

Start guessing...

💯 Score: 20

🥇 Highscore: 0

================================================ FILE: 05-Guess-My-Number/starter/script.js ================================================ 'use strict'; ================================================ FILE: 05-Guess-My-Number/starter/style.css ================================================ @import url('https://fonts.googleapis.com/css?family=Press+Start+2P&display=swap'); * { margin: 0; padding: 0; box-sizing: inherit; } html { font-size: 62.5%; box-sizing: border-box; } body { font-family: 'Press Start 2P', sans-serif; color: #eee; background-color: #222; /* background-color: #60b347; */ } /* LAYOUT */ header { position: relative; height: 35vh; border-bottom: 7px solid #eee; } main { height: 65vh; color: #eee; display: flex; align-items: center; justify-content: space-around; } .left { width: 52rem; display: flex; flex-direction: column; align-items: center; } .right { width: 52rem; font-size: 2rem; } /* ELEMENTS STYLE */ h1 { font-size: 4rem; text-align: center; position: absolute; width: 100%; top: 52%; left: 50%; transform: translate(-50%, -50%); } .number { background: #eee; color: #333; font-size: 6rem; width: 15rem; padding: 3rem 0rem; text-align: center; position: absolute; bottom: 0; left: 50%; transform: translate(-50%, 50%); } .between { font-size: 1.4rem; position: absolute; top: 2rem; right: 2rem; } .again { position: absolute; top: 2rem; left: 2rem; } .guess { background: none; border: 4px solid #eee; font-family: inherit; color: inherit; font-size: 5rem; padding: 2.5rem; width: 25rem; text-align: center; display: block; margin-bottom: 3rem; } .btn { border: none; background-color: #eee; color: #222; font-size: 2rem; font-family: inherit; padding: 2rem 3rem; cursor: pointer; } .btn:hover { background-color: #ccc; } .message { margin-bottom: 8rem; height: 3rem; } .label-score { margin-bottom: 2rem; } ================================================ FILE: 06-Modal/final/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 06-Modal/final/index.html ================================================ Modal window ================================================ FILE: 06-Modal/final/script.js ================================================ 'use strict'; const modal = document.querySelector('.modal'); const overlay = document.querySelector('.overlay'); const btnCloseModal = document.querySelector('.close-modal'); const btnsOpenModal = document.querySelectorAll('.show-modal'); const openModal = function () { modal.classList.remove('hidden'); overlay.classList.remove('hidden'); }; const closeModal = function () { modal.classList.add('hidden'); overlay.classList.add('hidden'); }; for (let i = 0; i < btnsOpenModal.length; i++) btnsOpenModal[i].addEventListener('click', openModal); btnCloseModal.addEventListener('click', closeModal); overlay.addEventListener('click', closeModal); document.addEventListener('keydown', function (e) { // console.log(e.key); if (e.key === 'Escape' && !modal.classList.contains('hidden')) { closeModal(); } }); ================================================ FILE: 06-Modal/final/style.css ================================================ * { margin: 0; padding: 0; box-sizing: inherit; } html { font-size: 62.5%; box-sizing: border-box; } body { font-family: sans-serif; color: #333; line-height: 1.5; height: 100vh; position: relative; display: flex; align-items: flex-start; justify-content: center; background: linear-gradient(to top left, #28b487, #7dd56f); } .show-modal { font-size: 2rem; font-weight: 600; padding: 1.75rem 3.5rem; margin: 5rem 2rem; border: none; background-color: #fff; color: #444; border-radius: 10rem; cursor: pointer; } .close-modal { position: absolute; top: 1.2rem; right: 2rem; font-size: 5rem; color: #333; cursor: pointer; border: none; background: none; } h1 { font-size: 2.5rem; margin-bottom: 2rem; } p { font-size: 1.8rem; } /* -------------------------- */ /* CLASSES TO MAKE MODAL WORK */ .hidden { display: none; } .modal { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 70%; background-color: white; padding: 6rem; border-radius: 5px; box-shadow: 0 3rem 5rem rgba(0, 0, 0, 0.3); z-index: 10; } .overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.6); backdrop-filter: blur(3px); z-index: 5; } ================================================ FILE: 06-Modal/starter/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 06-Modal/starter/index.html ================================================ Modal window ================================================ FILE: 06-Modal/starter/script.js ================================================ 'use strict'; ================================================ FILE: 06-Modal/starter/style.css ================================================ * { margin: 0; padding: 0; box-sizing: inherit; } html { font-size: 62.5%; box-sizing: border-box; } body { font-family: sans-serif; color: #333; line-height: 1.5; height: 100vh; position: relative; display: flex; align-items: flex-start; justify-content: center; background: linear-gradient(to top left, #28b487, #7dd56f); } .show-modal { font-size: 2rem; font-weight: 600; padding: 1.75rem 3.5rem; margin: 5rem 2rem; border: none; background-color: #fff; color: #444; border-radius: 10rem; cursor: pointer; } .close-modal { position: absolute; top: 1.2rem; right: 2rem; font-size: 5rem; color: #333; cursor: pointer; border: none; background: none; } h1 { font-size: 2.5rem; margin-bottom: 2rem; } p { font-size: 1.8rem; } /* -------------------------- */ /* CLASSES TO MAKE MODAL WORK */ .hidden { display: none; } .modal { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 70%; background-color: white; padding: 6rem; border-radius: 5px; box-shadow: 0 3rem 5rem rgba(0, 0, 0, 0.3); z-index: 10; } .overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.6); backdrop-filter: blur(3px); z-index: 5; } ================================================ FILE: 07-Pig-Game/final/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 07-Pig-Game/final/index.html ================================================ Pig Game

Player 1

43

Current

0

Player 2

24

Current

0

Playing dice
================================================ FILE: 07-Pig-Game/final/script.js ================================================ 'use strict'; // Selecting elements const player0El = document.querySelector('.player--0'); const player1El = document.querySelector('.player--1'); const score0El = document.querySelector('#score--0'); const score1El = document.getElementById('score--1'); const current0El = document.getElementById('current--0'); const current1El = document.getElementById('current--1'); const diceEl = document.querySelector('.dice'); const btnNew = document.querySelector('.btn--new'); const btnRoll = document.querySelector('.btn--roll'); const btnHold = document.querySelector('.btn--hold'); let scores, currentScore, activePlayer, playing; // Starting conditions const init = function () { scores = [0, 0]; currentScore = 0; activePlayer = 0; playing = true; score0El.textContent = 0; score1El.textContent = 0; current0El.textContent = 0; current1El.textContent = 0; diceEl.classList.add('hidden'); player0El.classList.remove('player--winner'); player1El.classList.remove('player--winner'); player0El.classList.add('player--active'); player1El.classList.remove('player--active'); }; init(); const switchPlayer = function () { document.getElementById(`current--${activePlayer}`).textContent = 0; currentScore = 0; activePlayer = activePlayer === 0 ? 1 : 0; player0El.classList.toggle('player--active'); player1El.classList.toggle('player--active'); }; // Rolling dice functionality btnRoll.addEventListener('click', function () { if (playing) { // 1. Generating a random dice roll const dice = Math.trunc(Math.random() * 6) + 1; // 2. Display dice diceEl.classList.remove('hidden'); diceEl.src = `dice-${dice}.png`; // 3. Check for rolled 1 if (dice !== 1) { // Add dice to current score currentScore += dice; document.getElementById( `current--${activePlayer}` ).textContent = currentScore; } else { // Switch to next player switchPlayer(); } } }); btnHold.addEventListener('click', function () { if (playing) { // 1. Add current score to active player's score scores[activePlayer] += currentScore; // scores[1] = scores[1] + currentScore document.getElementById(`score--${activePlayer}`).textContent = scores[activePlayer]; // 2. Check if player's score is >= 100 if (scores[activePlayer] >= 100) { // Finish the game playing = false; diceEl.classList.add('hidden'); document .querySelector(`.player--${activePlayer}`) .classList.add('player--winner'); document .querySelector(`.player--${activePlayer}`) .classList.remove('player--active'); } else { // Switch to the next player switchPlayer(); } } }); btnNew.addEventListener('click', init); ================================================ FILE: 07-Pig-Game/final/style.css ================================================ @import url('https://fonts.googleapis.com/css2?family=Nunito&display=swap'); * { margin: 0; padding: 0; box-sizing: inherit; } html { font-size: 62.5%; box-sizing: border-box; } body { font-family: 'Nunito', sans-serif; font-weight: 400; height: 100vh; color: #333; background-image: linear-gradient(to top left, #753682 0%, #bf2e34 100%); display: flex; align-items: center; justify-content: center; } /* LAYOUT */ main { position: relative; width: 100rem; height: 60rem; background-color: rgba(255, 255, 255, 0.35); backdrop-filter: blur(200px); filter: blur(); box-shadow: 0 3rem 5rem rgba(0, 0, 0, 0.25); border-radius: 9px; overflow: hidden; display: flex; } .player { flex: 50%; padding: 9rem; display: flex; flex-direction: column; align-items: center; transition: all 0.75s; } /* ELEMENTS */ .name { position: relative; font-size: 4rem; text-transform: uppercase; letter-spacing: 1px; word-spacing: 2px; font-weight: 300; margin-bottom: 1rem; } .score { font-size: 8rem; font-weight: 300; color: #c7365f; margin-bottom: auto; } .player--active { background-color: rgba(255, 255, 255, 0.4); } .player--active .name { font-weight: 700; } .player--active .score { font-weight: 400; } .player--active .current { opacity: 1; } .current { background-color: #c7365f; opacity: 0.8; border-radius: 9px; color: #fff; width: 65%; padding: 2rem; text-align: center; transition: all 0.75s; } .current-label { text-transform: uppercase; margin-bottom: 1rem; font-size: 1.7rem; color: #ddd; } .current-score { font-size: 3.5rem; } /* ABSOLUTE POSITIONED ELEMENTS */ .btn { position: absolute; left: 50%; transform: translateX(-50%); color: #444; background: none; border: none; font-family: inherit; font-size: 1.8rem; text-transform: uppercase; cursor: pointer; font-weight: 400; transition: all 0.2s; background-color: white; background-color: rgba(255, 255, 255, 0.6); backdrop-filter: blur(10px); padding: 0.7rem 2.5rem; border-radius: 50rem; box-shadow: 0 1.75rem 3.5rem rgba(0, 0, 0, 0.1); } .btn::first-letter { font-size: 2.4rem; display: inline-block; margin-right: 0.7rem; } .btn--new { top: 4rem; } .btn--roll { top: 39.3rem; } .btn--hold { top: 46.1rem; } .btn:active { transform: translate(-50%, 3px); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15); } .btn:focus { outline: none; } .dice { position: absolute; left: 50%; top: 16.5rem; transform: translateX(-50%); height: 10rem; box-shadow: 0 2rem 5rem rgba(0, 0, 0, 0.2); } .player--winner { background-color: #2f2f2f; } .player--winner .name { font-weight: 700; color: #c7365f; } .hidden { display: none; } ================================================ FILE: 07-Pig-Game/starter/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 07-Pig-Game/starter/index.html ================================================ Pig Game

Player 1

43

Current

0

Player 2

24

Current

0

Playing dice
================================================ FILE: 07-Pig-Game/starter/script.js ================================================ 'use strict'; ================================================ FILE: 07-Pig-Game/starter/style.css ================================================ @import url('https://fonts.googleapis.com/css2?family=Nunito&display=swap'); * { margin: 0; padding: 0; box-sizing: inherit; } html { font-size: 62.5%; box-sizing: border-box; } body { font-family: 'Nunito', sans-serif; font-weight: 400; height: 100vh; color: #333; background-image: linear-gradient(to top left, #753682 0%, #bf2e34 100%); display: flex; align-items: center; justify-content: center; } /* LAYOUT */ main { position: relative; width: 100rem; height: 60rem; background-color: rgba(255, 255, 255, 0.35); backdrop-filter: blur(200px); filter: blur(); box-shadow: 0 3rem 5rem rgba(0, 0, 0, 0.25); border-radius: 9px; overflow: hidden; display: flex; } .player { flex: 50%; padding: 9rem; display: flex; flex-direction: column; align-items: center; transition: all 0.75s; } /* ELEMENTS */ .name { position: relative; font-size: 4rem; text-transform: uppercase; letter-spacing: 1px; word-spacing: 2px; font-weight: 300; margin-bottom: 1rem; } .score { font-size: 8rem; font-weight: 300; color: #c7365f; margin-bottom: auto; } .player--active { background-color: rgba(255, 255, 255, 0.4); } .player--active .name { font-weight: 700; } .player--active .score { font-weight: 400; } .player--active .current { opacity: 1; } .current { background-color: #c7365f; opacity: 0.8; border-radius: 9px; color: #fff; width: 65%; padding: 2rem; text-align: center; transition: all 0.75s; } .current-label { text-transform: uppercase; margin-bottom: 1rem; font-size: 1.7rem; color: #ddd; } .current-score { font-size: 3.5rem; } /* ABSOLUTE POSITIONED ELEMENTS */ .btn { position: absolute; left: 50%; transform: translateX(-50%); color: #444; background: none; border: none; font-family: inherit; font-size: 1.8rem; text-transform: uppercase; cursor: pointer; font-weight: 400; transition: all 0.2s; background-color: white; background-color: rgba(255, 255, 255, 0.6); backdrop-filter: blur(10px); padding: 0.7rem 2.5rem; border-radius: 50rem; box-shadow: 0 1.75rem 3.5rem rgba(0, 0, 0, 0.1); } .btn::first-letter { font-size: 2.4rem; display: inline-block; margin-right: 0.7rem; } .btn--new { top: 4rem; } .btn--roll { top: 39.3rem; } .btn--hold { top: 46.1rem; } .btn:active { transform: translate(-50%, 3px); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15); } .btn:focus { outline: none; } .dice { position: absolute; left: 50%; top: 16.5rem; transform: translateX(-50%); height: 10rem; box-shadow: 0 2rem 5rem rgba(0, 0, 0, 0.2); } .player--winner { background-color: #2f2f2f; } .player--winner .name { font-weight: 700; color: #c7365f; } ================================================ FILE: 08-Behind-the-Scenes/final/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 08-Behind-the-Scenes/final/index.html ================================================ How JavaScript Works Behind the Scenes

How JavaScript Works Behind the Scenes

================================================ FILE: 08-Behind-the-Scenes/final/script.js ================================================ 'use strict'; /////////////////////////////////////// // Scoping in Practice /* function calcAge(birthYear) { const age = 2037 - birthYear; function printAge() { let output = `${firstName}, you are ${age}, born in ${birthYear}`; console.log(output); if (birthYear >= 1981 && birthYear <= 1996) { var millenial = true; // Creating NEW variable with same name as outer scope's variable const firstName = 'Steven'; // Reasssigning outer scope's variable output = 'NEW OUTPUT!'; const str = `Oh, and you're a millenial, ${firstName}`; console.log(str); function add(a, b) { return a + b; } } // console.log(str); console.log(millenial); // console.log(add(2, 3)); console.log(output); } printAge(); return age; } const firstName = 'Jonas'; calcAge(1991); // console.log(age); // printAge(); /////////////////////////////////////// // Hoisting and TDZ in Practice // Variables console.log(me); // console.log(job); // console.log(year); var me = 'Jonas'; let job = 'teacher'; const year = 1991; // Functions console.log(addDecl(2, 3)); // console.log(addExpr(2, 3)); console.log(addArrow); // console.log(addArrow(2, 3)); function addDecl(a, b) { return a + b; } const addExpr = function (a, b) { return a + b; }; var addArrow = (a, b) => a + b; // Example console.log(undefined); if (!numProducts) deleteShoppingCart(); var numProducts = 10; function deleteShoppingCart() { console.log('All products deleted!'); } var x = 1; let y = 2; const z = 3; console.log(x === window.x); console.log(y === window.y); console.log(z === window.z); /////////////////////////////////////// // The this Keyword in Practice console.log(this); const calcAge = function (birthYear) { console.log(2037 - birthYear); console.log(this); }; calcAge(1991); const calcAgeArrow = birthYear => { console.log(2037 - birthYear); console.log(this); }; calcAgeArrow(1980); const jonas = { year: 1991, calcAge: function () { console.log(this); console.log(2037 - this.year); }, }; jonas.calcAge(); const matilda = { year: 2017, }; matilda.calcAge = jonas.calcAge; matilda.calcAge(); const f = jonas.calcAge; f(); /////////////////////////////////////// // Regular Functions vs. Arrow Functions // var firstName = 'Matilda'; const jonas = { firstName: 'Jonas', year: 1991, calcAge: function () { // console.log(this); console.log(2037 - this.year); // Solution 1 // const self = this; // self or that // const isMillenial = function () { // console.log(self); // console.log(self.year >= 1981 && self.year <= 1996); // }; // Solution 2 const isMillenial = () => { console.log(this); console.log(this.year >= 1981 && this.year <= 1996); }; isMillenial(); }, greet: () => { console.log(this); console.log(`Hey ${this.firstName}`); }, }; jonas.greet(); jonas.calcAge(); // arguments keyword const addExpr = function (a, b) { console.log(arguments); return a + b; }; addExpr(2, 5); addExpr(2, 5, 8, 12); var addArrow = (a, b) => { console.log(arguments); return a + b; }; addArrow(2, 5, 8); /////////////////////////////////////// // Object References in Practice (Shallow vs. Deep Copies) const jessica1 = { firstName: 'Jessica', lastName: 'Williams', age: 27, }; function marryPerson(originalPerson, newLastName) { originalPerson.lastName = newLastName; return originalPerson; } const marriedJessica = marryPerson(jessica1, 'Davis'); // const marriedJessica = jessica1; // marriedJessica.lastName = 'Davis'; console.log('Before:', jessica1); console.log('After:', marriedJessica); const jessica = { firstName: 'Jessica', lastName: 'Williams', age: 27, familiy: ['Alice', 'Bob'], }; // Shallow copy const jessicaCopy = { ...jessica }; jessicaCopy.lastName = 'Davis'; // jessicaCopy.familiy.push('Mary'); // jessicaCopy.familiy.push('John'); // console.log('Before:', jessica); // console.log('After:', jessicaCopy); // Deep copy/clone const jessicaClone = structuredClone(jessica); jessicaClone.familiy.push('Mary'); jessicaClone.familiy.push('John'); console.log('Original:', jessica); console.log('Clone:', jessicaClone); */ ================================================ FILE: 08-Behind-the-Scenes/starter/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 08-Behind-the-Scenes/starter/index.html ================================================ How JavaScript Works Behind the Scenes

How JavaScript Works Behind the Scenes

================================================ FILE: 08-Behind-the-Scenes/starter/script.js ================================================ 'use strict'; ================================================ FILE: 09-Data-Structures-Operators/final/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 09-Data-Structures-Operators/final/index.html ================================================ Data Structures and Modern Operators

Data Structures and Modern Operators

================================================ FILE: 09-Data-Structures-Operators/final/script.js ================================================ 'use strict'; const weekdays = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']; const openingHours = { [weekdays[3]]: { open: 12, close: 22, }, [weekdays[4]]: { open: 11, close: 23, }, [weekdays[5]]: { open: 0, // Open 24 hours close: 24, }, }; const restaurant = { name: 'Classico Italiano', location: 'Via Angelo Tavanti 23, Firenze, Italy', categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'], starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'], mainMenu: ['Pizza', 'Pasta', 'Risotto'], // ES6 enhanced object literals openingHours, order(starterIndex, mainIndex) { return [this.starterMenu[starterIndex], this.mainMenu[mainIndex]]; }, orderDelivery({ starterIndex = 1, mainIndex = 0, time = '20:00', address }) { console.log( `Order received! ${this.starterMenu[starterIndex]} and ${this.mainMenu[mainIndex]} will be delivered to ${address} at ${time}` ); }, orderPasta(ing1, ing2, ing3) { console.log( `Here is your declicious pasta with ${ing1}, ${ing2} and ${ing3}` ); }, orderPizza(mainIngredient, ...otherIngredients) { console.log(mainIngredient); console.log(otherIngredients); }, }; /* /////////////////////////////////////// // String Methods Practice const flights = '_Delayed_Departure;fao93766109;txl2133758440;11:25+_Arrival;bru0943384722;fao93766109;11:45+_Delayed_Arrival;hel7439299980;fao93766109;12:05+_Departure;fao93766109;lis2323639855;12:30'; // 🔴 Delayed Departure from FAO to TXL (11h25) // Arrival from BRU to FAO (11h45) // 🔴 Delayed Arrival from HEL to FAO (12h05) // Departure from FAO to LIS (12h30) const getCode = str => str.slice(0, 3).toUpperCase(); for (const flight of flights.split('+')) { const [type, from, to, time] = flight.split(';'); const output = `${type.startsWith('_Delayed') ? '🔴' : ''}${type.replaceAll( '_', ' ' )} ${getCode(from)} ${getCode(to)} (${time.replace(':', 'h')})`.padStart(36); console.log(output); } /////////////////////////////////////// // Coding Challenge #4 Write a program that receives a list of variable names written in underscore_case and convert them to camelCase. The input will come from a textarea inserted into the DOM (see code below), and conversion will happen when the button is pressed. THIS TEST DATA (pasted to textarea) underscore_case first_name Some_Variable calculate_AGE delayed_departure SHOULD PRODUCE THIS OUTPUT (5 separate console.log outputs) underscoreCase ✅ firstName ✅✅ someVariable ✅✅✅ calculateAge ✅✅✅✅ delayedDeparture ✅✅✅✅✅ HINT 1: Remember which character defines a new line in the textarea 😉 HINT 2: The solution only needs to work for a variable made out of 2 words, like a_b HINT 3: Start without worrying about the ✅. Tackle that only after you have the variable name conversion working 😉 HINT 4: This challenge is difficult on purpose, so start watching the solution in case you're stuck. Then pause and continue! Afterwards, test with your own test data! GOOD LUCK 😀 */ /* document.body.append(document.createElement('textarea')); document.body.append(document.createElement('button')); document.querySelector('button').addEventListener('click', function () { const text = document.querySelector('textarea').value; const rows = text.split('\n'); for (const [i, row] of rows.entries()) { const [first, second] = row.toLowerCase().trim().split('_'); const output = `${first}${second.replace( second[0], second[0].toUpperCase() )}`; console.log(`${output.padEnd(20)}${'✅'.repeat(i + 1)}`); } }); */ /* /////////////////////////////////////// // Working With Strings - Part 3 // Split and join console.log('a+very+nice+string'.split('+')); console.log('Jonas Schmedtmann'.split(' ')); const [firstName, lastName] = 'Jonas Schmedtmann'.split(' '); const newName = ['Mr.', firstName, lastName.toUpperCase()].join(' '); console.log(newName); const capitalizeName = function (name) { const names = name.split(' '); const namesUpper = []; for (const n of names) { // namesUpper.push(n[0].toUpperCase() + n.slice(1)); namesUpper.push(n.replace(n[0], n[0].toUpperCase())); } console.log(namesUpper.join(' ')); }; capitalizeName('jessica ann smith davis'); capitalizeName('jonas schmedtmann'); // Padding const message = 'Go to gate 23!'; console.log(message.padStart(20, '+').padEnd(30, '+')); console.log('Jonas'.padStart(20, '+').padEnd(30, '+')); const maskCreditCard = function (number) { const str = number + ''; const last = str.slice(-4); return last.padStart(str.length, '*'); }; console.log(maskCreditCard(64637836)); console.log(maskCreditCard(43378463864647384)); console.log(maskCreditCard('334859493847755774747')); // Repeat const message2 = 'Bad waether... All Departues Delayed... '; console.log(message2.repeat(5)); const planesInLine = function (n) { console.log(`There are ${n} planes in line ${'🛩'.repeat(n)}`); }; planesInLine(5); planesInLine(3); planesInLine(12); /////////////////////////////////////// // Working With Strings - Part 2 const airline = 'TAP Air Portugal'; console.log(airline.toLowerCase()); console.log(airline.toUpperCase()); // Fix capitalization in name const passenger = 'jOnAS'; // Jonas const passengerLower = passenger.toLowerCase(); const passengerCorrect = passengerLower[0].toUpperCase() + passengerLower.slice(1); console.log(passengerCorrect); // Comparing emails const email = 'hello@jonas.io'; const loginEmail = ' Hello@Jonas.Io \n'; // const lowerEmail = loginEmail.toLowerCase(); // const trimmedEmail = lowerEmail.trim(); const normalizedEmail = loginEmail.toLowerCase().trim(); console.log(normalizedEmail); console.log(email === normalizedEmail); // replacing const priceGB = '288,97£'; const priceUS = priceGB.replace('£', '$').replace(',', '.'); console.log(priceUS); const announcement = 'All passengers come to boarding door 23. Boarding door 23!'; console.log(announcement.replace('door', 'gate')); console.log(announcement.replaceAll('door', 'gate')); // Alternative solution to replaceAll with regular expression console.log(announcement.replace(/door/g, 'gate')); // Booleans const plane = 'Airbus A320neo'; console.log(plane.includes('A320')); console.log(plane.includes('Boeing')); console.log(plane.startsWith('Airb')); if (plane.startsWith('Airbus') && plane.endsWith('neo')) { console.log('Part of the NEW ARirbus family'); } // Practice exercise const checkBaggage = function (items) { const baggage = items.toLowerCase(); if (baggage.includes('knife') || baggage.includes('gun')) { console.log('You are NOT allowed on board'); } else { console.log('Welcome aboard!'); } }; checkBaggage('I have a laptop, some Food and a pocket Knife'); checkBaggage('Socks and camera'); checkBaggage('Got some snacks and a gun for protection'); /////////////////////////////////////// // Working With Strings - Part 1 const airline = 'TAP Air Portugal'; const plane = 'A320'; console.log(plane[0]); console.log(plane[1]); console.log(plane[2]); console.log('B737'[0]); console.log(airline.length); console.log('B737'.length); console.log(airline.indexOf('r')); console.log(airline.lastIndexOf('r')); console.log(airline.indexOf('portugal')); console.log(airline.slice(4)); console.log(airline.slice(4, 7)); console.log(airline.slice(0, airline.indexOf(' '))); console.log(airline.slice(airline.lastIndexOf(' ') + 1)); console.log(airline.slice(-2)); console.log(airline.slice(1, -1)); const checkMiddleSeat = function (seat) { // B and E are middle seats const s = seat.slice(-1); if (s === 'B' || s === 'E') console.log('You got the middle seat 😬'); else console.log('You got lucky 😎'); }; checkMiddleSeat('11B'); checkMiddleSeat('23C'); checkMiddleSeat('3E'); console.log(new String('jonas')); console.log(typeof new String('jonas')); console.log(typeof new String('jonas').slice(1)); */ /////////////////////////////////////// // Coding Challenge #3 /* Let's continue with our football betting app! This time, we have a map with a log of the events that happened during the game. The values are the events themselves, and the keys are the minutes in which each event happened (a football game has 90 minutes plus some extra time). 1. Create an array 'events' of the different game events that happened (no duplicates) 2. After the game has finished, is was found that the yellow card from minute 64 was unfair. So remove this event from the game events log. 3. Print the following string to the console: "An event happened, on average, every 9 minutes" (keep in mind that a game has 90 minutes) 4. Loop over the events and log them to the console, marking whether it's in the first half or second half (after 45 min) of the game, like this: [FIRST HALF] 17: ⚽️ GOAL GOOD LUCK 😀 */ const gameEvents = new Map([ [17, '⚽️ GOAL'], [36, '🔁 Substitution'], [47, '⚽️ GOAL'], [61, '🔁 Substitution'], [64, '🔶 Yellow card'], [69, '🔴 Red card'], [70, '🔁 Substitution'], [72, '🔁 Substitution'], [76, '⚽️ GOAL'], [80, '⚽️ GOAL'], [92, '🔶 Yellow card'], ]); /* // 1. const events = [...new Set(gameEvents.values())]; console.log(events); // 2. gameEvents.delete(64); // 3. console.log( `An event happened, on average, every ${90 / gameEvents.size} minutes` ); const time = [...gameEvents.keys()].pop(); console.log(time); console.log( `An event happened, on average, every ${time / gameEvents.size} minutes` ); // 4. for (const [min, event] of gameEvents) { const half = min <= 45 ? 'FIRST' : 'SECOND'; console.log(`[${half} HALF] ${min}: ${event}`); } */ /* /////////////////////////////////////// // Maps: Iteration const question = new Map([ ['question', 'What is the best programming language in the world?'], [1, 'C'], [2, 'Java'], [3, 'JavaScript'], ['correct', 3], [true, 'Correct 🎉'], [false, 'Try again!'], ]); console.log(question); // Convert object to map console.log(Object.entries(openingHours)); const hoursMap = new Map(Object.entries(openingHours)); console.log(hoursMap); // Quiz app console.log(question.get('question')); for (const [key, value] of question) { if (typeof key === 'number') console.log(`Answer ${key}: ${value}`); } // const answer = Number(prompt('Your answer')); const answer = 3; console.log(answer); console.log(question.get(question.get('correct') === answer)); // Convert map to array console.log([...question]); // console.log(question.entries()); console.log([...question.keys()]); console.log([...question.values()]); /////////////////////////////////////// // Maps: Fundamentals const rest = new Map(); rest.set('name', 'Classico Italiano'); rest.set(1, 'Firenze, Italy'); console.log(rest.set(2, 'Lisbon, Portugal')); rest .set('categories', ['Italian', 'Pizzeria', 'Vegetarian', 'Organic']) .set('open', 11) .set('close', 23) .set(true, 'We are open :D') .set(false, 'We are closed :('); console.log(rest.get('name')); console.log(rest.get(true)); console.log(rest.get(1)); const time = 8; console.log(rest.get(time > rest.get('open') && time < rest.get('close'))); console.log(rest.has('categories')); rest.delete(2); // rest.clear(); const arr = [1, 2]; rest.set(arr, 'Test'); rest.set(document.querySelector('h1'), 'Heading'); console.log(rest); console.log(rest.size); console.log(rest.get(arr)); /////////////////////////////////////// // New Operations to Make Sets Useful! const italianFoods = new Set([ 'pasta', 'gnocchi', 'tomatoes', 'olive oil', 'garlic', 'basil', ]); const mexicanFoods = new Set([ 'tortillas', 'beans', 'rice', 'tomatoes', 'avocado', 'garlic', ]); const commonFoods = italianFoods.intersection(mexicanFoods); console.log('Intersection:', commonFoods); console.log([...commonFoods]); const italianMexicanFusion = italianFoods.union(mexicanFoods); console.log('Union:', italianMexicanFusion); console.log([...new Set([...italianFoods, ...mexicanFoods])]); const uniqueItalianFoods = italianFoods.difference(mexicanFoods); console.log('Difference italian', uniqueItalianFoods); const uniqueMexicanFoods = mexicanFoods.difference(italianFoods); console.log('Difference mexican', uniqueMexicanFoods); const uniqueItalianAndMexicanFoods = italianFoods.symmetricDifference(mexicanFoods); console.log(uniqueItalianAndMexicanFoods); console.log(italianFoods.isDisjointFrom(mexicanFoods)); /////////////////////////////////////// // Sets const ordersSet = new Set([ 'Pasta', 'Pizza', 'Pizza', 'Risotto', 'Pasta', 'Pizza', ]); console.log(ordersSet); console.log(new Set('Jonas')); console.log(ordersSet.size); console.log(ordersSet.has('Pizza')); console.log(ordersSet.has('Bread')); ordersSet.add('Garlic Bread'); ordersSet.add('Garlic Bread'); ordersSet.delete('Risotto'); // ordersSet.clear(); console.log(ordersSet); for (const order of ordersSet) console.log(order); // Example const staff = ['Waiter', 'Chef', 'Waiter', 'Manager', 'Chef', 'Waiter']; const staffUnique = [...new Set(staff)]; console.log(staffUnique); console.log( new Set(['Waiter', 'Chef', 'Waiter', 'Manager', 'Chef', 'Waiter']).size ); console.log(new Set('jonasschmedtmann').size); */ /////////////////////////////////////// // Coding Challenge #2 /* Let's continue with our football betting app! 1. Loop over the game.scored array and print each player name to the console, along with the goal number (Example: "Goal 1: Lewandowski") 2. Use a loop to calculate the average odd and log it to the console (We already studied how to calculate averages, you can go check if you don't remember) 3. Print the 3 odds to the console, but in a nice formatted way, exaclty like this: Odd of victory Bayern Munich: 1.33 Odd of draw: 3.25 Odd of victory Borrussia Dortmund: 6.5 Get the team names directly from the game object, don't hardcode them (except for "draw"). HINT: Note how the odds and the game objects have the same property names 😉 BONUS: Create an object called 'scorers' which contains the names of the players who scored as properties, and the number of goals as the value. In this game, it will look like this: { Gnarby: 1, Hummels: 1, Lewandowski: 2 } GOOD LUCK 😀 */ const game = { team1: 'Bayern Munich', team2: 'Borrussia Dortmund', players: [ [ 'Neuer', 'Pavard', 'Martinez', 'Alaba', 'Davies', 'Kimmich', 'Goretzka', 'Coman', 'Muller', 'Gnarby', 'Lewandowski', ], [ 'Burki', 'Schulz', 'Hummels', 'Akanji', 'Hakimi', 'Weigl', 'Witsel', 'Hazard', 'Brandt', 'Sancho', 'Gotze', ], ], score: '4:0', scored: ['Lewandowski', 'Gnarby', 'Lewandowski', 'Hummels'], date: 'Nov 9th, 2037', odds: { team1: 1.33, x: 3.25, team2: 6.5, }, }; /* // 1. for (const [i, player] of game.scored.entries()) console.log(`Goal ${i + 1}: ${player}`); // 2. const odds = Object.values(game.odds); let average = 0; for (const odd of odds) average += odd; average /= odds.length; console.log(average); // 3. for (const [team, odd] of Object.entries(game.odds)) { const teamStr = team === 'x' ? 'draw' : `victory ${game[team]}`; console.log(`Odd of ${teamStr} ${odd}`); } // Odd of victory Bayern Munich: 1.33 // Odd of draw: 3.25 // Odd of victory Borrussia Dortmund: 6.5 // BONUS // So the solution is to loop over the array, and add the array elements as object properties, and then increase the count as we encounter a new occurence of a certain element const scorers = {}; for (const player of game.scored) { scorers[player] ? scorers[player]++ : (scorers[player] = 1); } */ /* /////////////////////////////////////// // Looping Objects: Object Keys, Values, and Entries // Property NAMES const properties = Object.keys(openingHours); console.log(properties); let openStr = `We are open on ${properties.length} days: `; for (const day of properties) { openStr += `${day}, `; } console.log(openStr); // Property VALUES const values = Object.values(openingHours); console.log(values); // Entire object const entries = Object.entries(openingHours); // console.log(entries); // [key, value] for (const [day, { open, close }] of entries) { console.log(`On ${day} we open at ${open} and close at ${close}`); } /////////////////////////////////////// // Optional Chaining if (restaurant.openingHours && restaurant.openingHours.mon) console.log(restaurant.openingHours.mon.open); // console.log(restaurant.openingHours.mon.open); // WITH optional chaining console.log(restaurant.openingHours.mon?.open); console.log(restaurant.openingHours?.mon?.open); // Example const days = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']; for (const day of days) { const open = restaurant.openingHours[day]?.open ?? 'closed'; console.log(`On ${day}, we open at ${open}`); } // Methods console.log(restaurant.order?.(0, 1) ?? 'Method does not exist'); console.log(restaurant.orderRisotto?.(0, 1) ?? 'Method does not exist'); // Arrays const users = [{ name: 'Jonas', email: 'hello@jonas.io' }]; // const users = []; console.log(users[0]?.name ?? 'User array empty'); if (users.length > 0) console.log(users[0].name); else console.log('user array empty'); /////////////////////////////////////// // The for-of Loop const menu = [...restaurant.starterMenu, ...restaurant.mainMenu]; for (const item of menu) console.log(item); for (const [i, el] of menu.entries()) { console.log(`${i + 1}: ${el}`); } // console.log([...menu.entries()]); */ /////////////////////////////////////// // Coding Challenge #1 /* We're building a football betting app (soccer for my American friends 😅)! Suppose we get data from a web service about a certain game (below). In this challenge we're gonna work with the data. So here are your tasks: 1. Create one player array for each team (variables 'players1' and 'players2') 2. The first player in any player array is the goalkeeper and the others are field players. For Bayern Munich (team 1) create one variable ('gk') with the goalkeeper's name, and one array ('fieldPlayers') with all the remaining 10 field players 3. Create an array 'allPlayers' containing all players of both teams (22 players) 4. During the game, Bayern Munich (team 1) used 3 substitute players. So create a new array ('players1Final') containing all the original team1 players plus 'Thiago', 'Coutinho' and 'Perisic' 5. Based on the game.odds object, create one variable for each odd (called 'team1', 'draw' and 'team2') 6. Write a function ('printGoals') that receives an arbitrary number of player names (NOT an array) and prints each of them to the console, along with the number of goals that were scored in total (number of player names passed in) 7. The team with the lower odd is more likely to win. Print to the console which team is more likely to win, WITHOUT using an if/else statement or the ternary operator. TEST DATA FOR 6: Use players 'Davies', 'Muller', 'Lewandowski' and 'Kimmich'. Then, call the function again with players from game.scored GOOD LUCK 😀 */ /* // 1. const [players1, players2] = game.players; console.log(players1, players2); // 2. const [gk, ...fieldPlayers] = players1; console.log(gk, fieldPlayers); // 3. const allPlayers = [...players1, ...players2]; console.log(allPlayers); // 4. const players1Final = [...players1, 'Thiago', 'Coutinho', 'Periscic']; // 5. const { odds: { team1, x: draw, team2 }, } = game; console.log(team1, draw, team2); // 6. const printGoals = function (...players) { console.log(players); console.log(`${players.length} goals were scored`); }; // printGoals('Davies', 'Muller', 'Lewandowski', 'Kimmich'); // printGoals('Davies', 'Muller'); printGoals(...game.scored); // 7. team1 < team2 && console.log('Team 1 is more likely to win'); team1 > team2 && console.log('Team 2 is more likely to win'); /////////////////////////////////////// // Logical Assignment Operators const rest1 = { name: 'Capri', // numGuests: 20, numGuests: 0, }; const rest2 = { name: 'La Piazza', owner: 'Giovanni Rossi', }; // OR assignment operator // rest1.numGuests = rest1.numGuests || 10; // rest2.numGuests = rest2.numGuests || 10; // rest1.numGuests ||= 10; // rest2.numGuests ||= 10; // nullish assignment operator (null or undefined) rest1.numGuests ??= 10; rest2.numGuests ??= 10; // AND assignment operator // rest1.owner = rest1.owner && ''; // rest2.owner = rest2.owner && ''; rest1.owner &&= ''; rest2.owner &&= ''; console.log(rest1); console.log(rest2); /////////////////////////////////////// // The Nullish Coalescing Operator restaurant.numGuests = 0; const guests = restaurant.numGuests || 10; console.log(guests); // Nullish: null and undefined (NOT 0 or '') const guestCorrect = restaurant.numGuests ?? 10; console.log(guestCorrect); /////////////////////////////////////// // Short Circuiting (&& and ||) console.log('---- OR ----'); // Use ANY data type, return ANY data type, short-circuiting console.log(3 || 'Jonas'); console.log('' || 'Jonas'); console.log(true || 0); console.log(undefined || null); console.log(undefined || 0 || '' || 'Hello' || 23 || null); restaurant.numGuests = 0; const guests1 = restaurant.numGuests ? restaurant.numGuests : 10; console.log(guests1); const guests2 = restaurant.numGuests || 10; console.log(guests2); console.log('---- AND ----'); console.log(0 && 'Jonas'); console.log(7 && 'Jonas'); console.log('Hello' && 23 && null && 'jonas'); // Practical example if (restaurant.orderPizza) { restaurant.orderPizza('mushrooms', 'spinach'); } restaurant.orderPizza && restaurant.orderPizza('mushrooms', 'spinach'); /////////////////////////////////////// // Rest Pattern and Parameters // 1) Destructuring // SPREAD, because on RIGHT side of = const arr = [1, 2, ...[3, 4]]; // REST, because on LEFT side of = const [a, b, ...others] = [1, 2, 3, 4, 5]; console.log(a, b, others); const [pizza, , risotto, ...otherFood] = [ ...restaurant.mainMenu, ...restaurant.starterMenu, ]; console.log(pizza, risotto, otherFood); // Objects const { sat, ...weekdays } = restaurant.openingHours; console.log(weekdays); // 2) Functions const add = function (...numbers) { let sum = 0; for (let i = 0; i < numbers.length; i++) sum += numbers[i]; console.log(sum); }; add(2, 3); add(5, 3, 7, 2); add(8, 2, 5, 3, 2, 1, 4); const x = [23, 5, 7]; add(...x); restaurant.orderPizza('mushrooms', 'onion', 'olives', 'spinach'); restaurant.orderPizza('mushrooms'); /////////////////////////////////////// // The Spread Operator (...) const arr = [7, 8, 9]; const badNewArr = [1, 2, arr[0], arr[1], arr[2]]; console.log(badNewArr); const newArr = [1, 2, ...arr]; console.log(newArr); console.log(...newArr); console.log(1, 2, 7, 8, 9); const newMenu = [...restaurant.mainMenu, 'Gnocci']; console.log(newMenu); // Copy array const mainMenuCopy = [...restaurant.mainMenu]; // Join 2 arrays const menu = [...restaurant.starterMenu, ...restaurant.mainMenu]; console.log(menu); // Iterables: arrays, strings, maps, sets. NOT objects const str = 'Jonas'; const letters = [...str, ' ', 'S.']; console.log(letters); console.log(...str); // console.log(`${...str} Schmedtmann`); // Real-world example const ingredients = [ // prompt("Let's make pasta! Ingredient 1?"), // prompt('Ingredient 2?'), // prompt('Ingredient 3'), ]; console.log(ingredients); restaurant.orderPasta(ingredients[0], ingredients[1], ingredients[2]); restaurant.orderPasta(...ingredients); // Objects const newRestaurant = { foundedIn: 1998, ...restaurant, founder: 'Guiseppe' }; console.log(newRestaurant); const restaurantCopy = { ...restaurant }; restaurantCopy.name = 'Ristorante Roma'; console.log(restaurantCopy.name); console.log(restaurant.name); /////////////////////////////////////// // Destructuring Objects restaurant.orderDelivery({ time: '22:30', address: 'Via del Sole, 21', mainIndex: 2, starterIndex: 2, }); restaurant.orderDelivery({ address: 'Via del Sole, 21', starterIndex: 1, }); const { name, openingHours, categories } = restaurant; console.log(name, openingHours, categories); const { name: restaurantName, openingHours: hours, categories: tags, } = restaurant; console.log(restaurantName, hours, tags); // Default values const { menu = [], starterMenu: starters = [] } = restaurant; console.log(menu, starters); // Mutating variables let a = 111; let b = 999; const obj = { a: 23, b: 7, c: 14 }; ({ a, b } = obj); console.log(a, b); // Nested objects const { fri: { open: o, close: c }, } = openingHours; console.log(o, c); /////////////////////////////////////// // Destructuring Arrays const arr = [2, 3, 4]; const a = arr[0]; const b = arr[1]; const c = arr[2]; const [x, y, z] = arr; console.log(x, y, z); console.log(arr); let [main, , secondary] = restaurant.categories; console.log(main, secondary); // Switching variables // const temp = main; // main = secondary; // secondary = temp; // console.log(main, secondary); [main, secondary] = [secondary, main]; console.log(main, secondary); // Receive 2 return values from a function const [starter, mainCourse] = restaurant.order(2, 0); console.log(starter, mainCourse); // Nested destructuring const nested = [2, 4, [5, 6]]; // const [i, , j] = nested; const [i, , [j, k]] = nested; console.log(i, j, k); // Default values const [p = 1, q = 1, r = 1] = [8, 9]; console.log(p, q, r); */ ================================================ FILE: 09-Data-Structures-Operators/starter/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 09-Data-Structures-Operators/starter/index.html ================================================ Data Structures and Modern Operators

Data Structures and Modern Operators

================================================ FILE: 09-Data-Structures-Operators/starter/script.js ================================================ 'use strict'; // Data needed for a later exercise const flights = '_Delayed_Departure;fao93766109;txl2133758440;11:25+_Arrival;bru0943384722;fao93766109;11:45+_Delayed_Arrival;hel7439299980;fao93766109;12:05+_Departure;fao93766109;lis2323639855;12:30'; const italianFoods = new Set([ 'pasta', 'gnocchi', 'tomatoes', 'olive oil', 'garlic', 'basil', ]); const mexicanFoods = new Set([ 'tortillas', 'beans', 'rice', 'tomatoes', 'avocado', 'garlic', ]); // Data needed for first part of the section const restaurant = { name: 'Classico Italiano', location: 'Via Angelo Tavanti 23, Firenze, Italy', categories: ['Italian', 'Pizzeria', 'Vegetarian', 'Organic'], starterMenu: ['Focaccia', 'Bruschetta', 'Garlic Bread', 'Caprese Salad'], mainMenu: ['Pizza', 'Pasta', 'Risotto'], openingHours: { thu: { open: 12, close: 22, }, fri: { open: 11, close: 23, }, sat: { open: 0, // Open 24 hours close: 24, }, }, }; ================================================ FILE: 10-Functions/final/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 10-Functions/final/index.html ================================================ A Closer Look at Functions

A Closer Look at Functions

================================================ FILE: 10-Functions/final/script.js ================================================ 'use strict'; /* /////////////////////////////////////// // Default Parameters const bookings = []; const createBooking = function ( flightNum, numPassengers = 1, price = 199 * numPassengers ) { // ES5 // numPassengers = numPassengers || 1; // price = price || 199; const booking = { flightNum, numPassengers, price, }; console.log(booking); bookings.push(booking); }; createBooking('LH123'); createBooking('LH123', 2, 800); createBooking('LH123', 2); createBooking('LH123', 5); createBooking('LH123', undefined, 1000); /////////////////////////////////////// // How Passing Arguments Works: Values vs. Reference const flight = 'LH234'; const jonas = { name: 'Jonas Schmedtmann', passport: 24739479284, }; const checkIn = function (flightNum, passenger) { flightNum = 'LH999'; passenger.name = 'Mr. ' + passenger.name; if (passenger.passport === 24739479284) { alert('Checked in'); } else { alert('Wrong passport!'); } }; // checkIn(flight, jonas); // console.log(flight); // console.log(jonas); // Is the same as doing... // const flightNum = flight; // const passenger = jonas; const newPassport = function (person) { person.passport = Math.trunc(Math.random() * 100000000000); }; newPassport(jonas); checkIn(flight, jonas); /////////////////////////////////////// // Functions Accepting Callback Functions const oneWord = function (str) { return str.replace(/ /g, '').toLowerCase(); }; const upperFirstWord = function (str) { const [first, ...others] = str.split(' '); return [first.toUpperCase(), ...others].join(' '); }; // Higher-order function const transformer = function (str, fn) { console.log(`Original string: ${str}`); console.log(`Transformed string: ${fn(str)}`); console.log(`Transformed by: ${fn.name}`); }; transformer('JavaScript is the best!', upperFirstWord); transformer('JavaScript is the best!', oneWord); // JS uses callbacks all the time const high5 = function () { console.log('👋'); }; document.body.addEventListener('click', high5); ['Jonas', 'Martha', 'Adam'].forEach(high5); /////////////////////////////////////// // Functions Returning Functions const greet = function (greeting) { return function (name) { console.log(`${greeting} ${name}`); }; }; const greeterHey = greet('Hey'); greeterHey('Jonas'); greeterHey('Steven'); greet('Hello')('Jonas'); // Challenge const greetArr = greeting => name => console.log(`${greeting} ${name}`); greetArr('Hi')('Jonas'); /////////////////////////////////////// // The call and apply Methods const lufthansa = { airline: 'Lufthansa', iataCode: 'LH', bookings: [], // book: function() {} book(flightNum, name) { console.log( `${name} booked a seat on ${this.airline} flight ${this.iataCode}${flightNum}` ); this.bookings.push({ flight: `${this.iataCode}${flightNum}`, name }); }, }; lufthansa.book(239, 'Jonas Schmedtmann'); lufthansa.book(635, 'John Smith'); const eurowings = { airline: 'Eurowings', iataCode: 'EW', bookings: [], }; const book = lufthansa.book; // Does NOT work // book(23, 'Sarah Williams'); // Call method book.call(eurowings, 23, 'Sarah Williams'); console.log(eurowings); book.call(lufthansa, 239, 'Mary Cooper'); console.log(lufthansa); const swiss = { airline: 'Swiss Air Lines', iataCode: 'LX', bookings: [], }; book.call(swiss, 583, 'Mary Cooper'); // Apply method const flightData = [583, 'George Cooper']; book.apply(swiss, flightData); console.log(swiss); book.call(swiss, ...flightData); /////////////////////////////////////// // The bind Method // book.call(eurowings, 23, 'Sarah Williams'); const bookEW = book.bind(eurowings); const bookLH = book.bind(lufthansa); const bookLX = book.bind(swiss); bookEW(23, 'Steven Williams'); const bookEW23 = book.bind(eurowings, 23); bookEW23('Jonas Schmedtmann'); bookEW23('Martha Cooper'); // With Event Listeners lufthansa.planes = 300; lufthansa.buyPlane = function () { console.log(this); this.planes++; console.log(this.planes); }; // lufthansa.buyPlane(); document .querySelector('.buy') .addEventListener('click', lufthansa.buyPlane.bind(lufthansa)); // Partial application const addTax = (rate, value) => value + value * rate; console.log(addTax(0.1, 200)); const addVAT = addTax.bind(null, 0.23); // addVAT = value => value + value * 0.23; console.log(addVAT(100)); console.log(addVAT(23)); const addTaxRate = function (rate) { return function (value) { return value + value * rate; }; }; const addVAT2 = addTaxRate(0.23); console.log(addVAT2(100)); console.log(addVAT2(23)); */ /////////////////////////////////////// // Coding Challenge #1 /* Let's build a simple poll app! A poll has a question, an array of options from which people can choose, and an array with the number of replies for each option. This data is stored in the starter object below. Here are your tasks: 1. Create a method called 'registerNewAnswer' on the 'poll' object. The method does 2 things: 1.1. Display a prompt window for the user to input the number of the selected option. The prompt should look like this: What is your favourite programming language? 0: JavaScript 1: Python 2: Rust 3: C++ (Write option number) 1.2. Based on the input number, update the answers array. For example, if the option is 3, increase the value AT POSITION 3 of the array by 1. Make sure to check if the input is a number and if the number makes sense (e.g answer 52 wouldn't make sense, right?) 2. Call this method whenever the user clicks the "Answer poll" button. 3. Create a method 'displayResults' which displays the poll results. The method takes a string as an input (called 'type'), which can be either 'string' or 'array'. If type is 'array', simply display the results array as it is, using console.log(). This should be the default option. If type is 'string', display a string like "Poll results are 13, 2, 4, 1". 4. Run the 'displayResults' method at the end of each 'registerNewAnswer' method call. HINT: Use many of the tools you learned about in this and the last section 😉 BONUS: Use the 'displayResults' method to display the 2 arrays in the test data. Use both the 'array' and the 'string' option. Do NOT put the arrays in the poll object! So what shoud the this keyword look like in this situation? BONUS TEST DATA 1: [5, 2, 3] BONUS TEST DATA 2: [1, 5, 3, 9, 6, 1] GOOD LUCK 😀 */ /* const poll = { question: 'What is your favourite programming language?', options: ['0: JavaScript', '1: Python', '2: Rust', '3: C++'], // This generates [0, 0, 0, 0]. More in the next section 😃 answers: new Array(4).fill(0), registerNewAnswer() { // Get answer const answer = Number( prompt( `${this.question}\n${this.options.join('\n')}\n(Write option number)` ) ); console.log(answer); // Register answer typeof answer === 'number' && answer < this.answers.length && this.answers[answer]++; this.displayResults(); this.displayResults('string'); }, displayResults(type = 'array') { if (type === 'array') { console.log(this.answers); } else if (type === 'string') { // Poll results are 13, 2, 4, 1 console.log(`Poll results are ${this.answers.join(', ')}`); } }, }; document .querySelector('.poll') .addEventListener('click', poll.registerNewAnswer.bind(poll)); poll.displayResults.call({ answers: [5, 2, 3] }, 'string'); poll.displayResults.call({ answers: [1, 5, 3, 9, 6, 1] }, 'string'); poll.displayResults.call({ answers: [1, 5, 3, 9, 6, 1] }); // [5, 2, 3] // [1, 5, 3, 9, 6, 1] /////////////////////////////////////// // Immediately Invoked Function Expressions (IIFE) const runOnce = function () { console.log('This will never run again'); }; runOnce(); // IIFE (function () { console.log('This will never run again'); const isPrivate = 23; })(); // console.log(isPrivate); (() => console.log('This will ALSO never run again'))(); { const isPrivate = 23; var notPrivate = 46; } // console.log(isPrivate); console.log(notPrivate); /////////////////////////////////////// // Closures const secureBooking = function () { let passengerCount = 0; return function () { passengerCount++; console.log(`${passengerCount} passengers`); }; }; const booker = secureBooking(); booker(); booker(); booker(); console.dir(booker); /////////////////////////////////////// // More Closure Examples // Example 1 let f; const g = function () { const a = 23; f = function () { console.log(a * 2); }; }; const h = function () { const b = 777; f = function () { console.log(b * 2); }; }; g(); f(); console.dir(f); // Re-assigning f function h(); f(); console.dir(f); // Example 2 const boardPassengers = function (n, wait) { const perGroup = n / 3; setTimeout(function () { console.log(`We are now boarding all ${n} passengers`); console.log(`There are 3 groups, each with ${perGroup} passengers`); }, wait * 1000); console.log(`Will start boarding in ${wait} seconds`); }; const perGroup = 1000; boardPassengers(180, 3); */ /////////////////////////////////////// // Coding Challenge #2 /* This is more of a thinking challenge than a coding challenge 🤓 Take the IIFE below and at the end of the function, attach an event listener that changes the color of the selected h1 element ('header') to blue, each time the BODY element is clicked. Do NOT select the h1 element again! And now explain to YOURSELF (or someone around you) WHY this worked! Take all the time you need. Think about WHEN exactly the callback function is executed, and what that means for the variables involved in this example. GOOD LUCK 😀 */ /* (function () { const header = document.querySelector('h1'); header.style.color = 'red'; document.querySelector('body').addEventListener('click', function () { header.style.color = 'blue'; }); })(); */ ================================================ FILE: 10-Functions/starter/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 10-Functions/starter/index.html ================================================ A Closer Look at Functions

A Closer Look at Functions

================================================ FILE: 10-Functions/starter/script.js ================================================ 'use strict'; ================================================ FILE: 11-Arrays-Bankist/final/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 11-Arrays-Bankist/final/index.html ================================================ Bankist

Current balance

As of 05/03/2037

0000€

2 deposit
3 days ago
4 000€
1 withdrawal
24/01/2037
-378€

In

0000€

Out

0000€

Interest

0000€

Transfer money

Request loan

Close account

You will be logged out in 05:00

================================================ FILE: 11-Arrays-Bankist/final/script.js ================================================ 'use strict'; ///////////////////////////////////////////////// ///////////////////////////////////////////////// // BANKIST APP ///////////////////////////////////////////////// // Data const account1 = { owner: 'Jonas Schmedtmann', movements: [200, 450, -400, 3000, -650, -130, 70, 1300], interestRate: 1.2, // % pin: 1111, type: 'premium', }; const account2 = { owner: 'Jessica Davis', movements: [5000, 3400, -150, -790, -3210, -1000, 8500, -30], interestRate: 1.5, pin: 2222, type: 'standard', }; const account3 = { owner: 'Steven Thomas Williams', movements: [200, -200, 340, -300, -20, 50, 400, -460], interestRate: 0.7, pin: 3333, type: 'premium', }; const account4 = { owner: 'Sarah Smith', movements: [430, 1000, 700, 50, 90], interestRate: 1, pin: 4444, type: 'basic', }; const accounts = [account1, account2, account3, account4]; ///////////////////////////////////////////////// // Elements const labelWelcome = document.querySelector('.welcome'); const labelDate = document.querySelector('.date'); const labelBalance = document.querySelector('.balance__value'); const labelSumIn = document.querySelector('.summary__value--in'); const labelSumOut = document.querySelector('.summary__value--out'); const labelSumInterest = document.querySelector('.summary__value--interest'); const labelTimer = document.querySelector('.timer'); const containerApp = document.querySelector('.app'); const containerMovements = document.querySelector('.movements'); const btnLogin = document.querySelector('.login__btn'); const btnTransfer = document.querySelector('.form__btn--transfer'); const btnLoan = document.querySelector('.form__btn--loan'); const btnClose = document.querySelector('.form__btn--close'); const btnSort = document.querySelector('.btn--sort'); const inputLoginUsername = document.querySelector('.login__input--user'); const inputLoginPin = document.querySelector('.login__input--pin'); const inputTransferTo = document.querySelector('.form__input--to'); const inputTransferAmount = document.querySelector('.form__input--amount'); const inputLoanAmount = document.querySelector('.form__input--loan-amount'); const inputCloseUsername = document.querySelector('.form__input--user'); const inputClosePin = document.querySelector('.form__input--pin'); ///////////////////////////////////////////////// // Functions const displayMovements = function (movements, sort = false) { containerMovements.innerHTML = ''; const movs = sort ? movements.slice().sort((a, b) => a - b) : movements; movs.forEach(function (mov, i) { const type = mov > 0 ? 'deposit' : 'withdrawal'; const html = `
${ i + 1 } ${type}
${mov}€
`; containerMovements.insertAdjacentHTML('afterbegin', html); }); }; const calcDisplayBalance = function (acc) { acc.balance = acc.movements.reduce((acc, mov) => acc + mov, 0); labelBalance.textContent = `${acc.balance}€`; }; const calcDisplaySummary = function (acc) { const incomes = acc.movements .filter(mov => mov > 0) .reduce((acc, mov) => acc + mov, 0); labelSumIn.textContent = `${incomes}€`; const out = acc.movements .filter(mov => mov < 0) .reduce((acc, mov) => acc + mov, 0); labelSumOut.textContent = `${Math.abs(out)}€`; const interest = acc.movements .filter(mov => mov > 0) .map(deposit => (deposit * acc.interestRate) / 100) .filter((int, i, arr) => { // console.log(arr); return int >= 1; }) .reduce((acc, int) => acc + int, 0); labelSumInterest.textContent = `${interest}€`; }; const createUsernames = function (accs) { accs.forEach(function (acc) { acc.username = acc.owner .toLowerCase() .split(' ') .map(name => name[0]) .join(''); }); }; createUsernames(accounts); const updateUI = function (acc) { // Display movements displayMovements(acc.movements); // Display balance calcDisplayBalance(acc); // Display summary calcDisplaySummary(acc); }; /////////////////////////////////////// // Event handlers let currentAccount; btnLogin.addEventListener('click', function (e) { // Prevent form from submitting e.preventDefault(); currentAccount = accounts.find( acc => acc.username === inputLoginUsername.value ); console.log(currentAccount); if (currentAccount?.pin === Number(inputLoginPin.value)) { // Display UI and message labelWelcome.textContent = `Welcome back, ${ currentAccount.owner.split(' ')[0] }`; containerApp.style.opacity = 1; // Clear input fields inputLoginUsername.value = inputLoginPin.value = ''; inputLoginPin.blur(); // Update UI updateUI(currentAccount); } }); btnTransfer.addEventListener('click', function (e) { e.preventDefault(); const amount = Number(inputTransferAmount.value); const receiverAcc = accounts.find( acc => acc.username === inputTransferTo.value ); inputTransferAmount.value = inputTransferTo.value = ''; if ( amount > 0 && receiverAcc && currentAccount.balance >= amount && receiverAcc?.username !== currentAccount.username ) { // Doing the transfer currentAccount.movements.push(-amount); receiverAcc.movements.push(amount); // Update UI updateUI(currentAccount); } }); btnLoan.addEventListener('click', function (e) { e.preventDefault(); const amount = Number(inputLoanAmount.value); if (amount > 0 && currentAccount.movements.some(mov => mov >= amount * 0.1)) { // Add movement currentAccount.movements.push(amount); // Update UI updateUI(currentAccount); } inputLoanAmount.value = ''; }); btnClose.addEventListener('click', function (e) { e.preventDefault(); if ( inputCloseUsername.value === currentAccount.username && Number(inputClosePin.value) === currentAccount.pin ) { const index = accounts.findIndex( acc => acc.username === currentAccount.username ); console.log(index); // .indexOf(23) // Delete account accounts.splice(index, 1); // Hide UI containerApp.style.opacity = 0; } inputCloseUsername.value = inputClosePin.value = ''; }); let sorted = false; btnSort.addEventListener('click', function (e) { e.preventDefault(); displayMovements(currentAccount.movements, !sorted); sorted = !sorted; }); ///////////////////////////////////////////////// ///////////////////////////////////////////////// // LECTURES const movements = [200, 450, -400, 3000, -650, -130, 70, 1300]; /* ///////////////////////////////////////////////// // Simple Array Methods let arr = ['a', 'b', 'c', 'd', 'e']; // SLICE console.log(arr.slice(2)); console.log(arr.slice(2, 4)); console.log(arr.slice(-2)); console.log(arr.slice(-1)); console.log(arr.slice(1, -2)); console.log(arr.slice()); console.log([...arr]); // SPLICE // console.log(arr.splice(2)); arr.splice(-1); console.log(arr); arr.splice(1, 2); console.log(arr); // REVERSE arr = ['a', 'b', 'c', 'd', 'e']; const arr2 = ['j', 'i', 'h', 'g', 'f']; console.log(arr2.reverse()); console.log(arr2); // CONCAT const letters = arr.concat(arr2); console.log(letters); console.log([...arr, ...arr2]); // JOIN console.log(letters.join(' - ')); /////////////////////////////////////// // The new at Method const arr = [23, 11, 64]; console.log(arr[0]); console.log(arr.at(0)); // getting last array element console.log(arr[arr.length - 1]); console.log(arr.slice(-1)[0]); console.log(arr.at(-1)); console.log('jonas'.at(0)); console.log('jonas'.at(-1)); /////////////////////////////////////// // Looping Arrays: forEach const movements = [200, 450, -400, 3000, -650, -130, 70, 1300]; // for (const movement of movements) { for (const [i, movement] of movements.entries()) { if (movement > 0) { console.log(`Movement ${i + 1}: You deposited ${movement}`); } else { console.log(`Movement ${i + 1}: You withdrew ${Math.abs(movement)}`); } } console.log('---- FOREACH ----'); movements.forEach(function (mov, i, arr) { if (mov > 0) { console.log(`Movement ${i + 1}: You deposited ${mov}`); } else { console.log(`Movement ${i + 1}: You withdrew ${Math.abs(mov)}`); } }); // 0: function(200) // 1: function(450) // 2: function(400) // ... /////////////////////////////////////// // forEach With Maps and Sets // Map const currencies = new Map([ ['USD', 'United States dollar'], ['EUR', 'Euro'], ['GBP', 'Pound sterling'], ]); currencies.forEach(function (value, key, map) { console.log(`${key}: ${value}`); }); // Set const currenciesUnique = new Set(['USD', 'GBP', 'USD', 'EUR', 'EUR']); console.log(currenciesUnique); currenciesUnique.forEach(function (value, _, map) { console.log(`${value}: ${value}`); }); */ /////////////////////////////////////// // Coding Challenge #1 /* Julia and Kate are doing a study on dogs. So each of them asked 5 dog owners about their dog's age, and stored the data into an array (one array for each). For now, they are just interested in knowing whether a dog is an adult or a puppy. A dog is an adult if it is at least 3 years old, and it's a puppy if it's less than 3 years old. Create a function 'checkDogs', which accepts 2 arrays of dog's ages ('dogsJulia' and 'dogsKate'), and does the following things: 1. Julia found out that the owners of the FIRST and the LAST TWO dogs actually have cats, not dogs! So create a shallow copy of Julia's array, and remove the cat ages from that copied array (because it's a bad practice to mutate function parameters) 2. Create an array with both Julia's (corrected) and Kate's data 3. For each remaining dog, log to the console whether it's an adult ("Dog number 1 is an adult, and is 5 years old") or a puppy ("Dog number 2 is still a puppy 🐶") 4. Run the function for both test datasets HINT: Use tools from all lectures in this section so far 😉 TEST DATA 1: Julia's data [3, 5, 2, 12, 7], Kate's data [4, 1, 15, 8, 3] TEST DATA 2: Julia's data [9, 16, 6, 8, 3], Kate's data [10, 5, 6, 1, 4] GOOD LUCK 😀 */ /* const checkDogs = function (dogsJulia, dogsKate) { const dogsJuliaCorrected = dogsJulia.slice(); dogsJuliaCorrected.splice(0, 1); dogsJuliaCorrected.splice(-2); // dogsJulia.slice(1, 3); const dogs = dogsJuliaCorrected.concat(dogsKate); console.log(dogs); dogs.forEach(function (dog, i) { if (dog >= 3) { console.log(`Dog number ${i + 1} is an adult, and is ${dog} years old`); } else { console.log(`Dog number ${i + 1} is still a puppy 🐶`); } }); }; // checkDogs([3, 5, 2, 12, 7], [4, 1, 15, 8, 3]); checkDogs([9, 16, 6, 8, 3], [10, 5, 6, 1, 4]); /////////////////////////////////////// // The map Method const eurToUsd = 1.1; // const movementsUSD = movements.map(function (mov) { // return mov * eurToUsd; // }); const movementsUSD = movements.map(mov => mov * eurToUsd); console.log(movements); console.log(movementsUSD); const movementsUSDfor = []; for (const mov of movements) movementsUSDfor.push(mov * eurToUsd); console.log(movementsUSDfor); const movementsDescriptions = movements.map( (mov, i) => `Movement ${i + 1}: You ${mov > 0 ? 'deposited' : 'withdrew'} ${Math.abs( mov )}` ); console.log(movementsDescriptions); /////////////////////////////////////// // The filter Method const deposits = movements.filter(function (mov, i, arr) { return mov > 0; }); console.log(movements); console.log(deposits); const depositsFor = []; for (const mov of movements) if (mov > 0) depositsFor.push(mov); console.log(depositsFor); const withdrawals = movements.filter(mov => mov < 0); console.log(withdrawals); /////////////////////////////////////// // The reduce Method console.log(movements); // accumulator -> SNOWBALL // const balance = movements.reduce(function (acc, cur, i, arr) { // console.log(`Iteration ${i}: ${acc}`); // return acc + cur; // }, 0); const balance = movements.reduce((acc, cur) => acc + cur, 0); console.log(balance); let balance2 = 0; for (const mov of movements) balance2 += mov; console.log(balance2); // Maximum value const max = movements.reduce((acc, mov) => { if (acc > mov) return acc; else return mov; }, movements[0]); console.log(max); */ /////////////////////////////////////// // Coding Challenge #2 /* Let's go back to Julia and Kate's study about dogs. This time, they want to convert dog ages to human ages and calculate the average age of the dogs in their study. Create a function 'calcAverageHumanAge', which accepts an arrays of dog's ages ('ages'), and does the following things in order: 1. Calculate the dog age in human years using the following formula: if the dog is <= 2 years old, humanAge = 2 * dogAge. If the dog is > 2 years old, humanAge = 16 + dogAge * 4. 2. Exclude all dogs that are less than 18 human years old (which is the same as keeping dogs that are at least 18 years old) 3. Calculate the average human age of all adult dogs (you should already know from other challenges how we calculate averages 😉) 4. Run the function for both test datasets TEST DATA 1: [5, 2, 4, 1, 15, 8, 3] TEST DATA 2: [16, 6, 10, 5, 6, 1, 4] GOOD LUCK 😀 */ /* const calcAverageHumanAge = function (ages) { const humanAges = ages.map(age => (age <= 2 ? 2 * age : 16 + age * 4)); const adults = humanAges.filter(age => age >= 18); console.log(humanAges); console.log(adults); // const average = adults.reduce((acc, age) => acc + age, 0) / adults.length; const average = adults.reduce( (acc, age, i, arr) => acc + age / arr.length, 0 ); // 2 3. (2+3)/2 = 2.5 === 2/2+3/2 = 2.5 return average; }; const avg1 = calcAverageHumanAge([5, 2, 4, 1, 15, 8, 3]); const avg2 = calcAverageHumanAge([16, 6, 10, 5, 6, 1, 4]); console.log(avg1, avg2); /////////////////////////////////////// // The Magic of Chaining Methods const eurToUsd = 1.1; console.log(movements); // PIPELINE const totalDepositsUSD = movements .filter(mov => mov > 0) .map((mov, i, arr) => { // console.log(arr); return mov * eurToUsd; }) // .map(mov => mov * eurToUsd) .reduce((acc, mov) => acc + mov, 0); console.log(totalDepositsUSD); */ /////////////////////////////////////// // Coding Challenge #3 /* Rewrite the 'calcAverageHumanAge' function from the previous challenge, but this time as an arrow function, and using chaining! TEST DATA 1: [5, 2, 4, 1, 15, 8, 3] TEST DATA 2: [16, 6, 10, 5, 6, 1, 4] GOOD LUCK 😀 */ /* const calcAverageHumanAge = ages => ages .map(age => (age <= 2 ? 2 * age : 16 + age * 4)) .filter(age => age >= 18) .reduce((acc, age, i, arr) => acc + age / arr.length, 0); // adults.length const avg1 = calcAverageHumanAge([5, 2, 4, 1, 15, 8, 3]); const avg2 = calcAverageHumanAge([16, 6, 10, 5, 6, 1, 4]); console.log(avg1, avg2); /////////////////////////////////////// // The find Method const firstWithdrawal = movements.find(mov => mov < 0); console.log(movements); console.log(firstWithdrawal); console.log(accounts); const account = accounts.find(acc => acc.owner === 'Jessica Davis'); console.log(account); /////////////////////////////////////// // The New findLast and findLastIndex Methods console.log(movements); const lastWithdrawal = movements.findLast(mov => mov < 0); console.log(lastWithdrawal); // 'Your latest large movement was X movements ago' const latestLargeMovementIndex = movements.findLastIndex( mov => Math.abs(mov) > 1000 ); console.log(latestLargeMovementIndex); console.log( `Your latest large movement was ${ movements.length - latestLargeMovementIndex } movements ago` ); /////////////////////////////////////// // some and every console.log(movements); // EQUALITY console.log(movements.includes(-130)); // SOME: CONDITION console.log(movements.some(mov => mov === -130)); const anyDeposits = movements.some(mov => mov > 0); console.log(anyDeposits); // EVERY console.log(movements.every(mov => mov > 0)); console.log(account4.movements.every(mov => mov > 0)); // Separate callback const deposit = mov => mov > 0; console.log(movements.some(deposit)); console.log(movements.every(deposit)); console.log(movements.filter(deposit)); /////////////////////////////////////// // flat and flatMap const arr = [[1, 2, 3], [4, 5, 6], 7, 8]; console.log(arr.flat()); const arrDeep = [[[1, 2], 3], [4, [5, 6]], 7, 8]; console.log(arrDeep.flat(2)); // flat const overalBalance = accounts .map(acc => acc.movements) .flat() .reduce((acc, mov) => acc + mov, 0); console.log(overalBalance); // flatMap const overalBalance2 = accounts .flatMap(acc => acc.movements) .reduce((acc, mov) => acc + mov, 0); console.log(overalBalance2); */ /////////////////////////////////////// // Coding Challenge #4 /* This time, Julia and Kate are studying the activity levels of different dog breeds. YOUR TASKS: 1. Store the the average weight of a "Husky" in a variable "huskyWeight" 2. Find the name of the only breed that likes both "running" and "fetch" ("dogBothActivities" variable) 3. Create an array "allActivities" of all the activities of all the dog breeds 4. Create an array "uniqueActivities" that contains only the unique activities (no activity repetitions). HINT: Use a technique with a special data structure that we studied a few sections ago. 5. Many dog breeds like to swim. What other activities do these dogs like? Store all the OTHER activities these breeds like to do, in a unique array called "swimmingAdjacent". 6. Do all the breeds have an average weight of 10kg or more? Log to the console whether "true" or "false". 7. Are there any breeds that are "active"? "Active" means that the dog has 3 or more activities. Log to the console whether "true" or "false". BONUS: What's the average weight of the heaviest breed that likes to fetch? HINT: Use the "Math.max" method along with the ... operator. TEST DATA: */ /* const breeds = [ { breed: 'German Shepherd', averageWeight: 32, activities: ['fetch', 'swimming'], }, { breed: 'Dalmatian', averageWeight: 24, activities: ['running', 'fetch', 'agility'], }, { breed: 'Labrador', averageWeight: 28, activities: ['swimming', 'fetch'], }, { breed: 'Beagle', averageWeight: 12, activities: ['digging', 'fetch'], }, { breed: 'Husky', averageWeight: 26, activities: ['running', 'agility', 'swimming'], }, { breed: 'Bulldog', averageWeight: 36, activities: ['sleeping'], }, { breed: 'Poodle', averageWeight: 18, activities: ['agility', 'fetch'], }, ]; // 1. const huskyWeight = breeds.find(breed => breed.breed === 'Husky').averageWeight; console.log(huskyWeight); // 2. const dogBothActivities = breeds.find( breed => breed.activities.includes('fetch') && breed.activities.includes('running') ).breed; console.log(dogBothActivities); // 3. // const allActivities = breeds.map(breed => breed.activities).flat(); const allActivities = breeds.flatMap(breed => breed.activities); console.log(allActivities); // 4. const uniqueActivities = [...new Set(allActivities)]; console.log(uniqueActivities); // 5. const swimmingAdjacent = [ ...new Set( breeds .filter(breed => breed.activities.includes('swimming')) .flatMap(breed => breed.activities) .filter(activity => activity !== 'swimming') ), ]; console.log(swimmingAdjacent); // 6. console.log(breeds.every(breed => breed.averageWeight > 10)); // 7. console.log(breeds.some(breed => breed.activities.length >= 3)); // BONUS const fetchWeights = breeds .filter(breed => breed.activities.includes('fetch')) .map(breed => breed.averageWeight); const heaviestFetchBreed = Math.max(...fetchWeights); console.log(fetchWeights); console.log(heaviestFetchBreed); /////////////////////////////////////// // Sorting Arrays // Strings const owners = ['Jonas', 'Zach', 'Adam', 'Martha']; console.log(owners.sort()); console.log(owners); // Numbers console.log(movements); // return < 0, A, B (keep order) // return > 0, B, A (switch order) // Ascending // movements.sort((a, b) => { // if (a > b) return 1; // if (a < b) return -1; // }); movements.sort((a, b) => a - b); console.log(movements); // Descending // movements.sort((a, b) => { // if (a > b) return -1; // if (a < b) return 1; // }); movements.sort((a, b) => b - a); console.log(movements); /////////////////////////////////////// // Array Grouping console.log(movements); const groupedMovements = Object.groupBy(movements, movement => movement > 0 ? 'deposits' : 'withdrawals' ); console.log(groupedMovements); const groupedByActivity = Object.groupBy(accounts, account => { const movementCount = account.movements.length; if (movementCount >= 8) return 'very active'; if (movementCount >= 4) return 'active'; if (movementCount >= 1) return 'moderate'; return 'inactive'; }); console.log(groupedByActivity); // const groupedAccounts = Object.groupBy(accounts, account => account.type); const groupedAccounts = Object.groupBy(accounts, ({ type }) => type); console.log(groupedAccounts); /////////////////////////////////////// // More Ways of Creating and Filling Arrays const arr = [1, 2, 3, 4, 5, 6, 7]; console.log(new Array(1, 2, 3, 4, 5, 6, 7)); // Empty arrays + fill method const x = new Array(7); console.log(x); // console.log(x.map(() => 5)); x.fill(1, 3, 5); x.fill(1); console.log(x); arr.fill(23, 2, 6); console.log(arr); // Array.from const y = Array.from({ length: 7 }, () => 1); console.log(y); const z = Array.from({ length: 7 }, (_, i) => i + 1); console.log(z); labelBalance.addEventListener('click', function () { const movementsUI = Array.from( document.querySelectorAll('.movements__value'), el => Number(el.textContent.replace('€', '')) ); console.log(movementsUI); const movementsUI2 = [...document.querySelectorAll('.movements__value')]; }); /////////////////////////////////////// // Non-Destructive Alternatives: toReversed, toSorted, toSpliced, with console.log(movements); const reversedMov = movements.toReversed(); console.log(reversedMov); // toSorted (sort), toSpliced (splice) // movements[1] = 2000; const newMovements = movements.with(1, 2000); console.log(newMovements); console.log(movements); /////////////////////////////////////// // Array Methods Practice // 1. const bankDepositSum = accounts .flatMap(acc => acc.movements) .filter(mov => mov > 0) .reduce((sum, cur) => sum + cur, 0); console.log(bankDepositSum); // 2. // const numDeposits1000 = accounts // .flatMap(acc => acc.movements) // .filter(mov => mov >= 1000).length; const numDeposits1000 = accounts .flatMap(acc => acc.movements) .reduce((count, cur) => (cur >= 1000 ? ++count : count), 0); console.log(numDeposits1000); // Prefixed ++ operator let a = 10; console.log(++a); console.log(a); // 3. const { deposits, withdrawals } = accounts .flatMap(acc => acc.movements) .reduce( (sums, cur) => { // cur > 0 ? (sums.deposits += cur) : (sums.withdrawals += cur); sums[cur > 0 ? 'deposits' : 'withdrawals'] += cur; return sums; }, { deposits: 0, withdrawals: 0 }, ); console.log(deposits, withdrawals); // 4. // this is a nice title -> This Is a Nice Title const convertTitleCase = function (title) { const capitalize = str => str[0].toUpperCase() + str.slice(1); const exceptions = ['a', 'an', 'and', 'the', 'but', 'or', 'on', 'in', 'with']; const titleCase = title .toLowerCase() .split(' ') .map(word => (exceptions.includes(word) ? word : capitalize(word))) .join(' '); return capitalize(titleCase); }; console.log(convertTitleCase('this is a nice title')); console.log(convertTitleCase('this is a LONG title but not too long')); console.log(convertTitleCase('and here is another title with an EXAMPLE')); */ /////////////////////////////////////// // Coding Challenge #5 /* Julia and Kate are still studying dogs. This time they are want to figure out if the dogs in their are eating too much or too little food. - Formula for calculating recommended food portion: recommendedFood = weight ** 0.75 * 28. (The result is in grams of food, and the weight needs to be in kg) - Eating too much means the dog's current food portion is larger than the recommended portion, and eating too little is the opposite. - Eating an okay amount means the dog's current food portion is within a range 10% above and below the recommended portion (see hint). YOUR TASKS: 1. Loop over the array containing dog objects, and for each dog, calculate the recommended food portion (recFood) and add it to the object as a new property. Do NOT create a new array, simply loop over the array (We never did this before, so think about how you can do this without creating a new array). 2. Find Sarah's dog and log to the console whether it's eating too much or too little. HINT: Some dogs have multiple users, so you first need to find Sarah in the owners array, and so this one is a bit tricky (on purpose) 🤓 3. Create an array containing all owners of dogs who eat too much (ownersTooMuch) and an array with all owners of dogs who eat too little (ownersTooLittle). 4. Log a string to the console for each array created in 3., like this: "Matilda and Alice and Bob's dogs eat too much!" and "Sarah and John and Michael's dogs eat too little!" 5. Log to the console whether there is ANY dog eating EXACTLY the amount of food that is recommended (just true or false) 6. Log to the console whether ALL of the dogs are eating an OKAY amount of food (just true or false) 7. Create an array containing the dogs that are eating an OKAY amount of food (try to reuse the condition used in 6.) 8. Group the dogs into the following 3 groups: 'exact', 'too-much' and 'too-little', based on whether they are eating too much, too little or the exact amount of food, based on the recommended food portion. 9. Group the dogs by the number of owners they have 10. Sort the dogs array by recommended food portion in an ascending order. Make sure to NOT mutate the original array! HINT 1: Use many different tools to solve these challenges, you can use the summary lecture to choose between them 😉 HINT 2: Being within a range 10% above and below the recommended portion means: current > (recommended * 0.90) && current < (recommended * 1.10). Basically, the current portion should be between 90% and 110% of the recommended portion. TEST DATA: const dogs = [ { weight: 22, curFood: 250, owners: ['Alice', 'Bob'] }, { weight: 8, curFood: 200, owners: ['Matilda'] }, { weight: 13, curFood: 275, owners: ['Sarah', 'John', 'Leo'] }, { weight: 18, curFood: 244, owners: ['Joe'] }, { weight: 32, curFood: 340, owners: ['Michael'] }, ]; GOOD LUCK 😀 */ /* const dogs = [ { weight: 22, curFood: 250, owners: ['Alice', 'Bob'] }, { weight: 8, curFood: 200, owners: ['Matilda'] }, { weight: 13, curFood: 275, owners: ['Sarah', 'John', 'Leo'] }, { weight: 18, curFood: 244, owners: ['Joe'] }, { weight: 32, curFood: 340, owners: ['Michael'] }, ]; // 1. dogs.forEach(dog => (dog.recFood = Math.floor(dog.weight ** 0.75 * 28))); console.log(dogs); // 2. const dogSarah = dogs.find(dog => dog.owners.includes('Sarah')); console.log( `Sarah's dog eats too ${ dogSarah.curFood > dogSarah.recFood ? 'much' : 'little' }` ); // 3. const ownersTooMuch = dogs .filter(dog => dog.curFood > dog.recFood) .flatMap(dog => dog.owners); const ownersTooLittle = dogs .filter(dog => dog.curFood < dog.recFood) .flatMap(dog => dog.owners); console.log(ownersTooMuch); console.log(ownersTooLittle); // 4. console.log(`${ownersTooMuch.join(' and ')}'s dogs are eating too much`); console.log(`${ownersTooLittle.join(' and ')}'s dogs are eating too little`); // 5. console.log(dogs.some(dog => dog.curFood === dog.recFood)); // 6. const checkEatingOkay = dog => dog.curFood < dog.recFood * 1.1 && dog.curFood > dog.recFood * 0.9; console.log(dogs.every(checkEatingOkay)); // 7. const dogsEatingOkay = dogs.filter(checkEatingOkay); console.log(dogsEatingOkay); // 8. const dogsGroupedByPortion = Object.groupBy(dogs, dog => { if (dog.curFood > dog.recFood) { return 'too-much'; } else if (dog.curFood < dog.recFood) { return 'too-little'; } else { return 'exact'; } }); console.log(dogsGroupedByPortion); // 9. const dogsGroupedByOwners = Object.groupBy( dogs, dog => `${dog.owners.length}-owners` ); console.log(dogsGroupedByOwners); // 10. const dogsSorted = dogs.toSorted((a, b) => a.recFood - b.recFood); console.log(dogsSorted); */ ================================================ FILE: 11-Arrays-Bankist/final/style.css ================================================ /* * Use this CSS to learn some intersting techniques, * in case you're wondering how I built the UI. * Have fun! 😁 */ * { margin: 0; padding: 0; box-sizing: inherit; } html { font-size: 62.5%; box-sizing: border-box; } body { font-family: 'Poppins', sans-serif; color: #444; background-color: #f3f3f3; height: 100vh; padding: 2rem; } nav { display: flex; justify-content: space-between; align-items: center; padding: 0 2rem; } .welcome { font-size: 1.9rem; font-weight: 500; } .logo { height: 5.25rem; } .login { display: flex; } .login__input { border: none; padding: 0.5rem 2rem; font-size: 1.6rem; font-family: inherit; text-align: center; width: 12rem; border-radius: 10rem; margin-right: 1rem; color: inherit; border: 1px solid #fff; transition: all 0.3s; } .login__input:focus { outline: none; border: 1px solid #ccc; } .login__input::placeholder { color: #bbb; } .login__btn { border: none; background: none; font-size: 2.2rem; color: inherit; cursor: pointer; transition: all 0.3s; } .login__btn:hover, .login__btn:focus, .btn--sort:hover, .btn--sort:focus { outline: none; color: #777; } /* MAIN */ .app { position: relative; max-width: 100rem; margin: 4rem auto; display: grid; grid-template-columns: 4fr 3fr; grid-template-rows: auto repeat(3, 15rem) auto; gap: 2rem; /* NOTE This creates the fade in/out anumation */ opacity: 0; transition: all 1s; } .balance { grid-column: 1 / span 2; display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: 2rem; } .balance__label { font-size: 2.2rem; font-weight: 500; margin-bottom: -0.2rem; } .balance__date { font-size: 1.4rem; color: #888; } .balance__value { font-size: 4.5rem; font-weight: 400; } /* MOVEMENTS */ .movements { grid-row: 2 / span 3; background-color: #fff; border-radius: 1rem; overflow: scroll; } .movements__row { padding: 2.25rem 4rem; display: flex; align-items: center; border-bottom: 1px solid #eee; } .movements__type { font-size: 1.1rem; text-transform: uppercase; font-weight: 500; color: #fff; padding: 0.1rem 1rem; border-radius: 10rem; margin-right: 2rem; } .movements__date { font-size: 1.1rem; text-transform: uppercase; font-weight: 500; color: #666; } .movements__type--deposit { background-image: linear-gradient(to top left, #39b385, #9be15d); } .movements__type--withdrawal { background-image: linear-gradient(to top left, #e52a5a, #ff585f); } .movements__value { font-size: 1.7rem; margin-left: auto; } /* SUMMARY */ .summary { grid-row: 5 / 6; display: flex; align-items: baseline; padding: 0 0.3rem; margin-top: 1rem; } .summary__label { font-size: 1.2rem; font-weight: 500; text-transform: uppercase; margin-right: 0.8rem; } .summary__value { font-size: 2.2rem; margin-right: 2.5rem; } .summary__value--in, .summary__value--interest { color: #66c873; } .summary__value--out { color: #f5465d; } .btn--sort { margin-left: auto; border: none; background: none; font-size: 1.3rem; font-weight: 500; cursor: pointer; } /* OPERATIONS */ .operation { border-radius: 1rem; padding: 3rem 4rem; color: #333; } .operation--transfer { background-image: linear-gradient(to top left, #ffb003, #ffcb03); } .operation--loan { background-image: linear-gradient(to top left, #39b385, #9be15d); } .operation--close { background-image: linear-gradient(to top left, #e52a5a, #ff585f); } h2 { margin-bottom: 1.5rem; font-size: 1.7rem; font-weight: 600; color: #333; } .form { display: grid; grid-template-columns: 2.5fr 2.5fr 1fr; grid-template-rows: auto auto; gap: 0.4rem 1rem; } /* Exceptions for interst */ .form.form--loan { grid-template-columns: 2.5fr 1fr 2.5fr; } .form__label--loan { grid-row: 2; } /* End exceptions */ .form__input { width: 100%; border: none; background-color: rgba(255, 255, 255, 0.4); font-family: inherit; font-size: 1.5rem; text-align: center; color: #333; padding: 0.3rem 1rem; border-radius: 0.7rem; transition: all 0.3s; } .form__input:focus { outline: none; background-color: rgba(255, 255, 255, 0.6); } .form__label { font-size: 1.3rem; text-align: center; } .form__btn { border: none; border-radius: 0.7rem; font-size: 1.8rem; background-color: #fff; cursor: pointer; transition: all 0.3s; } .form__btn:focus { outline: none; background-color: rgba(255, 255, 255, 0.8); } .logout-timer { padding: 0 0.3rem; margin-top: 1.9rem; text-align: right; font-size: 1.25rem; } .timer { font-weight: 600; } ================================================ FILE: 11-Arrays-Bankist/starter/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 11-Arrays-Bankist/starter/index.html ================================================ Bankist

Current balance

As of 05/03/2037

0000€

2 deposit
3 days ago
4 000€
1 withdrawal
24/01/2037
-378€

In

0000€

Out

0000€

Interest

0000€

Transfer money

Request loan

Close account

You will be logged out in 05:00

================================================ FILE: 11-Arrays-Bankist/starter/script.js ================================================ 'use strict'; ///////////////////////////////////////////////// ///////////////////////////////////////////////// // BANKIST APP // Data const account1 = { owner: 'Jonas Schmedtmann', movements: [200, 450, -400, 3000, -650, -130, 70, 1300], interestRate: 1.2, // % pin: 1111, }; const account2 = { owner: 'Jessica Davis', movements: [5000, 3400, -150, -790, -3210, -1000, 8500, -30], interestRate: 1.5, pin: 2222, }; const account3 = { owner: 'Steven Thomas Williams', movements: [200, -200, 340, -300, -20, 50, 400, -460], interestRate: 0.7, pin: 3333, }; const account4 = { owner: 'Sarah Smith', movements: [430, 1000, 700, 50, 90], interestRate: 1, pin: 4444, }; const accounts = [account1, account2, account3, account4]; // Elements const labelWelcome = document.querySelector('.welcome'); const labelDate = document.querySelector('.date'); const labelBalance = document.querySelector('.balance__value'); const labelSumIn = document.querySelector('.summary__value--in'); const labelSumOut = document.querySelector('.summary__value--out'); const labelSumInterest = document.querySelector('.summary__value--interest'); const labelTimer = document.querySelector('.timer'); const containerApp = document.querySelector('.app'); const containerMovements = document.querySelector('.movements'); const btnLogin = document.querySelector('.login__btn'); const btnTransfer = document.querySelector('.form__btn--transfer'); const btnLoan = document.querySelector('.form__btn--loan'); const btnClose = document.querySelector('.form__btn--close'); const btnSort = document.querySelector('.btn--sort'); const inputLoginUsername = document.querySelector('.login__input--user'); const inputLoginPin = document.querySelector('.login__input--pin'); const inputTransferTo = document.querySelector('.form__input--to'); const inputTransferAmount = document.querySelector('.form__input--amount'); const inputLoanAmount = document.querySelector('.form__input--loan-amount'); const inputCloseUsername = document.querySelector('.form__input--user'); const inputClosePin = document.querySelector('.form__input--pin'); ///////////////////////////////////////////////// ///////////////////////////////////////////////// // LECTURES const currencies = new Map([ ['USD', 'United States dollar'], ['EUR', 'Euro'], ['GBP', 'Pound sterling'], ]); const movements = [200, 450, -400, 3000, -650, -130, 70, 1300]; ///////////////////////////////////////////////// ================================================ FILE: 11-Arrays-Bankist/starter/style.css ================================================ /* * Use this CSS to learn some intersting techniques, * in case you're wondering how I built the UI. * Have fun! 😁 */ * { margin: 0; padding: 0; box-sizing: inherit; } html { font-size: 62.5%; box-sizing: border-box; } body { font-family: "Poppins", sans-serif; color: #444; background-color: #f3f3f3; height: 100vh; padding: 2rem; } nav { display: flex; justify-content: space-between; align-items: center; padding: 0 2rem; } .welcome { font-size: 1.9rem; font-weight: 500; } .logo { height: 5.25rem; } .login { display: flex; } .login__input { border: none; padding: 0.5rem 2rem; font-size: 1.6rem; font-family: inherit; text-align: center; width: 12rem; border-radius: 10rem; margin-right: 1rem; color: inherit; border: 1px solid #fff; transition: all 0.3s; } .login__input:focus { outline: none; border: 1px solid #ccc; } .login__input::placeholder { color: #bbb; } .login__btn { border: none; background: none; font-size: 2.2rem; color: inherit; cursor: pointer; transition: all 0.3s; } .login__btn:hover, .login__btn:focus, .btn--sort:hover, .btn--sort:focus { outline: none; color: #777; } /* MAIN */ .app { position: relative; max-width: 100rem; margin: 4rem auto; display: grid; grid-template-columns: 4fr 3fr; grid-template-rows: auto repeat(3, 15rem) auto; gap: 2rem; /* NOTE This creates the fade in/out anumation */ opacity: 0; transition: all 1s; } .balance { grid-column: 1 / span 2; display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: 2rem; } .balance__label { font-size: 2.2rem; font-weight: 500; margin-bottom: -0.2rem; } .balance__date { font-size: 1.4rem; color: #888; } .balance__value { font-size: 4.5rem; font-weight: 400; } /* MOVEMENTS */ .movements { grid-row: 2 / span 3; background-color: #fff; border-radius: 1rem; overflow: scroll; } .movements__row { padding: 2.25rem 4rem; display: flex; align-items: center; border-bottom: 1px solid #eee; } .movements__type { font-size: 1.1rem; text-transform: uppercase; font-weight: 500; color: #fff; padding: 0.1rem 1rem; border-radius: 10rem; margin-right: 2rem; } .movements__date { font-size: 1.1rem; text-transform: uppercase; font-weight: 500; color: #666; } .movements__type--deposit { background-image: linear-gradient(to top left, #39b385, #9be15d); } .movements__type--withdrawal { background-image: linear-gradient(to top left, #e52a5a, #ff585f); } .movements__value { font-size: 1.7rem; margin-left: auto; } /* SUMMARY */ .summary { grid-row: 5 / 6; display: flex; align-items: baseline; padding: 0 0.3rem; margin-top: 1rem; } .summary__label { font-size: 1.2rem; font-weight: 500; text-transform: uppercase; margin-right: 0.8rem; } .summary__value { font-size: 2.2rem; margin-right: 2.5rem; } .summary__value--in, .summary__value--interest { color: #66c873; } .summary__value--out { color: #f5465d; } .btn--sort { margin-left: auto; border: none; background: none; font-size: 1.3rem; font-weight: 500; cursor: pointer; } /* OPERATIONS */ .operation { border-radius: 1rem; padding: 3rem 4rem; color: #333; } .operation--transfer { background-image: linear-gradient(to top left, #ffb003, #ffcb03); } .operation--loan { background-image: linear-gradient(to top left, #39b385, #9be15d); } .operation--close { background-image: linear-gradient(to top left, #e52a5a, #ff585f); } h2 { margin-bottom: 1.5rem; font-size: 1.7rem; font-weight: 600; color: #333; } .form { display: grid; grid-template-columns: 2.5fr 2.5fr 1fr; grid-template-rows: auto auto; gap: 0.4rem 1rem; } /* Exceptions for interst */ .form.form--loan { grid-template-columns: 2.5fr 1fr 2.5fr; } .form__label--loan { grid-row: 2; } /* End exceptions */ .form__input { width: 100%; border: none; background-color: rgba(255, 255, 255, 0.4); font-family: inherit; font-size: 1.5rem; text-align: center; color: #333; padding: 0.3rem 1rem; border-radius: 0.7rem; transition: all 0.3s; } .form__input:focus { outline: none; background-color: rgba(255, 255, 255, 0.6); } .form__label { font-size: 1.3rem; text-align: center; } .form__btn { border: none; border-radius: 0.7rem; font-size: 1.8rem; background-color: #fff; cursor: pointer; transition: all 0.3s; } .form__btn:focus { outline: none; background-color: rgba(255, 255, 255, 0.8); } .logout-timer { padding: 0 0.3rem; margin-top: 1.9rem; text-align: right; font-size: 1.25rem; } .timer { font-weight: 600; } ================================================ FILE: 12-Numbers-Dates-Timers-Bankist/final/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 12-Numbers-Dates-Timers-Bankist/final/index.html ================================================ Bankist

Current balance

As of 05/03/2037

0000€

2 deposit
3 days ago
4 000€
1 withdrawal
24/01/2037
-378€

In

0000€

Out

0000€

Interest

0000€

Transfer money

Request loan

Close account

You will be logged out in 05:00

================================================ FILE: 12-Numbers-Dates-Timers-Bankist/final/script.js ================================================ 'use strict'; ///////////////////////////////////////////////// ///////////////////////////////////////////////// // BANKIST APP ///////////////////////////////////////////////// // Data // DIFFERENT DATA! Contains movement dates, currency and locale const account1 = { owner: 'Jonas Schmedtmann', movements: [200, 455.23, -306.5, 25000, -642.21, -133.9, 79.97, 1300], interestRate: 1.2, // % pin: 1111, movementsDates: [ '2019-11-18T21:31:17.178Z', '2019-12-23T07:42:02.383Z', '2020-01-28T09:15:04.904Z', '2020-04-01T10:17:24.185Z', '2020-05-08T14:11:59.604Z', '2020-07-26T17:01:17.194Z', '2020-07-28T23:36:17.929Z', '2020-08-01T10:51:36.790Z', ], currency: 'EUR', locale: 'pt-PT', // de-DE }; const account2 = { owner: 'Jessica Davis', movements: [5000, 3400, -150, -790, -3210, -1000, 8500, -30], interestRate: 1.5, pin: 2222, movementsDates: [ '2019-11-01T13:15:33.035Z', '2019-11-30T09:48:16.867Z', '2019-12-25T06:04:23.907Z', '2020-01-25T14:18:46.235Z', '2020-02-05T16:33:06.386Z', '2020-04-10T14:43:26.374Z', '2020-06-25T18:49:59.371Z', '2020-07-26T12:01:20.894Z', ], currency: 'USD', locale: 'en-US', }; const accounts = [account1, account2]; ///////////////////////////////////////////////// // Elements const labelWelcome = document.querySelector('.welcome'); const labelDate = document.querySelector('.date'); const labelBalance = document.querySelector('.balance__value'); const labelSumIn = document.querySelector('.summary__value--in'); const labelSumOut = document.querySelector('.summary__value--out'); const labelSumInterest = document.querySelector('.summary__value--interest'); const labelTimer = document.querySelector('.timer'); const containerApp = document.querySelector('.app'); const containerMovements = document.querySelector('.movements'); const btnLogin = document.querySelector('.login__btn'); const btnTransfer = document.querySelector('.form__btn--transfer'); const btnLoan = document.querySelector('.form__btn--loan'); const btnClose = document.querySelector('.form__btn--close'); const btnSort = document.querySelector('.btn--sort'); const inputLoginUsername = document.querySelector('.login__input--user'); const inputLoginPin = document.querySelector('.login__input--pin'); const inputTransferTo = document.querySelector('.form__input--to'); const inputTransferAmount = document.querySelector('.form__input--amount'); const inputLoanAmount = document.querySelector('.form__input--loan-amount'); const inputCloseUsername = document.querySelector('.form__input--user'); const inputClosePin = document.querySelector('.form__input--pin'); ///////////////////////////////////////////////// // Functions const formatMovementDate = function (date, locale) { const calcDaysPassed = (date1, date2) => Math.round(Math.abs(date2 - date1) / (1000 * 60 * 60 * 24)); const daysPassed = calcDaysPassed(new Date(), date); console.log(daysPassed); if (daysPassed === 0) return 'Today'; if (daysPassed === 1) return 'Yesterday'; if (daysPassed <= 7) return `${daysPassed} days ago`; // const day = `${date.getDate()}`.padStart(2, 0); // const month = `${date.getMonth() + 1}`.padStart(2, 0); // const year = date.getFullYear(); // return `${day}/${month}/${year}`; return new Intl.DateTimeFormat(locale).format(date); }; const formatCur = function (value, locale, currency) { return new Intl.NumberFormat(locale, { style: 'currency', currency: currency, }).format(value); }; const displayMovements = function (acc, sort = false) { containerMovements.innerHTML = ''; const combinedMovsDates = acc.movements.map((mov, i) => ({ movement: mov, movementDate: acc.movementsDates.at(i), })); if (sort) combinedMovsDates.sort((a, b) => a.movement - b.movement); combinedMovsDates.forEach(function (obj, i) { const { movement, movementDate } = obj; const type = movement > 0 ? 'deposit' : 'withdrawal'; const date = new Date(movementDate); const displayDate = formatMovementDate(date, acc.locale); const formattedMov = formatCur(movement, acc.locale, acc.currency); const html = `
${ i + 1 } ${type}
${displayDate}
${formattedMov}
`; containerMovements.insertAdjacentHTML('afterbegin', html); }); }; const calcDisplayBalance = function (acc) { acc.balance = acc.movements.reduce((acc, mov) => acc + mov, 0); labelBalance.textContent = formatCur(acc.balance, acc.locale, acc.currency); }; const calcDisplaySummary = function (acc) { const incomes = acc.movements .filter(mov => mov > 0) .reduce((acc, mov) => acc + mov, 0); labelSumIn.textContent = formatCur(incomes, acc.locale, acc.currency); const out = acc.movements .filter(mov => mov < 0) .reduce((acc, mov) => acc + mov, 0); labelSumOut.textContent = formatCur(Math.abs(out), acc.locale, acc.currency); const interest = acc.movements .filter(mov => mov > 0) .map(deposit => (deposit * acc.interestRate) / 100) .filter((int, i, arr) => { // console.log(arr); return int >= 1; }) .reduce((acc, int) => acc + int, 0); labelSumInterest.textContent = formatCur(interest, acc.locale, acc.currency); }; const createUsernames = function (accs) { accs.forEach(function (acc) { acc.username = acc.owner .toLowerCase() .split(' ') .map(name => name[0]) .join(''); }); }; createUsernames(accounts); const updateUI = function (acc) { // Display movements displayMovements(acc); // Display balance calcDisplayBalance(acc); // Display summary calcDisplaySummary(acc); }; const startLogOutTimer = function () { const tick = function () { const min = String(Math.trunc(time / 60)).padStart(2, 0); const sec = String(time % 60).padStart(2, 0); // In each call, print the remaining time to UI labelTimer.textContent = `${min}:${sec}`; // When 0 seconds, stop timer and log out user if (time === 0) { clearInterval(timer); labelWelcome.textContent = 'Log in to get started'; containerApp.style.opacity = 0; } // Decrease 1s time--; }; // Set time to 5 minutes let time = 120; // Call the timer every second tick(); const timer = setInterval(tick, 1000); return timer; }; /////////////////////////////////////// // Event handlers let currentAccount, timer; // FAKE ALWAYS LOGGED IN // currentAccount = account1; // updateUI(currentAccount); // containerApp.style.opacity = 100; btnLogin.addEventListener('click', function (e) { // Prevent form from submitting e.preventDefault(); currentAccount = accounts.find( acc => acc.username === inputLoginUsername.value ); console.log(currentAccount); if (currentAccount?.pin === +inputLoginPin.value) { // Display UI and message labelWelcome.textContent = `Welcome back, ${ currentAccount.owner.split(' ')[0] }`; containerApp.style.opacity = 100; // Create current date and time const now = new Date(); const options = { hour: 'numeric', minute: 'numeric', day: 'numeric', month: 'numeric', year: 'numeric', // weekday: 'long', }; // const locale = navigator.language; // console.log(locale); labelDate.textContent = new Intl.DateTimeFormat( currentAccount.locale, options ).format(now); // const day = `${now.getDate()}`.padStart(2, 0); // const month = `${now.getMonth() + 1}`.padStart(2, 0); // const year = now.getFullYear(); // const hour = `${now.getHours()}`.padStart(2, 0); // const min = `${now.getMinutes()}`.padStart(2, 0); // labelDate.textContent = `${day}/${month}/${year}, ${hour}:${min}`; // Clear input fields inputLoginUsername.value = inputLoginPin.value = ''; inputLoginPin.blur(); // Timer if (timer) clearInterval(timer); timer = startLogOutTimer(); // Update UI updateUI(currentAccount); } }); btnTransfer.addEventListener('click', function (e) { e.preventDefault(); const amount = +inputTransferAmount.value; const receiverAcc = accounts.find( acc => acc.username === inputTransferTo.value ); inputTransferAmount.value = inputTransferTo.value = ''; if ( amount > 0 && receiverAcc && currentAccount.balance >= amount && receiverAcc?.username !== currentAccount.username ) { // Doing the transfer currentAccount.movements.push(-amount); receiverAcc.movements.push(amount); // Add transfer date currentAccount.movementsDates.push(new Date().toISOString()); receiverAcc.movementsDates.push(new Date().toISOString()); // Update UI updateUI(currentAccount); // Reset timer clearInterval(timer); timer = startLogOutTimer(); } }); btnLoan.addEventListener('click', function (e) { e.preventDefault(); const amount = Math.floor(inputLoanAmount.value); if (amount > 0 && currentAccount.movements.some(mov => mov >= amount * 0.1)) { setTimeout(function () { // Add movement currentAccount.movements.push(amount); // Add loan date currentAccount.movementsDates.push(new Date().toISOString()); // Update UI updateUI(currentAccount); // Reset timer clearInterval(timer); timer = startLogOutTimer(); }, 2500); } inputLoanAmount.value = ''; }); btnClose.addEventListener('click', function (e) { e.preventDefault(); if ( inputCloseUsername.value === currentAccount.username && +inputClosePin.value === currentAccount.pin ) { const index = accounts.findIndex( acc => acc.username === currentAccount.username ); console.log(index); // .indexOf(23) // Delete account accounts.splice(index, 1); // Hide UI containerApp.style.opacity = 0; } inputCloseUsername.value = inputClosePin.value = ''; }); let sorted = false; btnSort.addEventListener('click', function (e) { e.preventDefault(); // BUG in video: // displayMovements(currentAccount.movements, !sorted); // FIX: displayMovements(currentAccount, !sorted); sorted = !sorted; }); ///////////////////////////////////////////////// ///////////////////////////////////////////////// // LECTURES /* /////////////////////////////////////// // Converting and Checking Numbers console.log(23 === 23.0); // Base 10 - 0 to 9. 1/10 = 0.1. 3/10 = 3.3333333 // Binary base 2 - 0 1 console.log(0.1 + 0.2); console.log(0.1 + 0.2 === 0.3); // Conversion console.log(Number('23')); console.log(+'23'); // Parsing console.log(Number.parseInt('30px', 10)); console.log(Number.parseInt('e23', 10)); console.log(Number.parseInt(' 2.5rem ')); console.log(Number.parseFloat(' 2.5rem ')); // console.log(parseFloat(' 2.5rem ')); // Check if value is NaN console.log(Number.isNaN(20)); console.log(Number.isNaN('20')); console.log(Number.isNaN(+'20X')); console.log(Number.isNaN(23 / 0)); // Checking if value is number console.log(Number.isFinite(20)); console.log(Number.isFinite('20')); console.log(Number.isFinite(+'20X')); console.log(Number.isFinite(23 / 0)); console.log(Number.isInteger(23)); console.log(Number.isInteger(23.0)); console.log(Number.isInteger(23 / 0)); /////////////////////////////////////// // Math and Rounding console.log(Math.sqrt(25)); console.log(25 ** (1 / 2)); console.log(8 ** (1 / 3)); console.log(Math.max(5, 18, 23, 11, 2)); console.log(Math.max(5, 18, '23', 11, 2)); console.log(Math.max(5, 18, '23px', 11, 2)); console.log(Math.min(5, 18, 23, 11, 2)); console.log(Math.PI * Number.parseFloat('10px') ** 2); console.log(Math.trunc(Math.random() * 6) + 1); const randomInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; console.log(randomInt(10, 20)); console.log(randomInt(0, 3)); // Rounding integers console.log(Math.round(23.3)); console.log(Math.round(23.9)); console.log(Math.ceil(23.3)); console.log(Math.ceil(23.9)); console.log(Math.floor(23.3)); console.log(Math.floor('23.9')); console.log(Math.trunc(23.3)); console.log(Math.trunc(-23.3)); console.log(Math.floor(-23.3)); // Rounding decimals console.log((2.7).toFixed(0)); console.log((2.7).toFixed(3)); console.log((2.345).toFixed(2)); console.log(+(2.345).toFixed(2)); /////////////////////////////////////// // The Remainder Operator console.log(5 % 2); console.log(5 / 2); // 5 = 2 * 2 + 1 console.log(8 % 3); console.log(8 / 3); // 8 = 2 * 3 + 2 console.log(6 % 2); console.log(6 / 2); console.log(7 % 2); console.log(7 / 2); const isEven = n => n % 2 === 0; console.log(isEven(8)); console.log(isEven(23)); console.log(isEven(514)); labelBalance.addEventListener('click', function () { [...document.querySelectorAll('.movements__row')].forEach(function (row, i) { // 0, 2, 4, 6 if (i % 2 === 0) row.style.backgroundColor = 'orangered'; // 0, 3, 6, 9 if (i % 3 === 0) row.style.backgroundColor = 'blue'; }); }); /////////////////////////////////////// // Numeric Separators // 287,460,000,000 const diameter = 287_460_000_000; console.log(diameter); const price = 345_99; console.log(price); const transferFee1 = 15_00; const transferFee2 = 1_500; const PI = 3.1415; console.log(PI); console.log(Number('230_000')); console.log(parseInt('230_000')); /////////////////////////////////////// // Working with BigInt console.log(2 ** 53 - 1); console.log(Number.MAX_SAFE_INTEGER); console.log(2 ** 53 + 1); console.log(2 ** 53 + 2); console.log(2 ** 53 + 3); console.log(2 ** 53 + 4); console.log(4838430248342043823408394839483204n); console.log(BigInt(48384302)); // Operations console.log(10000n + 10000n); console.log(36286372637263726376237263726372632n * 10000000n); // console.log(Math.sqrt(16n)); const huge = 20289830237283728378237n; const num = 23; console.log(huge * BigInt(num)); // Exceptions console.log(20n > 15); console.log(20n === 20); console.log(typeof 20n); console.log(20n == '20'); console.log(huge + ' is REALLY big!!!'); // Divisions console.log(11n / 3n); console.log(10 / 3); /////////////////////////////////////// // Creating Dates // Create a date const now = new Date(); console.log(now); console.log(new Date('Aug 02 2020 18:05:41')); console.log(new Date('December 24, 2015')); console.log(new Date(account1.movementsDates[0])); console.log(new Date(2037, 10, 19, 15, 23, 5)); console.log(new Date(2037, 10, 31)); console.log(new Date(0)); console.log(new Date(3 * 24 * 60 * 60 * 1000)); // Working with dates const future = new Date(2037, 10, 19, 15, 23); console.log(future); console.log(future.getFullYear()); console.log(future.getMonth()); console.log(future.getDate()); console.log(future.getDay()); console.log(future.getHours()); console.log(future.getMinutes()); console.log(future.getSeconds()); console.log(future.toISOString()); console.log(future.getTime()); console.log(new Date(2142256980000)); console.log(Date.now()); future.setFullYear(2040); console.log(future); /////////////////////////////////////// // Operations With Dates const future = new Date(2037, 10, 19, 15, 23); console.log(+future); const calcDaysPassed = (date1, date2) => Math.abs(date2 - date1) / (1000 * 60 * 60 * 24); const days1 = calcDaysPassed(new Date(2037, 3, 4), new Date(2037, 3, 14)); console.log(days1); /////////////////////////////////////// // Internationalizing Numbers (Intl) const num = 3884764.23; const options = { style: 'currency', unit: 'celsius', currency: 'EUR', // useGrouping: false, }; console.log('US: ', new Intl.NumberFormat('en-US', options).format(num)); console.log('Germany: ', new Intl.NumberFormat('de-DE', options).format(num)); console.log('Syria: ', new Intl.NumberFormat('ar-SY', options).format(num)); console.log( navigator.language, new Intl.NumberFormat(navigator.language, options).format(num) ); /////////////////////////////////////// // Timers // setTimeout const ingredients = ['olives', 'spinach']; const pizzaTimer = setTimeout( (ing1, ing2) => console.log(`Here is your pizza with ${ing1} and ${ing2} 🍕`), 3000, ...ingredients ); console.log('Waiting...'); if (ingredients.includes('spinach')) clearTimeout(pizzaTimer); // setInterval setInterval(function () { const now = new Date(); console.log(now); }, 1000); */ ================================================ FILE: 12-Numbers-Dates-Timers-Bankist/final/style.css ================================================ /* * Use this CSS to learn some intersting techniques, * in case you're wondering how I built the UI. * Have fun! 😁 */ * { margin: 0; padding: 0; box-sizing: inherit; } html { font-size: 62.5%; box-sizing: border-box; } body { font-family: 'Poppins', sans-serif; color: #444; background-color: #f3f3f3; height: 100vh; padding: 2rem; } nav { display: flex; justify-content: space-between; align-items: center; padding: 0 2rem; } .welcome { font-size: 1.9rem; font-weight: 500; } .logo { height: 5.25rem; } .login { display: flex; } .login__input { border: none; padding: 0.5rem 2rem; font-size: 1.6rem; font-family: inherit; text-align: center; width: 12rem; border-radius: 10rem; margin-right: 1rem; color: inherit; border: 1px solid #fff; transition: all 0.3s; } .login__input:focus { outline: none; border: 1px solid #ccc; } .login__input::placeholder { color: #bbb; } .login__btn { border: none; background: none; font-size: 2.2rem; color: inherit; cursor: pointer; transition: all 0.3s; } .login__btn:hover, .login__btn:focus, .btn--sort:hover, .btn--sort:focus { outline: none; color: #777; } /* MAIN */ .app { position: relative; max-width: 100rem; margin: 4rem auto; display: grid; grid-template-columns: 4fr 3fr; grid-template-rows: auto repeat(3, 15rem) auto; gap: 2rem; /* NOTE This creates the fade in/out anumation */ opacity: 0; transition: all 1s; } .balance { grid-column: 1 / span 2; display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: 2rem; } .balance__label { font-size: 2.2rem; font-weight: 500; margin-bottom: -0.2rem; } .balance__date { font-size: 1.4rem; color: #888; } .balance__value { font-size: 4.5rem; font-weight: 400; } /* MOVEMENTS */ .movements { grid-row: 2 / span 3; background-color: #fff; border-radius: 1rem; overflow: scroll; } .movements__row { padding: 2.25rem 4rem; display: flex; align-items: center; border-bottom: 1px solid #eee; } .movements__type { font-size: 1.1rem; text-transform: uppercase; font-weight: 500; color: #fff; padding: 0.1rem 1rem; border-radius: 10rem; margin-right: 2rem; } .movements__date { font-size: 1.1rem; text-transform: uppercase; font-weight: 500; color: #666; } .movements__type--deposit { background-image: linear-gradient(to top left, #39b385, #9be15d); } .movements__type--withdrawal { background-image: linear-gradient(to top left, #e52a5a, #ff585f); } .movements__value { font-size: 1.7rem; margin-left: auto; } /* SUMMARY */ .summary { grid-row: 5 / 6; display: flex; align-items: baseline; padding: 0 0.3rem; margin-top: 1rem; } .summary__label { font-size: 1.2rem; font-weight: 500; text-transform: uppercase; margin-right: 0.8rem; } .summary__value { font-size: 2.2rem; margin-right: 2.5rem; } .summary__value--in, .summary__value--interest { color: #66c873; } .summary__value--out { color: #f5465d; } .btn--sort { margin-left: auto; border: none; background: none; font-size: 1.3rem; font-weight: 500; cursor: pointer; } /* OPERATIONS */ .operation { border-radius: 1rem; padding: 3rem 4rem; color: #333; } .operation--transfer { background-image: linear-gradient(to top left, #ffb003, #ffcb03); } .operation--loan { background-image: linear-gradient(to top left, #39b385, #9be15d); } .operation--close { background-image: linear-gradient(to top left, #e52a5a, #ff585f); } h2 { margin-bottom: 1.5rem; font-size: 1.7rem; font-weight: 600; color: #333; } .form { display: grid; grid-template-columns: 2.5fr 2.5fr 1fr; grid-template-rows: auto auto; gap: 0.4rem 1rem; } /* Exceptions for interst */ .form.form--loan { grid-template-columns: 2.5fr 1fr 2.5fr; } .form__label--loan { grid-row: 2; } /* End exceptions */ .form__input { width: 100%; border: none; background-color: rgba(255, 255, 255, 0.4); font-family: inherit; font-size: 1.5rem; text-align: center; color: #333; padding: 0.3rem 1rem; border-radius: 0.7rem; transition: all 0.3s; } .form__input:focus { outline: none; background-color: rgba(255, 255, 255, 0.6); } .form__label { font-size: 1.3rem; text-align: center; } .form__btn { border: none; border-radius: 0.7rem; font-size: 1.8rem; background-color: #fff; cursor: pointer; transition: all 0.3s; } .form__btn:focus { outline: none; background-color: rgba(255, 255, 255, 0.8); } .logout-timer { padding: 0 0.3rem; margin-top: 1.9rem; text-align: right; font-size: 1.25rem; } .timer { font-weight: 600; } ================================================ FILE: 12-Numbers-Dates-Timers-Bankist/starter/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 12-Numbers-Dates-Timers-Bankist/starter/index.html ================================================ Bankist

Current balance

As of 05/03/2037

0000€

2 deposit
3 days ago
4 000€
1 withdrawal
24/01/2037
-378€

In

0000€

Out

0000€

Interest

0000€

Transfer money

Request loan

Close account

You will be logged out in 05:00

================================================ FILE: 12-Numbers-Dates-Timers-Bankist/starter/script.js ================================================ 'use strict'; ///////////////////////////////////////////////// ///////////////////////////////////////////////// // BANKIST APP ///////////////////////////////////////////////// // Data // DIFFERENT DATA! Contains movement dates, currency and locale const account1 = { owner: 'Jonas Schmedtmann', movements: [200, 455.23, -306.5, 25000, -642.21, -133.9, 79.97, 1300], interestRate: 1.2, // % pin: 1111, movementsDates: [ '2019-11-18T21:31:17.178Z', '2019-12-23T07:42:02.383Z', '2020-01-28T09:15:04.904Z', '2020-04-01T10:17:24.185Z', '2020-05-08T14:11:59.604Z', '2020-05-27T17:01:17.194Z', '2020-07-11T23:36:17.929Z', '2020-07-12T10:51:36.790Z', ], currency: 'EUR', locale: 'pt-PT', // de-DE }; const account2 = { owner: 'Jessica Davis', movements: [5000, 3400, -150, -790, -3210, -1000, 8500, -30], interestRate: 1.5, pin: 2222, movementsDates: [ '2019-11-01T13:15:33.035Z', '2019-11-30T09:48:16.867Z', '2019-12-25T06:04:23.907Z', '2020-01-25T14:18:46.235Z', '2020-02-05T16:33:06.386Z', '2020-04-10T14:43:26.374Z', '2020-06-25T18:49:59.371Z', '2020-07-26T12:01:20.894Z', ], currency: 'USD', locale: 'en-US', }; const accounts = [account1, account2]; ///////////////////////////////////////////////// // Elements const labelWelcome = document.querySelector('.welcome'); const labelDate = document.querySelector('.date'); const labelBalance = document.querySelector('.balance__value'); const labelSumIn = document.querySelector('.summary__value--in'); const labelSumOut = document.querySelector('.summary__value--out'); const labelSumInterest = document.querySelector('.summary__value--interest'); const labelTimer = document.querySelector('.timer'); const containerApp = document.querySelector('.app'); const containerMovements = document.querySelector('.movements'); const btnLogin = document.querySelector('.login__btn'); const btnTransfer = document.querySelector('.form__btn--transfer'); const btnLoan = document.querySelector('.form__btn--loan'); const btnClose = document.querySelector('.form__btn--close'); const btnSort = document.querySelector('.btn--sort'); const inputLoginUsername = document.querySelector('.login__input--user'); const inputLoginPin = document.querySelector('.login__input--pin'); const inputTransferTo = document.querySelector('.form__input--to'); const inputTransferAmount = document.querySelector('.form__input--amount'); const inputLoanAmount = document.querySelector('.form__input--loan-amount'); const inputCloseUsername = document.querySelector('.form__input--user'); const inputClosePin = document.querySelector('.form__input--pin'); ///////////////////////////////////////////////// // Functions const displayMovements = function (movements, sort = false) { containerMovements.innerHTML = ''; const movs = sort ? movements.slice().sort((a, b) => a - b) : movements; movs.forEach(function (mov, i) { const type = mov > 0 ? 'deposit' : 'withdrawal'; const html = `
${ i + 1 } ${type}
${mov}€
`; containerMovements.insertAdjacentHTML('afterbegin', html); }); }; const calcDisplayBalance = function (acc) { acc.balance = acc.movements.reduce((acc, mov) => acc + mov, 0); labelBalance.textContent = `${acc.balance}€`; }; const calcDisplaySummary = function (acc) { const incomes = acc.movements .filter(mov => mov > 0) .reduce((acc, mov) => acc + mov, 0); labelSumIn.textContent = `${incomes}€`; const out = acc.movements .filter(mov => mov < 0) .reduce((acc, mov) => acc + mov, 0); labelSumOut.textContent = `${Math.abs(out)}€`; const interest = acc.movements .filter(mov => mov > 0) .map(deposit => (deposit * acc.interestRate) / 100) .filter((int, i, arr) => { // console.log(arr); return int >= 1; }) .reduce((acc, int) => acc + int, 0); labelSumInterest.textContent = `${interest}€`; }; const createUsernames = function (accs) { accs.forEach(function (acc) { acc.username = acc.owner .toLowerCase() .split(' ') .map(name => name[0]) .join(''); }); }; createUsernames(accounts); const updateUI = function (acc) { // Display movements displayMovements(acc.movements); // Display balance calcDisplayBalance(acc); // Display summary calcDisplaySummary(acc); }; /////////////////////////////////////// // Event handlers let currentAccount; btnLogin.addEventListener('click', function (e) { // Prevent form from submitting e.preventDefault(); currentAccount = accounts.find( acc => acc.username === inputLoginUsername.value ); console.log(currentAccount); if (currentAccount?.pin === Number(inputLoginPin.value)) { // Display UI and message labelWelcome.textContent = `Welcome back, ${ currentAccount.owner.split(' ')[0] }`; containerApp.style.opacity = 100; // Clear input fields inputLoginUsername.value = inputLoginPin.value = ''; inputLoginPin.blur(); // Update UI updateUI(currentAccount); } }); btnTransfer.addEventListener('click', function (e) { e.preventDefault(); const amount = Number(inputTransferAmount.value); const receiverAcc = accounts.find( acc => acc.username === inputTransferTo.value ); inputTransferAmount.value = inputTransferTo.value = ''; if ( amount > 0 && receiverAcc && currentAccount.balance >= amount && receiverAcc?.username !== currentAccount.username ) { // Doing the transfer currentAccount.movements.push(-amount); receiverAcc.movements.push(amount); // Update UI updateUI(currentAccount); } }); btnLoan.addEventListener('click', function (e) { e.preventDefault(); const amount = Number(inputLoanAmount.value); if (amount > 0 && currentAccount.movements.some(mov => mov >= amount * 0.1)) { // Add movement currentAccount.movements.push(amount); // Update UI updateUI(currentAccount); } inputLoanAmount.value = ''; }); btnClose.addEventListener('click', function (e) { e.preventDefault(); if ( inputCloseUsername.value === currentAccount.username && Number(inputClosePin.value) === currentAccount.pin ) { const index = accounts.findIndex( acc => acc.username === currentAccount.username ); console.log(index); // .indexOf(23) // Delete account accounts.splice(index, 1); // Hide UI containerApp.style.opacity = 0; } inputCloseUsername.value = inputClosePin.value = ''; }); let sorted = false; btnSort.addEventListener('click', function (e) { e.preventDefault(); displayMovements(currentAccount.movements, !sorted); sorted = !sorted; }); ///////////////////////////////////////////////// ///////////////////////////////////////////////// // LECTURES ================================================ FILE: 12-Numbers-Dates-Timers-Bankist/starter/style.css ================================================ /* * Use this CSS to learn some intersting techniques, * in case you're wondering how I built the UI. * Have fun! 😁 */ * { margin: 0; padding: 0; box-sizing: inherit; } html { font-size: 62.5%; box-sizing: border-box; } body { font-family: 'Poppins', sans-serif; color: #444; background-color: #f3f3f3; height: 100vh; padding: 2rem; } nav { display: flex; justify-content: space-between; align-items: center; padding: 0 2rem; } .welcome { font-size: 1.9rem; font-weight: 500; } .logo { height: 5.25rem; } .login { display: flex; } .login__input { border: none; padding: 0.5rem 2rem; font-size: 1.6rem; font-family: inherit; text-align: center; width: 12rem; border-radius: 10rem; margin-right: 1rem; color: inherit; border: 1px solid #fff; transition: all 0.3s; } .login__input:focus { outline: none; border: 1px solid #ccc; } .login__input::placeholder { color: #bbb; } .login__btn { border: none; background: none; font-size: 2.2rem; color: inherit; cursor: pointer; transition: all 0.3s; } .login__btn:hover, .login__btn:focus, .btn--sort:hover, .btn--sort:focus { outline: none; color: #777; } /* MAIN */ .app { position: relative; max-width: 100rem; margin: 4rem auto; display: grid; grid-template-columns: 4fr 3fr; grid-template-rows: auto repeat(3, 15rem) auto; gap: 2rem; /* NOTE This creates the fade in/out anumation */ opacity: 0; transition: all 1s; } .balance { grid-column: 1 / span 2; display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: 2rem; } .balance__label { font-size: 2.2rem; font-weight: 500; margin-bottom: -0.2rem; } .balance__date { font-size: 1.4rem; color: #888; } .balance__value { font-size: 4.5rem; font-weight: 400; } /* MOVEMENTS */ .movements { grid-row: 2 / span 3; background-color: #fff; border-radius: 1rem; overflow: scroll; } .movements__row { padding: 2.25rem 4rem; display: flex; align-items: center; border-bottom: 1px solid #eee; } .movements__type { font-size: 1.1rem; text-transform: uppercase; font-weight: 500; color: #fff; padding: 0.1rem 1rem; border-radius: 10rem; margin-right: 2rem; } .movements__date { font-size: 1.1rem; text-transform: uppercase; font-weight: 500; color: #666; } .movements__type--deposit { background-image: linear-gradient(to top left, #39b385, #9be15d); } .movements__type--withdrawal { background-image: linear-gradient(to top left, #e52a5a, #ff585f); } .movements__value { font-size: 1.7rem; margin-left: auto; } /* SUMMARY */ .summary { grid-row: 5 / 6; display: flex; align-items: baseline; padding: 0 0.3rem; margin-top: 1rem; } .summary__label { font-size: 1.2rem; font-weight: 500; text-transform: uppercase; margin-right: 0.8rem; } .summary__value { font-size: 2.2rem; margin-right: 2.5rem; } .summary__value--in, .summary__value--interest { color: #66c873; } .summary__value--out { color: #f5465d; } .btn--sort { margin-left: auto; border: none; background: none; font-size: 1.3rem; font-weight: 500; cursor: pointer; } /* OPERATIONS */ .operation { border-radius: 1rem; padding: 3rem 4rem; color: #333; } .operation--transfer { background-image: linear-gradient(to top left, #ffb003, #ffcb03); } .operation--loan { background-image: linear-gradient(to top left, #39b385, #9be15d); } .operation--close { background-image: linear-gradient(to top left, #e52a5a, #ff585f); } h2 { margin-bottom: 1.5rem; font-size: 1.7rem; font-weight: 600; color: #333; } .form { display: grid; grid-template-columns: 2.5fr 2.5fr 1fr; grid-template-rows: auto auto; gap: 0.4rem 1rem; } /* Exceptions for interst */ .form.form--loan { grid-template-columns: 2.5fr 1fr 2.5fr; } .form__label--loan { grid-row: 2; } /* End exceptions */ .form__input { width: 100%; border: none; background-color: rgba(255, 255, 255, 0.4); font-family: inherit; font-size: 1.5rem; text-align: center; color: #333; padding: 0.3rem 1rem; border-radius: 0.7rem; transition: all 0.3s; } .form__input:focus { outline: none; background-color: rgba(255, 255, 255, 0.6); } .form__label { font-size: 1.3rem; text-align: center; } .form__btn { border: none; border-radius: 0.7rem; font-size: 1.8rem; background-color: #fff; cursor: pointer; transition: all 0.3s; } .form__btn:focus { outline: none; background-color: rgba(255, 255, 255, 0.8); } .logout-timer { padding: 0 0.3rem; margin-top: 1.9rem; text-align: right; font-size: 1.25rem; } .timer { font-weight: 600; } ================================================ FILE: 13-Advanced-DOM-Bankist/final/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 13-Advanced-DOM-Bankist/final/index.html ================================================ Bankist | When Banking meets Minimalist

When banking meets
minimalist

A simpler banking experience for a simpler life.

Minimalist bank items

Features

Everything you need in a modern bank and more.

Computer
100% digital bank

Lorem ipsum dolor sit amet consectetur adipisicing elit. Unde alias sint quos? Accusantium a fugiat porro reiciendis saepe quibusdam debitis ducimus.

Watch your money grow

Nesciunt quos autem dolorum voluptates cum dolores dicta fuga inventore ab? Nulla incidunt eius numquam sequi iste pariatur quibusdam!

Plant Credit card
Free debit card included

Quasi, fugit in cumque cupiditate reprehenderit debitis animi enim eveniet consequatur odit quam quos possimus assumenda dicta fuga inventore ab.

Operations

Everything as simple as possible, but no simpler.

Tranfser money to anyone, instantly! No fees, no BS.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Buy a home or make your dreams come true, with instant loans.

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

No longer need your account? No problem! Close it instantly.

Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Not sure yet?

Millions of Bankists are already making their lifes simpler.

Best financial decision ever!
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Accusantium quas quisquam non? Quas voluptate nulla minima deleniti optio ullam nesciunt, numquam corporis et asperiores laboriosam sunt, praesentium suscipit blanditiis. Necessitatibus id alias reiciendis, perferendis facere pariatur dolore veniam autem esse non voluptatem saepe provident nihil molestiae.
Aarav Lynn

San Francisco, USA

The last step to becoming a complete minimalist
Quisquam itaque deserunt ullam, quia ea repellendus provident, ducimus neque ipsam modi voluptatibus doloremque, corrupti laborum. Incidunt numquam perferendis veritatis neque repellendus. Lorem, ipsum dolor sit amet consectetur adipisicing elit. Illo deserunt exercitationem deleniti.
Miyah Miles

London, UK

Finally free from old-school banks
Debitis, nihil sit minus suscipit magni aperiam vel tenetur incidunt commodi architecto numquam omnis nulla autem, necessitatibus blanditiis modi similique quidem. Odio aliquam culpa dicta beatae quod maiores ipsa minus consequatur error sunt, deleniti saepe aliquid quos inventore sequi. Necessitatibus id alias reiciendis, perferendis facere.
Francisco Gomes

Lisbon, Portugal

================================================ FILE: 13-Advanced-DOM-Bankist/final/script.js ================================================ 'use strict'; const modal = document.querySelector('.modal'); const overlay = document.querySelector('.overlay'); const btnCloseModal = document.querySelector('.btn--close-modal'); const btnsOpenModal = document.querySelectorAll('.btn--show-modal'); const btnScrollTo = document.querySelector('.btn--scroll-to'); const section1 = document.querySelector('#section--1'); const nav = document.querySelector('.nav'); const tabs = document.querySelectorAll('.operations__tab'); const tabsContainer = document.querySelector('.operations__tab-container'); const tabsContent = document.querySelectorAll('.operations__content'); /////////////////////////////////////// // Modal window const openModal = function (e) { e.preventDefault(); modal.classList.remove('hidden'); overlay.classList.remove('hidden'); }; const closeModal = function () { modal.classList.add('hidden'); overlay.classList.add('hidden'); }; btnsOpenModal.forEach(btn => btn.addEventListener('click', openModal)); btnCloseModal.addEventListener('click', closeModal); overlay.addEventListener('click', closeModal); document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && !modal.classList.contains('hidden')) { closeModal(); } }); /////////////////////////////////////// // Button scrolling btnScrollTo.addEventListener('click', function (e) { const s1coords = section1.getBoundingClientRect(); console.log(s1coords); console.log(e.target.getBoundingClientRect()); console.log('Current scroll (X/Y)', window.pageXOffset, window.pageYOffset); console.log( 'height/width viewport', document.documentElement.clientHeight, document.documentElement.clientWidth ); // Scrolling // window.scrollTo( // s1coords.left + window.pageXOffset, // s1coords.top + window.pageYOffset // ); // window.scrollTo({ // left: s1coords.left + window.pageXOffset, // top: s1coords.top + window.pageYOffset, // behavior: 'smooth', // }); section1.scrollIntoView({ behavior: 'smooth' }); }); /////////////////////////////////////// // Page navigation // document.querySelectorAll('.nav__link').forEach(function (el) { // el.addEventListener('click', function (e) { // e.preventDefault(); // const id = this.getAttribute('href'); // console.log(id); // document.querySelector(id).scrollIntoView({ behavior: 'smooth' }); // }); // }); // 1. Add event listener to common parent element // 2. Determine what element originated the event document.querySelector('.nav__links').addEventListener('click', function (e) { e.preventDefault(); // Matching strategy if (e.target.classList.contains('nav__link')) { const id = e.target.getAttribute('href'); document.querySelector(id).scrollIntoView({ behavior: 'smooth' }); } }); /////////////////////////////////////// // Tabbed component tabsContainer.addEventListener('click', function (e) { const clicked = e.target.closest('.operations__tab'); // Guard clause if (!clicked) return; // Remove active classes tabs.forEach(t => t.classList.remove('operations__tab--active')); tabsContent.forEach(c => c.classList.remove('operations__content--active')); // Activate tab clicked.classList.add('operations__tab--active'); // Activate content area document .querySelector(`.operations__content--${clicked.dataset.tab}`) .classList.add('operations__content--active'); }); /////////////////////////////////////// // Menu fade animation const handleHover = function (e) { if (e.target.classList.contains('nav__link')) { const link = e.target; const siblings = link.closest('.nav').querySelectorAll('.nav__link'); const logo = link.closest('.nav').querySelector('img'); siblings.forEach(el => { if (el !== link) el.style.opacity = this; }); logo.style.opacity = this; } }; // Passing "argument" into handler nav.addEventListener('mouseover', handleHover.bind(0.5)); nav.addEventListener('mouseout', handleHover.bind(1)); /////////////////////////////////////// // Sticky navigation: Intersection Observer API const header = document.querySelector('.header'); const navHeight = nav.getBoundingClientRect().height; const stickyNav = function (entries) { const [entry] = entries; // console.log(entry); if (!entry.isIntersecting) nav.classList.add('sticky'); else nav.classList.remove('sticky'); }; const headerObserver = new IntersectionObserver(stickyNav, { root: null, threshold: 0, rootMargin: `-${navHeight}px`, }); headerObserver.observe(header); /////////////////////////////////////// // Reveal sections const allSections = document.querySelectorAll('.section'); const revealSection = function (entries, observer) { entries.forEach(entry => { if (!entry.isIntersecting) return; entry.target.classList.remove('section--hidden'); observer.unobserve(entry.target); }); }; const sectionObserver = new IntersectionObserver(revealSection, { root: null, threshold: 0.15, }); allSections.forEach(function (section) { sectionObserver.observe(section); section.classList.add('section--hidden'); }); // Lazy loading images const imgTargets = document.querySelectorAll('img[data-src]'); const loadImg = function (entries, observer) { const [entry] = entries; if (!entry.isIntersecting) return; // Replace src with data-src entry.target.src = entry.target.dataset.src; entry.target.addEventListener('load', function () { entry.target.classList.remove('lazy-img'); }); observer.unobserve(entry.target); }; const imgObserver = new IntersectionObserver(loadImg, { root: null, threshold: 0, rootMargin: '200px', }); imgTargets.forEach(img => imgObserver.observe(img)); /////////////////////////////////////// // Slider const slider = function () { const slides = document.querySelectorAll('.slide'); const btnLeft = document.querySelector('.slider__btn--left'); const btnRight = document.querySelector('.slider__btn--right'); const dotContainer = document.querySelector('.dots'); let curSlide = 0; const maxSlide = slides.length; // Functions const createDots = function () { slides.forEach(function (_, i) { dotContainer.insertAdjacentHTML( 'beforeend', `` ); }); }; const activateDot = function (slide) { document .querySelectorAll('.dots__dot') .forEach(dot => dot.classList.remove('dots__dot--active')); document .querySelector(`.dots__dot[data-slide="${slide}"]`) .classList.add('dots__dot--active'); }; const goToSlide = function (slide) { slides.forEach( (s, i) => (s.style.transform = `translateX(${100 * (i - slide)}%)`) ); }; // Next slide const nextSlide = function () { if (curSlide === maxSlide - 1) { curSlide = 0; } else { curSlide++; } goToSlide(curSlide); activateDot(curSlide); }; const prevSlide = function () { if (curSlide === 0) { curSlide = maxSlide - 1; } else { curSlide--; } goToSlide(curSlide); activateDot(curSlide); }; const init = function () { goToSlide(0); createDots(); activateDot(0); }; init(); // Event handlers btnRight.addEventListener('click', nextSlide); btnLeft.addEventListener('click', prevSlide); document.addEventListener('keydown', function (e) { if (e.key === 'ArrowLeft') prevSlide(); e.key === 'ArrowRight' && nextSlide(); }); dotContainer.addEventListener('click', function (e) { if (e.target.classList.contains('dots__dot')) { // BUG in v2: This way, we're not keeping track of the current slide when clicking on a slide // const { slide } = e.target.dataset; curSlide = Number(e.target.dataset.slide); goToSlide(curSlide); activateDot(curSlide); } }); }; slider(); /////////////////////////////////////// /////////////////////////////////////// /////////////////////////////////////// /* /////////////////////////////////////// // Selecting, Creating, and Deleting Elements // Selecting elements console.log(document.documentElement); console.log(document.head); console.log(document.body); const header = document.querySelector('.header'); const allSections = document.querySelectorAll('.section'); console.log(allSections); document.getElementById('section--1'); const allButtons = document.getElementsByTagName('button'); console.log(allButtons); console.log(document.getElementsByClassName('btn')); // Creating and inserting elements const message = document.createElement('div'); message.classList.add('cookie-message'); // message.textContent = 'We use cookied for improved functionality and analytics.'; message.innerHTML = 'We use cookied for improved functionality and analytics. '; // header.prepend(message); header.append(message); // header.append(message.cloneNode(true)); // header.before(message); // header.after(message); // Delete elements document .querySelector('.btn--close-cookie') .addEventListener('click', function () { // message.remove(); message.parentElement.removeChild(message); }); /////////////////////////////////////// // Styles, Attributes and Classes // Styles message.style.backgroundColor = '#37383d'; message.style.width = '120%'; console.log(message.style.color); console.log(message.style.backgroundColor); console.log(getComputedStyle(message).color); console.log(getComputedStyle(message).height); message.style.height = Number.parseFloat(getComputedStyle(message).height, 10) + 30 + 'px'; document.documentElement.style.setProperty('--color-primary', 'orangered'); // Attributes const logo = document.querySelector('.nav__logo'); console.log(logo.alt); console.log(logo.className); logo.alt = 'Beautiful minimalist logo'; // Non-standard console.log(logo.designer); console.log(logo.getAttribute('designer')); logo.setAttribute('company', 'Bankist'); console.log(logo.src); console.log(logo.getAttribute('src')); const link = document.querySelector('.nav__link--btn'); console.log(link.href); console.log(link.getAttribute('href')); // Data attributes console.log(logo.dataset.versionNumber); // Classes logo.classList.add('c', 'j'); logo.classList.remove('c', 'j'); logo.classList.toggle('c'); logo.classList.contains('c'); // not includes // Don't use logo.clasName = 'jonas'; /////////////////////////////////////// // Types of Events and Event Handlers const h1 = document.querySelector('h1'); const alertH1 = function (e) { alert('addEventListener: Great! You are reading the heading :D'); }; h1.addEventListener('mouseenter', alertH1); setTimeout(() => h1.removeEventListener('mouseenter', alertH1), 3000); // h1.onmouseenter = function (e) { // alert('onmouseenter: Great! You are reading the heading :D'); // }; /////////////////////////////////////// // Event Propagation in Practice const randomInt = (min, max) => Math.floor(Math.random() * (max - min + 1) + min); const randomColor = () => `rgb(${randomInt(0, 255)},${randomInt(0, 255)},${randomInt(0, 255)})`; document.querySelector('.nav__link').addEventListener('click', function (e) { this.style.backgroundColor = randomColor(); console.log('LINK', e.target, e.currentTarget); console.log(e.currentTarget === this); // Stop propagation // e.stopPropagation(); }); document.querySelector('.nav__links').addEventListener('click', function (e) { this.style.backgroundColor = randomColor(); console.log('CONTAINER', e.target, e.currentTarget); }); document.querySelector('.nav').addEventListener('click', function (e) { this.style.backgroundColor = randomColor(); console.log('NAV', e.target, e.currentTarget); }); /////////////////////////////////////// // DOM Traversing const h1 = document.querySelector('h1'); // Going downwards: child console.log(h1.querySelectorAll('.highlight')); console.log(h1.childNodes); console.log(h1.children); h1.firstElementChild.style.color = 'white'; h1.lastElementChild.style.color = 'orangered'; // Going upwards: parents console.log(h1.parentNode); console.log(h1.parentElement); h1.closest('.header').style.background = 'var(--gradient-secondary)'; h1.closest('h1').style.background = 'var(--gradient-primary)'; // Going sideways: siblings console.log(h1.previousElementSibling); console.log(h1.nextElementSibling); console.log(h1.previousSibling); console.log(h1.nextSibling); console.log(h1.parentElement.children); [...h1.parentElement.children].forEach(function (el) { if (el !== h1) el.style.transform = 'scale(0.5)'; }); /////////////////////////////////////// // Sticky navigation const initialCoords = section1.getBoundingClientRect(); console.log(initialCoords); window.addEventListener('scroll', function () { console.log(window.scrollY); if (window.scrollY > initialCoords.top) nav.classList.add('sticky'); else nav.classList.remove('sticky'); }); /////////////////////////////////////// // Sticky navigation: Intersection Observer API const obsCallback = function (entries, observer) { entries.forEach(entry => { console.log(entry); }); }; const obsOptions = { root: null, threshold: [0, 0.2], }; const observer = new IntersectionObserver(obsCallback, obsOptions); observer.observe(section1); /////////////////////////////////////// // Lifecycle DOM Events document.addEventListener('DOMContentLoaded', function (e) { console.log('HTML parsed and DOM tree built!', e); }); window.addEventListener('load', function (e) { console.log('Page fully loaded', e); }); window.addEventListener('beforeunload', function (e) { e.preventDefault(); console.log(e); e.returnValue = ''; }); */ ================================================ FILE: 13-Advanced-DOM-Bankist/final/style.css ================================================ /* The page is NOT responsive. You can implement responsiveness yourself if you wanna have some fun 😃 */ :root { --color-primary: #5ec576; --color-secondary: #ffcb03; --color-tertiary: #ff585f; --color-primary-darker: #4bbb7d; --color-secondary-darker: #ffbb00; --color-tertiary-darker: #fd424b; --color-primary-opacity: #5ec5763a; --color-secondary-opacity: #ffcd0331; --color-tertiary-opacity: #ff58602d; --gradient-primary: linear-gradient(to top left, #39b385, #9be15d); --gradient-secondary: linear-gradient(to top left, #ffb003, #ffcb03); } * { margin: 0; padding: 0; box-sizing: inherit; } html { font-size: 62.5%; box-sizing: border-box; } body { font-family: 'Poppins', sans-serif; font-weight: 300; color: #444; line-height: 1.9; background-color: #f3f3f3; } /* GENERAL ELEMENTS */ .section { padding: 15rem 3rem; border-top: 1px solid #ddd; transition: transform 1s, opacity 1s; } .section--hidden { opacity: 0; transform: translateY(8rem); } .section__title { max-width: 80rem; margin: 0 auto 8rem auto; } .section__description { font-size: 1.8rem; font-weight: 600; text-transform: uppercase; color: var(--color-primary); margin-bottom: 1rem; } .section__header { font-size: 4rem; line-height: 1.3; font-weight: 500; } .btn { display: inline-block; background-color: var(--color-primary); font-size: 1.6rem; font-family: inherit; font-weight: 500; border: none; padding: 1.25rem 4.5rem; border-radius: 10rem; cursor: pointer; transition: all 0.3s; } .btn:hover { background-color: var(--color-primary-darker); } .btn--text { display: inline-block; background: none; font-size: 1.7rem; font-family: inherit; font-weight: 500; color: var(--color-primary); border: none; border-bottom: 1px solid currentColor; padding-bottom: 2px; cursor: pointer; transition: all 0.3s; } p { color: #666; } /* This is BAD for accessibility! Don't do in the real world! */ button:focus { outline: none; } img { transition: filter 0.5s; } .lazy-img { filter: blur(20px); } /* NAVIGATION */ .nav { display: flex; justify-content: space-between; align-items: center; height: 9rem; width: 100%; padding: 0 6rem; z-index: 100; } /* nav and stickly class at the same time */ .nav.sticky { position: fixed; background-color: rgba(255, 255, 255, 0.95); } .nav__logo { height: 4.5rem; transition: all 0.3s; } .nav__links { display: flex; align-items: center; list-style: none; } .nav__item { margin-left: 4rem; } .nav__link:link, .nav__link:visited { font-size: 1.7rem; font-weight: 400; color: inherit; text-decoration: none; display: block; transition: all 0.3s; } .nav__link--btn:link, .nav__link--btn:visited { padding: 0.8rem 2.5rem; border-radius: 3rem; background-color: var(--color-primary); color: #222; } .nav__link--btn:hover, .nav__link--btn:active { color: inherit; background-color: var(--color-primary-darker); color: #333; } /* HEADER */ .header { padding: 0 3rem; height: 100vh; display: flex; flex-direction: column; align-items: center; } .header__title { flex: 1; max-width: 115rem; display: grid; grid-template-columns: 3fr 2fr; row-gap: 3rem; align-content: center; justify-content: center; align-items: start; justify-items: start; } h1 { font-size: 5.5rem; line-height: 1.35; } h4 { font-size: 2.4rem; font-weight: 500; } .header__img { width: 100%; grid-column: 2 / 3; grid-row: 1 / span 4; transform: translateY(-6rem); } .highlight { position: relative; } .highlight::after { display: block; content: ''; position: absolute; bottom: 0; left: 0; height: 100%; width: 100%; z-index: -1; opacity: 0.7; transform: scale(1.07, 1.05) skewX(-15deg); background-image: var(--gradient-primary); } /* FEATURES */ .features { display: grid; grid-template-columns: 1fr 1fr; gap: 4rem; margin: 0 12rem; } .features__img { width: 100%; } .features__feature { align-self: center; justify-self: center; width: 70%; font-size: 1.5rem; } .features__icon { display: flex; align-items: center; justify-content: center; background-color: var(--color-primary-opacity); height: 5.5rem; width: 5.5rem; border-radius: 50%; margin-bottom: 2rem; } .features__icon svg { height: 2.5rem; width: 2.5rem; fill: var(--color-primary); } .features__header { font-size: 2rem; margin-bottom: 1rem; } /* OPERATIONS */ .operations { max-width: 100rem; margin: 12rem auto 0 auto; background-color: #fff; } .operations__tab-container { display: flex; justify-content: center; } .operations__tab { margin-right: 2.5rem; transform: translateY(-50%); } .operations__tab span { margin-right: 1rem; font-weight: 600; display: inline-block; } .operations__tab--1 { background-color: var(--color-secondary); } .operations__tab--1:hover { background-color: var(--color-secondary-darker); } .operations__tab--3 { background-color: var(--color-tertiary); margin: 0; } .operations__tab--3:hover { background-color: var(--color-tertiary-darker); } .operations__tab--active { transform: translateY(-66%); } .operations__content { display: none; /* JUST PRESENTATIONAL */ font-size: 1.7rem; padding: 2.5rem 7rem 6.5rem 7rem; } .operations__content--active { display: grid; grid-template-columns: 7rem 1fr; column-gap: 3rem; row-gap: 0.5rem; } .operations__header { font-size: 2.25rem; font-weight: 500; align-self: center; } .operations__icon { display: flex; align-items: center; justify-content: center; height: 7rem; width: 7rem; border-radius: 50%; } .operations__icon svg { height: 2.75rem; width: 2.75rem; } .operations__content p { grid-column: 2; } .operations__icon--1 { background-color: var(--color-secondary-opacity); } .operations__icon--2 { background-color: var(--color-primary-opacity); } .operations__icon--3 { background-color: var(--color-tertiary-opacity); } .operations__icon--1 svg { fill: var(--color-secondary-darker); } .operations__icon--2 svg { fill: var(--color-primary); } .operations__icon--3 svg { fill: var(--color-tertiary); } /* SLIDER */ .slider { max-width: 100rem; height: 50rem; margin: 0 auto; position: relative; /* IN THE END */ overflow: hidden; } .slide { position: absolute; top: 0; width: 100%; height: 50rem; display: flex; align-items: center; justify-content: center; /* THIS creates the animation! */ transition: transform 1s; } .slide > img { /* Only for images that have different size than slide */ width: 100%; height: 100%; object-fit: cover; } .slider__btn { position: absolute; top: 50%; z-index: 10; border: none; background: rgba(255, 255, 255, 0.7); font-family: inherit; color: #333; border-radius: 50%; height: 5.5rem; width: 5.5rem; font-size: 3.25rem; cursor: pointer; } .slider__btn--left { left: 6%; transform: translate(-50%, -50%); } .slider__btn--right { right: 6%; transform: translate(50%, -50%); } .dots { position: absolute; bottom: 5%; left: 50%; transform: translateX(-50%); display: flex; } .dots__dot { border: none; background-color: #b9b9b9; opacity: 0.7; height: 1rem; width: 1rem; border-radius: 50%; margin-right: 1.75rem; cursor: pointer; transition: all 0.5s; /* Only necessary when overlying images */ /* box-shadow: 0 0.6rem 1.5rem rgba(0, 0, 0, 0.7); */ } .dots__dot:last-child { margin: 0; } .dots__dot--active { /* background-color: #fff; */ background-color: #888; opacity: 1; } /* TESTIMONIALS */ .testimonial { width: 65%; position: relative; } .testimonial::before { content: '\201C'; position: absolute; top: -5.7rem; left: -6.8rem; line-height: 1; font-size: 20rem; font-family: inherit; color: var(--color-primary); z-index: -1; } .testimonial__header { font-size: 2.25rem; font-weight: 500; margin-bottom: 1.5rem; } .testimonial__text { font-size: 1.7rem; margin-bottom: 3.5rem; color: #666; } .testimonial__author { margin-left: 3rem; font-style: normal; display: grid; grid-template-columns: 6.5rem 1fr; column-gap: 2rem; } .testimonial__photo { grid-row: 1 / span 2; width: 6.5rem; border-radius: 50%; } .testimonial__name { font-size: 1.7rem; font-weight: 500; align-self: end; margin: 0; } .testimonial__location { font-size: 1.5rem; } .section__title--testimonials { margin-bottom: 4rem; } /* SIGNUP */ .section--sign-up { background-color: #37383d; border-top: none; border-bottom: 1px solid #444; text-align: center; padding: 10rem 3rem; } .section--sign-up .section__header { color: #fff; text-align: center; } .section--sign-up .section__title { margin-bottom: 6rem; } .section--sign-up .btn { font-size: 1.9rem; padding: 2rem 5rem; } /* FOOTER */ .footer { padding: 10rem 3rem; background-color: #37383d; } .footer__nav { list-style: none; display: flex; justify-content: center; margin-bottom: 5rem; } .footer__item { margin-right: 4rem; } .footer__link { font-size: 1.6rem; color: #eee; text-decoration: none; } .footer__logo { height: 5rem; display: block; margin: 0 auto; margin-bottom: 5rem; } .footer__copyright { font-size: 1.4rem; color: #aaa; text-align: center; } .footer__copyright .footer__link { font-size: 1.4rem; } /* MODAL WINDOW */ .modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); max-width: 60rem; background-color: #f3f3f3; padding: 5rem 6rem; box-shadow: 0 4rem 6rem rgba(0, 0, 0, 0.3); z-index: 1000; transition: all 0.5s; } .overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); backdrop-filter: blur(4px); z-index: 100; transition: all 0.5s; } .modal__header { font-size: 3.25rem; margin-bottom: 4.5rem; line-height: 1.5; } .modal__form { margin: 0 3rem; display: grid; grid-template-columns: 1fr 2fr; align-items: center; gap: 2.5rem; } .modal__form label { font-size: 1.7rem; font-weight: 500; } .modal__form input { font-size: 1.7rem; padding: 1rem 1.5rem; border: 1px solid #ddd; border-radius: 0.5rem; } .modal__form button { grid-column: 1 / span 2; justify-self: center; margin-top: 1rem; } .btn--close-modal { font-family: inherit; color: inherit; position: absolute; top: 0.5rem; right: 2rem; font-size: 4rem; cursor: pointer; border: none; background: none; } .hidden { visibility: hidden; opacity: 0; } /* COOKIE MESSAGE */ .cookie-message { display: flex; align-items: center; justify-content: space-evenly; width: 100%; background-color: white; color: #bbb; font-size: 1.5rem; font-weight: 400; } ================================================ FILE: 13-Advanced-DOM-Bankist/starter/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 13-Advanced-DOM-Bankist/starter/index.html ================================================ Bankist | When Banking meets Minimalist

When banking meets
minimalist

A simpler banking experience for a simpler life.

Minimalist bank items

Features

Everything you need in a modern bank and more.

Computer
100% digital bank

Lorem ipsum dolor sit amet consectetur adipisicing elit. Unde alias sint quos? Accusantium a fugiat porro reiciendis saepe quibusdam debitis ducimus.

Watch your money grow

Nesciunt quos autem dolorum voluptates cum dolores dicta fuga inventore ab? Nulla incidunt eius numquam sequi iste pariatur quibusdam!

Plant Credit card
Free debit card included

Quasi, fugit in cumque cupiditate reprehenderit debitis animi enim eveniet consequatur odit quam quos possimus assumenda dicta fuga inventore ab.

Operations

Everything as simple as possible, but no simpler.

Tranfser money to anyone, instantly! No fees, no BS.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Buy a home or make your dreams come true, with instant loans.

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

No longer need your account? No problem! Close it instantly.

Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Not sure yet?

Millions of Bankists are already making their lifes simpler.

Best financial decision ever!
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Accusantium quas quisquam non? Quas voluptate nulla minima deleniti optio ullam nesciunt, numquam corporis et asperiores laboriosam sunt, praesentium suscipit blanditiis. Necessitatibus id alias reiciendis, perferendis facere pariatur dolore veniam autem esse non voluptatem saepe provident nihil molestiae.
Aarav Lynn

San Francisco, USA

The last step to becoming a complete minimalist
Quisquam itaque deserunt ullam, quia ea repellendus provident, ducimus neque ipsam modi voluptatibus doloremque, corrupti laborum. Incidunt numquam perferendis veritatis neque repellendus. Lorem, ipsum dolor sit amet consectetur adipisicing elit. Illo deserunt exercitationem deleniti.
Miyah Miles

London, UK

Finally free from old-school banks
Debitis, nihil sit minus suscipit magni aperiam vel tenetur incidunt commodi architecto numquam omnis nulla autem, necessitatibus blanditiis modi similique quidem. Odio aliquam culpa dicta beatae quod maiores ipsa minus consequatur error sunt, deleniti saepe aliquid quos inventore sequi. Necessitatibus id alias reiciendis, perferendis facere.
Francisco Gomes

Lisbon, Portugal

================================================ FILE: 13-Advanced-DOM-Bankist/starter/script.js ================================================ 'use strict'; /////////////////////////////////////// // Modal window const modal = document.querySelector('.modal'); const overlay = document.querySelector('.overlay'); const btnCloseModal = document.querySelector('.btn--close-modal'); const btnsOpenModal = document.querySelectorAll('.btn--show-modal'); const openModal = function () { modal.classList.remove('hidden'); overlay.classList.remove('hidden'); }; const closeModal = function () { modal.classList.add('hidden'); overlay.classList.add('hidden'); }; for (let i = 0; i < btnsOpenModal.length; i++) btnsOpenModal[i].addEventListener('click', openModal); btnCloseModal.addEventListener('click', closeModal); overlay.addEventListener('click', closeModal); document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && !modal.classList.contains('hidden')) { closeModal(); } }); ================================================ FILE: 13-Advanced-DOM-Bankist/starter/style.css ================================================ /* The page is NOT responsive. You can implement responsiveness yourself if you wanna have some fun 😃 */ :root { --color-primary: #5ec576; --color-secondary: #ffcb03; --color-tertiary: #ff585f; --color-primary-darker: #4bbb7d; --color-secondary-darker: #ffbb00; --color-tertiary-darker: #fd424b; --color-primary-opacity: #5ec5763a; --color-secondary-opacity: #ffcd0331; --color-tertiary-opacity: #ff58602d; --gradient-primary: linear-gradient(to top left, #39b385, #9be15d); --gradient-secondary: linear-gradient(to top left, #ffb003, #ffcb03); } * { margin: 0; padding: 0; box-sizing: inherit; } html { font-size: 62.5%; box-sizing: border-box; } body { font-family: 'Poppins', sans-serif; font-weight: 300; color: #444; line-height: 1.9; background-color: #f3f3f3; } /* GENERAL ELEMENTS */ .section { padding: 15rem 3rem; border-top: 1px solid #ddd; transition: transform 1s, opacity 1s; } .section--hidden { opacity: 0; transform: translateY(8rem); } .section__title { max-width: 80rem; margin: 0 auto 8rem auto; } .section__description { font-size: 1.8rem; font-weight: 600; text-transform: uppercase; color: var(--color-primary); margin-bottom: 1rem; } .section__header { font-size: 4rem; line-height: 1.3; font-weight: 500; } .btn { display: inline-block; background-color: var(--color-primary); font-size: 1.6rem; font-family: inherit; font-weight: 500; border: none; padding: 1.25rem 4.5rem; border-radius: 10rem; cursor: pointer; transition: all 0.3s; } .btn:hover { background-color: var(--color-primary-darker); } .btn--text { display: inline-block; background: none; font-size: 1.7rem; font-family: inherit; font-weight: 500; color: var(--color-primary); border: none; border-bottom: 1px solid currentColor; padding-bottom: 2px; cursor: pointer; transition: all 0.3s; } p { color: #666; } /* This is BAD for accessibility! Don't do in the real world! */ button:focus { outline: none; } img { transition: filter 0.5s; } .lazy-img { filter: blur(20px); } /* NAVIGATION */ .nav { display: flex; justify-content: space-between; align-items: center; height: 9rem; width: 100%; padding: 0 6rem; z-index: 100; } /* nav and stickly class at the same time */ .nav.sticky { position: fixed; background-color: rgba(255, 255, 255, 0.95); } .nav__logo { height: 4.5rem; transition: all 0.3s; } .nav__links { display: flex; align-items: center; list-style: none; } .nav__item { margin-left: 4rem; } .nav__link:link, .nav__link:visited { font-size: 1.7rem; font-weight: 400; color: inherit; text-decoration: none; display: block; transition: all 0.3s; } .nav__link--btn:link, .nav__link--btn:visited { padding: 0.8rem 2.5rem; border-radius: 3rem; background-color: var(--color-primary); color: #222; } .nav__link--btn:hover, .nav__link--btn:active { color: inherit; background-color: var(--color-primary-darker); color: #333; } /* HEADER */ .header { padding: 0 3rem; height: 100vh; display: flex; flex-direction: column; align-items: center; } .header__title { flex: 1; max-width: 115rem; display: grid; grid-template-columns: 3fr 2fr; row-gap: 3rem; align-content: center; justify-content: center; align-items: start; justify-items: start; } h1 { font-size: 5.5rem; line-height: 1.35; } h4 { font-size: 2.4rem; font-weight: 500; } .header__img { width: 100%; grid-column: 2 / 3; grid-row: 1 / span 4; transform: translateY(-6rem); } .highlight { position: relative; } .highlight::after { display: block; content: ''; position: absolute; bottom: 0; left: 0; height: 100%; width: 100%; z-index: -1; opacity: 0.7; transform: scale(1.07, 1.05) skewX(-15deg); background-image: var(--gradient-primary); } /* FEATURES */ .features { display: grid; grid-template-columns: 1fr 1fr; gap: 4rem; margin: 0 12rem; } .features__img { width: 100%; } .features__feature { align-self: center; justify-self: center; width: 70%; font-size: 1.5rem; } .features__icon { display: flex; align-items: center; justify-content: center; background-color: var(--color-primary-opacity); height: 5.5rem; width: 5.5rem; border-radius: 50%; margin-bottom: 2rem; } .features__icon svg { height: 2.5rem; width: 2.5rem; fill: var(--color-primary); } .features__header { font-size: 2rem; margin-bottom: 1rem; } /* OPERATIONS */ .operations { max-width: 100rem; margin: 12rem auto 0 auto; background-color: #fff; } .operations__tab-container { display: flex; justify-content: center; } .operations__tab { margin-right: 2.5rem; transform: translateY(-50%); } .operations__tab span { margin-right: 1rem; font-weight: 600; display: inline-block; } .operations__tab--1 { background-color: var(--color-secondary); } .operations__tab--1:hover { background-color: var(--color-secondary-darker); } .operations__tab--3 { background-color: var(--color-tertiary); margin: 0; } .operations__tab--3:hover { background-color: var(--color-tertiary-darker); } .operations__tab--active { transform: translateY(-66%); } .operations__content { display: none; /* JUST PRESENTATIONAL */ font-size: 1.7rem; padding: 2.5rem 7rem 6.5rem 7rem; } .operations__content--active { display: grid; grid-template-columns: 7rem 1fr; column-gap: 3rem; row-gap: 0.5rem; } .operations__header { font-size: 2.25rem; font-weight: 500; align-self: center; } .operations__icon { display: flex; align-items: center; justify-content: center; height: 7rem; width: 7rem; border-radius: 50%; } .operations__icon svg { height: 2.75rem; width: 2.75rem; } .operations__content p { grid-column: 2; } .operations__icon--1 { background-color: var(--color-secondary-opacity); } .operations__icon--2 { background-color: var(--color-primary-opacity); } .operations__icon--3 { background-color: var(--color-tertiary-opacity); } .operations__icon--1 svg { fill: var(--color-secondary-darker); } .operations__icon--2 svg { fill: var(--color-primary); } .operations__icon--3 svg { fill: var(--color-tertiary); } /* SLIDER */ .slider { max-width: 100rem; height: 50rem; margin: 0 auto; position: relative; /* IN THE END */ overflow: hidden; } .slide { position: absolute; top: 0; width: 100%; height: 50rem; display: flex; align-items: center; justify-content: center; /* THIS creates the animation! */ transition: transform 1s; } .slide > img { /* Only for images that have different size than slide */ width: 100%; height: 100%; object-fit: cover; } .slider__btn { position: absolute; top: 50%; z-index: 10; border: none; background: rgba(255, 255, 255, 0.7); font-family: inherit; color: #333; border-radius: 50%; height: 5.5rem; width: 5.5rem; font-size: 3.25rem; cursor: pointer; } .slider__btn--left { left: 6%; transform: translate(-50%, -50%); } .slider__btn--right { right: 6%; transform: translate(50%, -50%); } .dots { position: absolute; bottom: 5%; left: 50%; transform: translateX(-50%); display: flex; } .dots__dot { border: none; background-color: #b9b9b9; opacity: 0.7; height: 1rem; width: 1rem; border-radius: 50%; margin-right: 1.75rem; cursor: pointer; transition: all 0.5s; /* Only necessary when overlying images */ /* box-shadow: 0 0.6rem 1.5rem rgba(0, 0, 0, 0.7); */ } .dots__dot:last-child { margin: 0; } .dots__dot--active { /* background-color: #fff; */ background-color: #888; opacity: 1; } /* TESTIMONIALS */ .testimonial { width: 65%; position: relative; } .testimonial::before { content: '\201C'; position: absolute; top: -5.7rem; left: -6.8rem; line-height: 1; font-size: 20rem; font-family: inherit; color: var(--color-primary); z-index: -1; } .testimonial__header { font-size: 2.25rem; font-weight: 500; margin-bottom: 1.5rem; } .testimonial__text { font-size: 1.7rem; margin-bottom: 3.5rem; color: #666; } .testimonial__author { margin-left: 3rem; font-style: normal; display: grid; grid-template-columns: 6.5rem 1fr; column-gap: 2rem; } .testimonial__photo { grid-row: 1 / span 2; width: 6.5rem; border-radius: 50%; } .testimonial__name { font-size: 1.7rem; font-weight: 500; align-self: end; margin: 0; } .testimonial__location { font-size: 1.5rem; } .section__title--testimonials { margin-bottom: 4rem; } /* SIGNUP */ .section--sign-up { background-color: #37383d; border-top: none; border-bottom: 1px solid #444; text-align: center; padding: 10rem 3rem; } .section--sign-up .section__header { color: #fff; text-align: center; } .section--sign-up .section__title { margin-bottom: 6rem; } .section--sign-up .btn { font-size: 1.9rem; padding: 2rem 5rem; } /* FOOTER */ .footer { padding: 10rem 3rem; background-color: #37383d; } .footer__nav { list-style: none; display: flex; justify-content: center; margin-bottom: 5rem; } .footer__item { margin-right: 4rem; } .footer__link { font-size: 1.6rem; color: #eee; text-decoration: none; } .footer__logo { height: 5rem; display: block; margin: 0 auto; margin-bottom: 5rem; } .footer__copyright { font-size: 1.4rem; color: #aaa; text-align: center; } .footer__copyright .footer__link { font-size: 1.4rem; } /* MODAL WINDOW */ .modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); max-width: 60rem; background-color: #f3f3f3; padding: 5rem 6rem; box-shadow: 0 4rem 6rem rgba(0, 0, 0, 0.3); z-index: 1000; transition: all 0.5s; } .overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); backdrop-filter: blur(4px); z-index: 100; transition: all 0.5s; } .modal__header { font-size: 3.25rem; margin-bottom: 4.5rem; line-height: 1.5; } .modal__form { margin: 0 3rem; display: grid; grid-template-columns: 1fr 2fr; align-items: center; gap: 2.5rem; } .modal__form label { font-size: 1.7rem; font-weight: 500; } .modal__form input { font-size: 1.7rem; padding: 1rem 1.5rem; border: 1px solid #ddd; border-radius: 0.5rem; } .modal__form button { grid-column: 1 / span 2; justify-self: center; margin-top: 1rem; } .btn--close-modal { font-family: inherit; color: inherit; position: absolute; top: 0.5rem; right: 2rem; font-size: 4rem; cursor: pointer; border: none; background: none; } .hidden { visibility: hidden; opacity: 0; } /* COOKIE MESSAGE */ .cookie-message { display: flex; align-items: center; justify-content: space-evenly; width: 100%; background-color: white; color: #bbb; font-size: 1.5rem; font-weight: 400; } ================================================ FILE: 14-OOP/final/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 14-OOP/final/index.html ================================================ Object Oriented Programming (OOP) With JavaScript

Object Oriented Programming (OOP) With JavaScript

================================================ FILE: 14-OOP/final/script.js ================================================ 'use strict'; /* /////////////////////////////////////// // Constructor Functions and the new Operator const Person = function (firstName, birthYear) { // Instance properties this.firstName = firstName; this.birthYear = birthYear; // Never to this! // this.calcAge = function () { // console.log(2037 - this.birthYear); // }; }; const jonas = new Person('Jonas', 1991); console.log(jonas); // 1. New {} is created // 2. function is called, this = {} // 3. {} linked to prototype // 4. function automatically return {} const matilda = new Person('Matilda', 2017); const jack = new Person('Jack', 1975); console.log(jonas instanceof Person); Person.hey = function () { console.log('Hey there 👋'); console.log(this); }; Person.hey(); /////////////////////////////////////// // Prototypes console.log(Person.prototype); Person.prototype.calcAge = function () { console.log(2037 - this.birthYear); }; jonas.calcAge(); matilda.calcAge(); console.log(jonas.__proto__); console.log(jonas.__proto__ === Person.prototype); console.log(Person.prototype.isPrototypeOf(jonas)); console.log(Person.prototype.isPrototypeOf(matilda)); console.log(Person.prototype.isPrototypeOf(Person)); // .prototyeOfLinkedObjects Person.prototype.species = 'Homo Sapiens'; console.log(jonas.species, matilda.species); console.log(jonas.hasOwnProperty('firstName')); console.log(jonas.hasOwnProperty('species')); /////////////////////////////////////// // Prototypal Inheritance on Built-In Objects console.log(jonas.__proto__); // Object.prototype (top of prototype chain) console.log(jonas.__proto__.__proto__); console.log(jonas.__proto__.__proto__.__proto__); console.dir(Person.prototype.constructor); const arr = [3, 6, 6, 5, 6, 9, 9]; // new Array === [] console.log(arr.__proto__); console.log(arr.__proto__ === Array.prototype); console.log(arr.__proto__.__proto__); Array.prototype.unique = function () { return [...new Set(this)]; }; console.log(arr.unique()); const h1 = document.querySelector('h1'); console.dir(x => x + 1); */ /////////////////////////////////////// // Coding Challenge #1 /* 1. Use a constructor function to implement a Car. A car has a make and a speed property. The speed property is the current speed of the car in km/h; 2. Implement an 'accelerate' method that will increase the car's speed by 10, and log the new speed to the console; 3. Implement a 'brake' method that will decrease the car's speed by 5, and log the new speed to the console; 4. Create 2 car objects and experiment with calling 'accelerate' and 'brake' multiple times on each of them. DATA CAR 1: 'BMW' going at 120 km/h DATA CAR 2: 'Mercedes' going at 95 km/h GOOD LUCK 😀 */ /* const Car = function (make, speed) { this.make = make; this.speed = speed; }; Car.prototype.accelerate = function () { this.speed += 10; console.log(`${this.make} is going at ${this.speed} km/h`); }; Car.prototype.brake = function () { this.speed -= 5; console.log(`${this.make} is going at ${this.speed} km/h`); }; const bmw = new Car('BMW', 120); const mercedes = new Car('Mercedes', 95); bmw.accelerate(); bmw.accelerate(); bmw.brake(); bmw.accelerate(); /////////////////////////////////////// // ES6 Classes // Class expression // const PersonCl = class {} // Class declaration class PersonCl { constructor(fullName, birthYear) { this.fullName = fullName; this.birthYear = birthYear; } // Instance methods // Methods will be added to .prototype property calcAge() { console.log(2037 - this.birthYear); } greet() { console.log(`Hey ${this.fullName}`); } get age() { return 2037 - this.birthYear; } // Set a property that already exists set fullName(name) { if (name.includes(' ')) this._fullName = name; else alert(`${name} is not a full name!`); } get fullName() { return this._fullName; } // Static method static hey() { console.log('Hey there 👋'); console.log(this); } } const jessica = new PersonCl('Jessica Davis', 1996); console.log(jessica); jessica.calcAge(); console.log(jessica.age); console.log(jessica.__proto__ === PersonCl.prototype); // PersonCl.prototype.greet = function () { // console.log(`Hey ${this.firstName}`); // }; jessica.greet(); // 1. Classes are NOT hoisted // 2. Classes are first-class citizens // 3. Classes are executed in strict mode const walter = new PersonCl('Walter White', 1965); // PersonCl.hey(); /////////////////////////////////////// // Setters and Getters const account = { owner: 'Jonas', movements: [200, 530, 120, 300], get latest() { return this.movements.slice(-1).pop(); }, set latest(mov) { this.movements.push(mov); }, }; console.log(account.latest); account.latest = 50; console.log(account.movements); /////////////////////////////////////// // Object.create const PersonProto = { calcAge() { console.log(2037 - this.birthYear); }, init(firstName, birthYear) { this.firstName = firstName; this.birthYear = birthYear; }, }; const steven = Object.create(PersonProto); console.log(steven); steven.name = 'Steven'; steven.birthYear = 2002; steven.calcAge(); console.log(steven.__proto__ === PersonProto); const sarah = Object.create(PersonProto); sarah.init('Sarah', 1979); sarah.calcAge(); */ /////////////////////////////////////// // Coding Challenge #2 /* 1. Re-create challenge 1, but this time using an ES6 class; 2. Add a getter called 'speedUS' which returns the current speed in mi/h (divide by 1.6); 3. Add a setter called 'speedUS' which sets the current speed in mi/h (but converts it to km/h before storing the value, by multiplying the input by 1.6); 4. Create a new car and experiment with the accelerate and brake methods, and with the getter and setter. DATA CAR 1: 'Ford' going at 120 km/h GOOD LUCK 😀 */ /* class CarCl { constructor(make, speed) { this.make = make; this.speed = speed; } accelerate() { this.speed += 10; console.log(`${this.make} is going at ${this.speed} km/h`); } brake() { this.speed -= 5; console.log(`${this.make} is going at ${this.speed} km/h`); } get speedUS() { return this.speed / 1.6; } set speedUS(speed) { this.speed = speed * 1.6; } } const ford = new CarCl('Ford', 120); console.log(ford.speedUS); ford.accelerate(); ford.accelerate(); ford.brake(); ford.speedUS = 50; console.log(ford); /////////////////////////////////////// // Inheritance Between "Classes": Constructor Functions const Person = function (firstName, birthYear) { this.firstName = firstName; this.birthYear = birthYear; }; Person.prototype.calcAge = function () { console.log(2037 - this.birthYear); }; const Student = function (firstName, birthYear, course) { Person.call(this, firstName, birthYear); this.course = course; }; // Linking prototypes Student.prototype = Object.create(Person.prototype); Student.prototype.introduce = function () { console.log(`My name is ${this.firstName} and I study ${this.course}`); }; const mike = new Student('Mike', 2020, 'Computer Science'); mike.introduce(); mike.calcAge(); console.log(mike.__proto__); console.log(mike.__proto__.__proto__); console.log(mike instanceof Student); console.log(mike instanceof Person); console.log(mike instanceof Object); Student.prototype.constructor = Student; console.dir(Student.prototype.constructor); */ /////////////////////////////////////// // Coding Challenge #3 /* 1. Use a constructor function to implement an Electric Car (called EV) as a CHILD "class" of Car. Besides a make and current speed, the EV also has the current battery charge in % ('charge' property); 2. Implement a 'chargeBattery' method which takes an argument 'chargeTo' and sets the battery charge to 'chargeTo'; 3. Implement an 'accelerate' method that will increase the car's speed by 20, and decrease the charge by 1%. Then log a message like this: 'Tesla going at 140 km/h, with a charge of 22%'; 4. Create an electric car object and experiment with calling 'accelerate', 'brake' and 'chargeBattery' (charge to 90%). Notice what happens when you 'accelerate'! HINT: Review the definiton of polymorphism 😉 DATA CAR 1: 'Tesla' going at 120 km/h, with a charge of 23% GOOD LUCK 😀 */ /* const Car = function (make, speed) { this.make = make; this.speed = speed; }; Car.prototype.accelerate = function () { this.speed += 10; console.log(`${this.make} is going at ${this.speed} km/h`); }; Car.prototype.brake = function () { this.speed -= 5; console.log(`${this.make} is going at ${this.speed} km/h`); }; const EV = function (make, speed, charge) { Car.call(this, make, speed); this.charge = charge; }; // Link the prototypes EV.prototype = Object.create(Car.prototype); EV.prototype.chargeBattery = function (chargeTo) { this.charge = chargeTo; }; EV.prototype.accelerate = function () { this.speed += 20; this.charge--; console.log( `${this.make} is going at ${this.speed} km/h, with a charge of ${this.charge}` ); }; const tesla = new EV('Tesla', 120, 23); tesla.chargeBattery(90); console.log(tesla); tesla.brake(); tesla.accelerate(); /////////////////////////////////////// // Inheritance Between "Classes": ES6 Classes class PersonCl { constructor(fullName, birthYear) { this.fullName = fullName; this.birthYear = birthYear; } // Instance methods calcAge() { console.log(2037 - this.birthYear); } greet() { console.log(`Hey ${this.fullName}`); } get age() { return 2037 - this.birthYear; } set fullName(name) { if (name.includes(' ')) this._fullName = name; else alert(`${name} is not a full name!`); } get fullName() { return this._fullName; } // Static method static hey() { console.log('Hey there 👋'); } } class StudentCl extends PersonCl { constructor(fullName, birthYear, course) { // Always needs to happen first! super(fullName, birthYear); this.course = course; } introduce() { console.log(`My name is ${this.fullName} and I study ${this.course}`); } calcAge() { console.log( `I'm ${ 2037 - this.birthYear } years old, but as a student I feel more like ${ 2037 - this.birthYear + 10 }` ); } } const martha = new StudentCl('Martha Jones', 2012, 'Computer Science'); martha.introduce(); martha.calcAge(); /////////////////////////////////////// // Inheritance Between "Classes": Object.create const PersonProto = { calcAge() { console.log(2037 - this.birthYear); }, init(firstName, birthYear) { this.firstName = firstName; this.birthYear = birthYear; }, }; const steven = Object.create(PersonProto); const StudentProto = Object.create(PersonProto); StudentProto.init = function (firstName, birthYear, course) { PersonProto.init.call(this, firstName, birthYear); this.course = course; }; StudentProto.introduce = function () { // BUG in video: // console.log(`My name is ${this.fullName} and I study ${this.course}`); // FIX: console.log(`My name is ${this.firstName} and I study ${this.course}`); }; const jay = Object.create(StudentProto); jay.init('Jay', 2010, 'Computer Science'); jay.introduce(); jay.calcAge(); /////////////////////////////////////// // Another Class Example class Account { constructor(owner, currency, pin) { this.owner = owner; this.currency = currency; this.pin = pin; this.movements = []; this.locale = navigator.language; console.log(`Thanks for opening an account, ${owner}`); } // Public interface deposit(val) { this.movements.push(val); } withdraw(val) { this.deposit(-val); } approveLoan(val) { return true; } requestLoan(val) { if (this.approveLoan(val)) { this.deposit(val); console.log(`Loan approved`); } } } const acc1 = new Account('Jonas', 'EUR', 1111); // acc1.movements.push(250); // acc1.movements.push(-140); acc1.deposit(250); acc1.withdraw(140); acc1.approveLoan(1000); acc1.requestLoan(1000); console.log(acc1); console.log(acc1.pin); /////////////////////////////////////// // Encapsulation: Private Class Fields and Methods // 1) Public fields // 2) Private fields // 3) Public methods // 4) Private methods // STATIC version of these 4 class Account { locale = navigator.language; bank = 'Bankist'; #movements = []; #pin; constructor(owner, currency, pin) { this.owner = owner; this.currency = currency; this.#pin = pin; // this.movements = []; // this.locale = navigator.language; console.log(`Thanks for opening an account, ${owner}`); } // Public interface (API) getMovements() { return this.#movements; // Not chaninable } deposit(val) { this.#movements.push(val); return this; } withdraw(val) { this.deposit(-val); return this; } #approveLoan(val) { // Fake method return true; } requestLoan(val) { if (this.#approveLoan(val)) { this.deposit(val); console.log(`Loan approved`); } return this; } } const acc1 = new Account('Jonas', 'EUR', 1111); // acc1.deposit(300); // acc1.withdraw(100); const movements = acc1 .deposit(300) .withdraw(100) .withdraw(50) .requestLoan(25000) .withdraw(4000) .getMovements(); console.log(acc1); // console.log(acc1.#movements); // Account.#test(); console.log(movements); /////////////////////////////////////// // Coding Challenge #4 /* 1. Re-create challenge #3, but this time using ES6 classes: create an 'EVCl' child class of the 'CarCl' class 2. Make the 'charge' property private; 3. Implement the ability to chain the 'accelerate' and 'chargeBattery' methods of this class, and also update the 'brake' method in the 'CarCl' class. They experiment with chining! DATA CAR 1: 'Rivian' going at 120 km/h, with a charge of 23% GOOD LUCK 😀 */ /* class CarCl { constructor(make, speed) { this.make = make; this.speed = speed; } accelerate() { this.speed += 10; console.log(`${this.make} is going at ${this.speed} km/h`); } brake() { this.speed -= 5; console.log(`${this.make} is going at ${this.speed} km/h`); return this; } get speedUS() { return this.speed / 1.6; } set speedUS(speed) { this.speed = speed * 1.6; } } class EVCl extends CarCl { #charge; constructor(make, speed, charge) { super(make, speed); this.#charge = charge; } chargeBattery(chargeTo) { this.#charge = chargeTo; return this; } accelerate() { this.speed += 20; this.#charge--; console.log( `${this.make} is going at ${this.speed} km/h, with a charge of ${ this.#charge }` ); return this; } } const rivian = new EVCl('Rivian', 120, 23); console.log(rivian); // console.log(rivian.#charge); rivian .accelerate() .accelerate() .accelerate() .brake() .chargeBattery(50) .accelerate(); console.log(rivian.speedUS); */ ================================================ FILE: 14-OOP/starter/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 14-OOP/starter/index.html ================================================ Object Oriented Programming (OOP) With JavaScript

Object Oriented Programming (OOP) With JavaScript

================================================ FILE: 14-OOP/starter/script.js ================================================ 'use strict'; ================================================ FILE: 15-Mapty/final/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 15-Mapty/final/index.html ================================================ mapty // Map your workouts
================================================ FILE: 15-Mapty/final/other.js ================================================ const firstName = 'Jonas'; console.log(months); ================================================ FILE: 15-Mapty/final/script.js ================================================ 'use strict'; class Workout { date = new Date(); id = (Date.now() + '').slice(-10); clicks = 0; constructor(coords, distance, duration) { // this.date = ... // this.id = ... this.coords = coords; // [lat, lng] this.distance = distance; // in km this.duration = duration; // in min } _setDescription() { // prettier-ignore const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; this.description = `${this.type[0].toUpperCase()}${this.type.slice(1)} on ${ months[this.date.getMonth()] } ${this.date.getDate()}`; } click() { this.clicks++; } } class Running extends Workout { type = 'running'; constructor(coords, distance, duration, cadence) { super(coords, distance, duration); this.cadence = cadence; this.calcPace(); this._setDescription(); } calcPace() { // min/km this.pace = this.duration / this.distance; return this.pace; } } class Cycling extends Workout { type = 'cycling'; constructor(coords, distance, duration, elevationGain) { super(coords, distance, duration); this.elevationGain = elevationGain; // this.type = 'cycling'; this.calcSpeed(); this._setDescription(); } calcSpeed() { // km/h this.speed = this.distance / (this.duration / 60); return this.speed; } } // const run1 = new Running([39, -12], 5.2, 24, 178); // const cycling1 = new Cycling([39, -12], 27, 95, 523); // console.log(run1, cycling1); /////////////////////////////////////// // APPLICATION ARCHITECTURE const form = document.querySelector('.form'); const containerWorkouts = document.querySelector('.workouts'); const inputType = document.querySelector('.form__input--type'); const inputDistance = document.querySelector('.form__input--distance'); const inputDuration = document.querySelector('.form__input--duration'); const inputCadence = document.querySelector('.form__input--cadence'); const inputElevation = document.querySelector('.form__input--elevation'); class App { #map; #mapZoomLevel = 13; #mapEvent; #workouts = []; constructor() { // Get user's position this._getPosition(); // Get data from local storage this._getLocalStorage(); // Attach event handlers form.addEventListener('submit', this._newWorkout.bind(this)); inputType.addEventListener('change', this._toggleElevationField); containerWorkouts.addEventListener('click', this._moveToPopup.bind(this)); } _getPosition() { if (navigator.geolocation) navigator.geolocation.getCurrentPosition( this._loadMap.bind(this), function () { alert('Could not get your position'); } ); } _loadMap(position) { const { latitude } = position.coords; const { longitude } = position.coords; // console.log(`https://www.google.pt/maps/@${latitude},${longitude}`); const coords = [latitude, longitude]; this.#map = L.map('map').setView(coords, this.#mapZoomLevel); L.tileLayer('https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors', }).addTo(this.#map); // Handling clicks on map this.#map.on('click', this._showForm.bind(this)); this.#workouts.forEach(work => { this._renderWorkoutMarker(work); }); } _showForm(mapE) { this.#mapEvent = mapE; form.classList.remove('hidden'); inputDistance.focus(); } _hideForm() { // Empty inputs inputDistance.value = inputDuration.value = inputCadence.value = inputElevation.value = ''; form.style.display = 'none'; form.classList.add('hidden'); setTimeout(() => (form.style.display = 'grid'), 1000); } _toggleElevationField() { inputElevation.closest('.form__row').classList.toggle('form__row--hidden'); inputCadence.closest('.form__row').classList.toggle('form__row--hidden'); } _newWorkout(e) { const validInputs = (...inputs) => inputs.every(inp => Number.isFinite(inp)); const allPositive = (...inputs) => inputs.every(inp => inp > 0); e.preventDefault(); // Get data from form const type = inputType.value; const distance = +inputDistance.value; const duration = +inputDuration.value; const { lat, lng } = this.#mapEvent.latlng; let workout; // If workout running, create running object if (type === 'running') { const cadence = +inputCadence.value; // Check if data is valid if ( // !Number.isFinite(distance) || // !Number.isFinite(duration) || // !Number.isFinite(cadence) !validInputs(distance, duration, cadence) || !allPositive(distance, duration, cadence) ) return alert('Inputs have to be positive numbers!'); workout = new Running([lat, lng], distance, duration, cadence); } // If workout cycling, create cycling object if (type === 'cycling') { const elevation = +inputElevation.value; if ( !validInputs(distance, duration, elevation) || !allPositive(distance, duration) ) return alert('Inputs have to be positive numbers!'); workout = new Cycling([lat, lng], distance, duration, elevation); } // Add new object to workout array this.#workouts.push(workout); // Render workout on map as marker this._renderWorkoutMarker(workout); // Render workout on list this._renderWorkout(workout); // Hide form + clear input fields this._hideForm(); // Set local storage to all workouts this._setLocalStorage(); } _renderWorkoutMarker(workout) { L.marker(workout.coords) .addTo(this.#map) .bindPopup( L.popup({ maxWidth: 250, minWidth: 100, autoClose: false, closeOnClick: false, className: `${workout.type}-popup`, }) ) .setPopupContent( `${workout.type === 'running' ? '🏃‍♂️' : '🚴‍♀️'} ${workout.description}` ) .openPopup(); } _renderWorkout(workout) { let html = `
  • ${workout.description}

    ${ workout.type === 'running' ? '🏃‍♂️' : '🚴‍♀️' } ${workout.distance} km
    ${workout.duration} min
    `; if (workout.type === 'running') html += `
    ⚡️ ${workout.pace.toFixed(1)} min/km
    🦶🏼 ${workout.cadence} spm
  • `; if (workout.type === 'cycling') html += `
    ⚡️ ${workout.speed.toFixed(1)} km/h
    ${workout.elevationGain} m
    `; form.insertAdjacentHTML('afterend', html); } _moveToPopup(e) { // BUGFIX: When we click on a workout before the map has loaded, we get an error. But there is an easy fix: if (!this.#map) return; const workoutEl = e.target.closest('.workout'); if (!workoutEl) return; const workout = this.#workouts.find( work => work.id === workoutEl.dataset.id ); this.#map.setView(workout.coords, this.#mapZoomLevel, { animate: true, pan: { duration: 1, }, }); // using the public interface // workout.click(); } _setLocalStorage() { localStorage.setItem('workouts', JSON.stringify(this.#workouts)); } _getLocalStorage() { const data = JSON.parse(localStorage.getItem('workouts')); if (!data) return; this.#workouts = data; this.#workouts.forEach(work => { this._renderWorkout(work); }); } reset() { localStorage.removeItem('workouts'); location.reload(); } } const app = new App(); ================================================ FILE: 15-Mapty/final/style.css ================================================ :root { --color-brand--1: #ffb545; --color-brand--2: #00c46a; --color-dark--1: #2d3439; --color-dark--2: #42484d; --color-light--1: #aaa; --color-light--2: #ececec; --color-light--3: rgb(214, 222, 224); } * { margin: 0; padding: 0; box-sizing: inherit; } html { font-size: 62.5%; box-sizing: border-box; } body { font-family: 'Manrope', sans-serif; color: var(--color-light--2); font-weight: 400; line-height: 1.6; height: 100vh; overscroll-behavior-y: none; background-color: #fff; padding: 2.5rem; display: flex; } /* GENERAL */ a:link, a:visited { color: var(--color-brand--1); } /* SIDEBAR */ .sidebar { flex-basis: 50rem; background-color: var(--color-dark--1); padding: 3rem 5rem 4rem 5rem; display: flex; flex-direction: column; } .logo { height: 5.2rem; align-self: center; margin-bottom: 4rem; } .workouts { list-style: none; height: 77vh; overflow-y: scroll; overflow-x: hidden; } .workouts::-webkit-scrollbar { width: 0; } .workout { background-color: var(--color-dark--2); border-radius: 5px; padding: 1.5rem 2.25rem; margin-bottom: 1.75rem; cursor: pointer; display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 0.75rem 1.5rem; } .workout--running { border-left: 5px solid var(--color-brand--2); } .workout--cycling { border-left: 5px solid var(--color-brand--1); } .workout__title { font-size: 1.7rem; font-weight: 600; grid-column: 1 / -1; } .workout__details { display: flex; align-items: baseline; } .workout__icon { font-size: 1.8rem; margin-right: 0.2rem; height: 0.28rem; } .workout__value { font-size: 1.5rem; margin-right: 0.5rem; } .workout__unit { font-size: 1.1rem; color: var(--color-light--1); text-transform: uppercase; font-weight: 800; } .form { background-color: var(--color-dark--2); border-radius: 5px; padding: 1.5rem 2.75rem; margin-bottom: 1.75rem; display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem 2.5rem; /* Match height and activity boxes */ height: 9.25rem; transition: all 0.5s, transform 1ms; } .form.hidden { transform: translateY(-30rem); height: 0; padding: 0 2.25rem; margin-bottom: 0; opacity: 0; } .form__row { display: flex; align-items: center; } .form__row--hidden { display: none; } .form__label { flex: 0 0 50%; font-size: 1.5rem; font-weight: 600; } .form__input { width: 100%; padding: 0.3rem 1.1rem; font-family: inherit; font-size: 1.4rem; border: none; border-radius: 3px; background-color: var(--color-light--3); transition: all 0.2s; } .form__input:focus { outline: none; background-color: #fff; } .form__btn { display: none; } .copyright { margin-top: auto; font-size: 1.3rem; text-align: center; color: var(--color-light--1); } .twitter-link:link, .twitter-link:visited { color: var(--color-light--1); transition: all 0.2s; } .twitter-link:hover, .twitter-link:active { color: var(--color-light--2); } /* MAP */ #map { flex: 1; height: 100%; background-color: var(--color-light--1); } /* Popup width is defined in JS using options */ .leaflet-popup .leaflet-popup-content-wrapper { background-color: var(--color-dark--1); color: var(--color-light--2); border-radius: 5px; padding-right: 0.6rem; } .leaflet-popup .leaflet-popup-content { font-size: 1.5rem; } .leaflet-popup .leaflet-popup-tip { background-color: var(--color-dark--1); } .running-popup .leaflet-popup-content-wrapper { border-left: 5px solid var(--color-brand--2); } .cycling-popup .leaflet-popup-content-wrapper { border-left: 5px solid var(--color-brand--1); } ================================================ FILE: 15-Mapty/starter/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 15-Mapty/starter/index.html ================================================ mapty // Map your workouts
    ================================================ FILE: 15-Mapty/starter/script.js ================================================ 'use strict'; // prettier-ignore const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; const form = document.querySelector('.form'); const containerWorkouts = document.querySelector('.workouts'); const inputType = document.querySelector('.form__input--type'); const inputDistance = document.querySelector('.form__input--distance'); const inputDuration = document.querySelector('.form__input--duration'); const inputCadence = document.querySelector('.form__input--cadence'); const inputElevation = document.querySelector('.form__input--elevation'); ================================================ FILE: 15-Mapty/starter/style.css ================================================ :root { --color-brand--1: #ffb545; --color-brand--2: #00c46a; --color-dark--1: #2d3439; --color-dark--2: #42484d; --color-light--1: #aaa; --color-light--2: #ececec; --color-light--3: rgb(214, 222, 224); } * { margin: 0; padding: 0; box-sizing: inherit; } html { font-size: 62.5%; box-sizing: border-box; } body { font-family: 'Manrope', sans-serif; color: var(--color-light--2); font-weight: 400; line-height: 1.6; height: 100vh; overscroll-behavior-y: none; background-color: #fff; padding: 2.5rem; display: flex; } /* GENERAL */ a:link, a:visited { color: var(--color-brand--1); } /* SIDEBAR */ .sidebar { flex-basis: 50rem; background-color: var(--color-dark--1); padding: 3rem 5rem 4rem 5rem; display: flex; flex-direction: column; } .logo { height: 5.2rem; align-self: center; margin-bottom: 4rem; } .workouts { list-style: none; height: 77vh; overflow-y: scroll; overflow-x: hidden; } .workouts::-webkit-scrollbar { width: 0; } .workout { background-color: var(--color-dark--2); border-radius: 5px; padding: 1.5rem 2.25rem; margin-bottom: 1.75rem; cursor: pointer; display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 0.75rem 1.5rem; } .workout--running { border-left: 5px solid var(--color-brand--2); } .workout--cycling { border-left: 5px solid var(--color-brand--1); } .workout__title { font-size: 1.7rem; font-weight: 600; grid-column: 1 / -1; } .workout__details { display: flex; align-items: baseline; } .workout__icon { font-size: 1.8rem; margin-right: 0.2rem; height: 0.28rem; } .workout__value { font-size: 1.5rem; margin-right: 0.5rem; } .workout__unit { font-size: 1.1rem; color: var(--color-light--1); text-transform: uppercase; font-weight: 800; } .form { background-color: var(--color-dark--2); border-radius: 5px; padding: 1.5rem 2.75rem; margin-bottom: 1.75rem; display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem 2.5rem; /* Match height and activity boxes */ height: 9.25rem; transition: all 0.5s, transform 1ms; } .form.hidden { transform: translateY(-30rem); height: 0; padding: 0 2.25rem; margin-bottom: 0; opacity: 0; } .form__row { display: flex; align-items: center; } .form__row--hidden { display: none; } .form__label { flex: 0 0 50%; font-size: 1.5rem; font-weight: 600; } .form__input { width: 100%; padding: 0.3rem 1.1rem; font-family: inherit; font-size: 1.4rem; border: none; border-radius: 3px; background-color: var(--color-light--3); transition: all 0.2s; } .form__input:focus { outline: none; background-color: #fff; } .form__btn { display: none; } .copyright { margin-top: auto; font-size: 1.3rem; text-align: center; color: var(--color-light--1); } .twitter-link:link, .twitter-link:visited { color: var(--color-light--1); transition: all 0.2s; } .twitter-link:hover, .twitter-link:active { color: var(--color-light--2); } /* MAP */ #map { flex: 1; height: 100%; background-color: var(--color-light--1); } /* Popup width is defined in JS using options */ .leaflet-popup .leaflet-popup-content-wrapper { background-color: var(--color-dark--1); color: var(--color-light--2); border-radius: 5px; padding-right: 0.6rem; } .leaflet-popup .leaflet-popup-content { font-size: 1.5rem; } .leaflet-popup .leaflet-popup-tip { background-color: var(--color-dark--1); } .running-popup .leaflet-popup-content-wrapper { border-left: 5px solid var(--color-brand--2); } .cycling-popup .leaflet-popup-content-wrapper { border-left: 5px solid var(--color-brand--1); } ================================================ FILE: 16-Asynchronous/final/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 16-Asynchronous/final/index.html ================================================ Asynchronous JavaScript
    ================================================ FILE: 16-Asynchronous/final/script.js ================================================ 'use strict'; const btn = document.querySelector('.btn-country'); const countriesContainer = document.querySelector('.countries'); const renderCountry = function (data, className = '') { const html = `

    ${data.name}

    ${data.region}

    👫${( +data.population / 1000000 ).toFixed(1)} people

    🗣️${data.languages[0].name}

    💰${data.currencies[0].name}

    `; countriesContainer.insertAdjacentHTML('beforeend', html); countriesContainer.style.opacity = 1; }; const renderError = function (msg) { countriesContainer.insertAdjacentText('beforeend', msg); countriesContainer.style.opacity = 1; }; const getJSON = function (url, errorMsg = 'Something went wrong') { return fetch(url).then(response => { if (!response.ok) throw new Error(`${errorMsg} (${response.status})`); return response.json(); }); }; /* /////////////////////////////////////// // Our First AJAX Call: XMLHttpRequest const getCountryData = function (country) { const request = new XMLHttpRequest(); request.open('GET', `https://restcountries.com/v2/name/${country}`); request.send(); request.addEventListener('load', function () { const [data] = JSON.parse(this.responseText); console.log(data); const html = `

    ${data.name}

    ${data.region}

    👫${( +data.population / 1000000 ).toFixed(1)} people

    🗣️${data.languages[0].name}

    💰${data.currencies[0].name}

    `; countriesContainer.insertAdjacentHTML('beforeend', html); countriesContainer.style.opacity = 1; }); }; getCountryData('portugal'); getCountryData('usa'); getCountryData('germany'); */ /////////////////////////////////////// // Welcome to Callback Hell /* const getCountryAndNeighbour = function (country) { // AJAX call country 1 const request = new XMLHttpRequest(); request.open('GET', `https://restcountries.com/v2/name/${country}`); request.send(); request.addEventListener('load', function () { const [data] = JSON.parse(this.responseText); console.log(data); // Render country 1 renderCountry(data); // Get neighbour country (2) const [neighbour] = data.borders; if (!neighbour) return; // AJAX call country 2 const request2 = new XMLHttpRequest(); request2.open('GET', `https://restcountries.eu/rest/v2/alpha/${neighbour}`); request2.send(); request2.addEventListener('load', function () { const data2 = JSON.parse(this.responseText); console.log(data2); renderCountry(data2, 'neighbour'); }); }); }; // getCountryAndNeighbour('portugal'); getCountryAndNeighbour('usa'); setTimeout(() => { console.log('1 second passed'); setTimeout(() => { console.log('2 seconds passed'); setTimeout(() => { console.log('3 second passed'); setTimeout(() => { console.log('4 second passed'); }, 1000); }, 1000); }, 1000); }, 1000); /////////////////////////////////////// // Consuming Promises // Chaining Promises // Handling Rejected Promises // Throwing Errors Manually // const getCountryData = function (country) { // fetch(`https://restcountries.com/v2/name/${country}`) // .then(function (response) { // console.log(response); // return response.json(); // }) // .then(function (data) { // console.log(data); // renderCountry(data[0]); // }); // }; // const getCountryData = function (country) { // // Country 1 // fetch(`https://restcountries.com/v2/name/${country}`) // .then(response => { // console.log(response); // if (!response.ok) // throw new Error(`Country not found (${response.status})`); // return response.json(); // }) // .then(data => { // renderCountry(data[0]); // // const neighbour = data[0].borders[0]; // const neighbour = 'dfsdfdef'; // if (!neighbour) return; // // Country 2 // return fetch(`https://restcountries.eu/rest/v2/alpha/${neighbour}`); // }) // .then(response => { // if (!response.ok) // throw new Error(`Country not found (${response.status})`); // return response.json(); // }) // .then(data => renderCountry(data, 'neighbour')) // .catch(err => { // console.error(`${err} 💥💥💥`); // renderError(`Something went wrong 💥💥 ${err.message}. Try again!`); // }) // .finally(() => { // countriesContainer.style.opacity = 1; // }); // }; const getCountryData = function (country) { // Country 1 getJSON( `https://restcountries.com/v2/name/${country}`, 'Country not found' ) .then(data => { renderCountry(data[0]); const neighbour = data[0].borders[0]; if (!neighbour) throw new Error('No neighbour found!'); // Country 2 return getJSON( `https://restcountries.eu/rest/v2/alpha/${neighbour}`, 'Country not found' ); }) .then(data => renderCountry(data, 'neighbour')) .catch(err => { console.error(`${err} 💥💥💥`); renderError(`Something went wrong 💥💥 ${err.message}. Try again!`); }) .finally(() => { countriesContainer.style.opacity = 1; }); }; btn.addEventListener('click', function () { getCountryData('portugal'); }); // getCountryData('australia'); */ /////////////////////////////////////// // Coding Challenge #1 /* In this challenge you will build a function 'whereAmI' which renders a country ONLY based on GPS coordinates. For that, you will use a second API to geocode coordinates. Here are your tasks: PART 1 1. Create a function 'whereAmI' which takes as inputs a latitude value (lat) and a longitude value (lng) (these are GPS coordinates, examples are below). 2. Do 'reverse geocoding' of the provided coordinates. Reverse geocoding means to convert coordinates to a meaningful location, like a city and country name. Use this API to do reverse geocoding: https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=${lat}&longitude=${lng}. The AJAX call will be done to a URL with this format: https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=52.508&longitude=13.381. Use the fetch API and promises to get the data. Do NOT use the getJSON function we created, that is cheating 😉 3. Once you have the data, take a look at it in the console to see all the attributes that you recieved about the provided location. Then, using this data, log a messsage like this to the console: 'You are in Berlin, Germany' 4. Chain a .catch method to the end of the promise chain and log errors to the console 5. This API allows you to make only 3 requests per second. If you reload fast, you will get this error with code 403. This is an error with the request. Remember, fetch() does NOT reject the promise in this case. So create an error to reject the promise yourself, with a meaningful error message. PART 2 6. Now it's time to use the received data to render a country. So take the relevant attribute from the geocoding API result, and plug it into the countries API that we have been using. 7. Render the country and catch any errors, just like we have done in the last lecture (you can even copy this code, no need to type the same code) TEST COORDINATES 1: 52.508, 13.381 (Latitude, Longitude) TEST COORDINATES 2: 19.037, 72.873 TEST COORDINATES 2: -33.933, 18.474 GOOD LUCK 😀 */ /* const whereAmI = function (lat, lng) { fetch(`https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=${lat}&longitude=${lng}`) .then(res => { if (!res.ok) throw new Error(`Problem with geocoding ${res.status}`); return res.json(); }) .then(data => { console.log(data); console.log(`You are in ${data.city}, ${data.countryCode}`); return fetch(`https://restcountries.com/v2/name/${data.country}`); }) .then(res => { if (!res.ok) throw new Error(`Country not found (${res.status})`); return res.json(); }) .then(data => renderCountry(data[0])) .catch(err => console.error(`${err.message} 💥`)); }; whereAmI(52.508, 13.381); whereAmI(19.037, 72.873); whereAmI(-33.933, 18.474); /////////////////////////////////////// // The Event Loop in Practice console.log('Test start'); setTimeout(() => console.log('0 sec timer'), 0); Promise.resolve('Resolved promise 1').then(res => console.log(res)); Promise.resolve('Resolved promise 2').then(res => { for (let i = 0; i < 1000000000; i++) {} console.log(res); }); console.log('Test end'); /////////////////////////////////////// // Building a Simple Promise const lotteryPromise = new Promise(function (resolve, reject) { console.log('Lotter draw is happening 🔮'); setTimeout(function () { if (Math.random() >= 0.5) { resolve('You WIN 💰'); } else { reject(new Error('You lost your money 💩')); } }, 2000); }); lotteryPromise.then(res => console.log(res)).catch(err => console.error(err)); // Promisifying setTimeout const wait = function (seconds) { return new Promise(function (resolve) { setTimeout(resolve, seconds * 1000); }); }; wait(1) .then(() => { console.log('1 second passed'); return wait(1); }) .then(() => { console.log('2 second passed'); return wait(1); }) .then(() => { console.log('3 second passed'); return wait(1); }) .then(() => console.log('4 second passed')); // setTimeout(() => { // console.log('1 second passed'); // setTimeout(() => { // console.log('2 seconds passed'); // setTimeout(() => { // console.log('3 second passed'); // setTimeout(() => { // console.log('4 second passed'); // }, 1000); // }, 1000); // }, 1000); // }, 1000); Promise.resolve('abc').then(x => console.log(x)); Promise.reject(new Error('Problem!')).catch(x => console.error(x)); /////////////////////////////////////// // Promisifying the Geolocation API const getPosition = function () { return new Promise(function (resolve, reject) { // navigator.geolocation.getCurrentPosition( // position => resolve(position), // err => reject(err) // ); navigator.geolocation.getCurrentPosition(resolve, reject); }); }; // getPosition().then(pos => console.log(pos)); const whereAmI = function () { getPosition() .then(pos => { const { latitude: lat, longitude: lng } = pos.coords; return fetch(`https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=${lat}&longitude=${lng}`); }) .then(res => { if (!res.ok) throw new Error(`Problem with geocoding ${res.status}`); return res.json(); }) .then(data => { console.log(data); console.log(`You are in ${data.city}, ${data.countryCode}`); return fetch(`https://restcountries.com/v2/name/${data.countryCode}`); }) .then(res => { if (!res.ok) throw new Error(`Country not found (${res.status})`); return res.json(); }) .then(data => renderCountry(data[0])) .catch(err => console.error(`${err.message} 💥`)); }; btn.addEventListener('click', whereAmI); */ /////////////////////////////////////// // Coding Challenge #2 /* Build the image loading functionality that I just showed you on the screen. Tasks are not super-descriptive this time, so that you can figure out some stuff on your own. Pretend you're working on your own 😉 PART 1 1. Create a function 'createImage' which receives imgPath as an input. This function returns a promise which creates a new image (use document.createElement('img')) and sets the .src attribute to the provided image path. When the image is done loading, append it to the DOM element with the 'images' class, and resolve the promise. The fulfilled value should be the image element itself. In case there is an error loading the image ('error' event), reject the promise. If this part is too tricky for you, just watch the first part of the solution. PART 2 2. Comsume the promise using .then and also add an error handler; 3. After the image has loaded, pause execution for 2 seconds using the wait function we created earlier; 4. After the 2 seconds have passed, hide the current image (set display to 'none'), and load a second image (HINT: Use the image element returned by the createImage promise to hide the current image. You will need a global variable for that 😉); 5. After the second image has loaded, pause execution for 2 seconds again; 6. After the 2 seconds have passed, hide the current image. TEST DATA: Images in the img folder. Test the error handler by passing a wrong image path. Set the network speed to 'Fast 3G' in the dev tools Network tab, otherwise images load too fast. GOOD LUCK 😀 */ /* const wait = function (seconds) { return new Promise(function (resolve) { setTimeout(resolve, seconds * 1000); }); }; const imgContainer = document.querySelector('.images'); const createImage = function (imgPath) { return new Promise(function (resolve, reject) { const img = document.createElement('img'); img.src = imgPath; img.addEventListener('load', function () { imgContainer.append(img); resolve(img); }); img.addEventListener('error', function () { reject(new Error('Image not found')); }); }); }; let currentImg; createImage('img/img-1.jpg') .then(img => { currentImg = img; console.log('Image 1 loaded'); return wait(2); }) .then(() => { currentImg.style.display = 'none'; return createImage('img/img-2.jpg'); }) .then(img => { currentImg = img; console.log('Image 2 loaded'); return wait(2); }) .then(() => { currentImg.style.display = 'none'; }) .catch(err => console.error(err)); /////////////////////////////////////// // Consuming Promises with Async/Await // Error Handling With try...catch const getPosition = function () { return new Promise(function (resolve, reject) { navigator.geolocation.getCurrentPosition(resolve, reject); }); }; // fetch(`https://restcountries.com/v2/name/${country}`).then(res => console.log(res)) const whereAmI = async function () { try { // Geolocation const pos = await getPosition(); const { latitude: lat, longitude: lng } = pos.coords; // Reverse geocoding const resGeo = await fetch(`https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=${lat}&longitude=${lng}`); if (!resGeo.ok) throw new Error('Problem getting location data'); const dataGeo = await resGeo.json(); console.log(dataGeo); // Country data const res = await fetch( `https://restcountries.com/v2/name/${dataGeo.countryCode}` ); // BUG in video: // if (!resGeo.ok) throw new Error('Problem getting country'); // FIX: if (!res.ok) throw new Error('Problem getting country'); const data = await res.json(); console.log(data); renderCountry(data[0]); } catch (err) { console.error(`${err} 💥`); renderError(`💥 ${err.message}`); } }; whereAmI(); whereAmI(); whereAmI(); console.log('FIRST'); // try { // let y = 1; // const x = 2; // y = 3; // } catch (err) { // alert(err.message); // } /////////////////////////////////////// // Returning Values from Async Functions const getPosition = function () { return new Promise(function (resolve, reject) { navigator.geolocation.getCurrentPosition(resolve, reject); }); }; const whereAmI = async function () { try { // Geolocation const pos = await getPosition(); const { latitude: lat, longitude: lng } = pos.coords; // Reverse geocoding const resGeo = await fetch(`https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=${lat}&longitude=${lng}`); if (!resGeo.ok) throw new Error('Problem getting location data'); const dataGeo = await resGeo.json(); // Country data const res = await fetch( `https://restcountries.com/v2/name/${dataGeo.country}` ); if (!resGeo.ok) throw new Error('Problem getting country'); const data = await res.json(); renderCountry(data[0]); return `You are in ${dataGeo.city}, ${dataGeo.country}`; } catch (err) { console.error(`${err} 💥`); renderError(`💥 ${err.message}`); // Reject promise returned from async function throw err; } }; console.log('1: Will get location'); // const city = whereAmI(); // console.log(city); // whereAmI() // .then(city => console.log(`2: ${city}`)) // .catch(err => console.error(`2: ${err.message} 💥`)) // .finally(() => console.log('3: Finished getting location')); (async function () { try { const city = await whereAmI(); console.log(`2: ${city}`); } catch (err) { console.error(`2: ${err.message} 💥`); } console.log('3: Finished getting location'); })(); /////////////////////////////////////// // Running Promises in Parallel const get3Countries = async function (c1, c2, c3) { try { // const [data1] = await getJSON( // `https://restcountries.com/v2/name/${c1}` // ); // const [data2] = await getJSON( // `https://restcountries.com/v2/name/${c2}` // ); // const [data3] = await getJSON( // `https://restcountries.com/v2/name/${c3}` // ); // console.log([data1.capital, data2.capital, data3.capital]); const data = await Promise.all([ getJSON(`https://restcountries.com/v2/name/${c1}`), getJSON(`https://restcountries.com/v2/name/${c2}`), getJSON(`https://restcountries.com/v2/name/${c3}`), ]); console.log(data.map(d => d[0].capital)); } catch (err) { console.error(err); } }; get3Countries('portugal', 'canada', 'tanzania'); /////////////////////////////////////// // Other Promise Combinators: race, allSettled and any // Promise.race (async function () { const res = await Promise.race([ getJSON(`https://restcountries.com/v2/name/italy`), getJSON(`https://restcountries.com/v2/name/egypt`), getJSON(`https://restcountries.com/v2/name/mexico`), ]); console.log(res[0]); })(); const timeout = function (sec) { return new Promise(function (_, reject) { setTimeout(function () { reject(new Error('Request took too long!')); }, sec * 1000); }); }; Promise.race([ getJSON(`https://restcountries.com/v2/name/tanzania`), timeout(5), ]) .then(res => console.log(res[0])) .catch(err => console.error(err)); // Promise.allSettled Promise.allSettled([ Promise.resolve('Success'), Promise.reject('ERROR'), Promise.resolve('Another success'), ]).then(res => console.log(res)); Promise.all([ Promise.resolve('Success'), Promise.reject('ERROR'), Promise.resolve('Another success'), ]) .then(res => console.log(res)) .catch(err => console.error(err)); // Promise.any [ES2021] Promise.any([ Promise.resolve('Success'), Promise.reject('ERROR'), Promise.resolve('Another success'), ]) .then(res => console.log(res)) .catch(err => console.error(err)); */ /////////////////////////////////////// // Coding Challenge #3 /* PART 1 Write an async function 'loadNPause' that recreates Coding Challenge #2, this time using async/await (only the part where the promise is consumed). Compare the two versions, think about the big differences, and see which one you like more. Don't forget to test the error handler, and to set the network speed to 'Fast 3G' in the dev tools Network tab. PART 2 1. Create an async function 'loadAll' that receives an array of image paths 'imgArr'; 2. Use .map to loop over the array, to load all the images with the 'createImage' function (call the resulting array 'imgs') 3. Check out the 'imgs' array in the console! Is it like you expected? 4. Use a promise combinator function to actually get the images from the array 😉 5. Add the 'paralell' class to all the images (it has some CSS styles). TEST DATA: ['img/img-1.jpg', 'img/img-2.jpg', 'img/img-3.jpg']. To test, turn off the 'loadNPause' function. GOOD LUCK 😀 */ /* const wait = function (seconds) { return new Promise(function (resolve) { setTimeout(resolve, seconds * 1000); }); }; const imgContainer = document.querySelector('.images'); const createImage = function (imgPath) { return new Promise(function (resolve, reject) { const img = document.createElement('img'); img.src = imgPath; img.addEventListener('load', function () { imgContainer.append(img); resolve(img); }); img.addEventListener('error', function () { reject(new Error('Image not found')); }); }); }; let currentImg; // createImage('img/img-1.jpg') // .then(img => { // currentImg = img; // console.log('Image 1 loaded'); // return wait(2); // }) // .then(() => { // currentImg.style.display = 'none'; // return createImage('img/img-2.jpg'); // }) // .then(img => { // currentImg = img; // console.log('Image 2 loaded'); // return wait(2); // }) // .then(() => { // currentImg.style.display = 'none'; // }) // .catch(err => console.error(err)); // PART 1 const loadNPause = async function () { try { // Load image 1 let img = await createImage('img/img-1.jpg'); console.log('Image 1 loaded'); await wait(2); img.style.display = 'none'; // Load image 1 img = await createImage('img/img-2.jpg'); console.log('Image 2 loaded'); await wait(2); img.style.display = 'none'; } catch (err) { console.error(err); } }; // loadNPause(); // PART 2 const loadAll = async function (imgArr) { try { const imgs = imgArr.map(async img => await createImage(img)); const imgsEl = await Promise.all(imgs); console.log(imgsEl); imgsEl.forEach(img => img.classList.add('parallel')); } catch (err) { console.error(err); } }; loadAll(['img/img-1.jpg', 'img/img-2.jpg', 'img/img-3.jpg']); */ ================================================ FILE: 16-Asynchronous/final/style.css ================================================ * { margin: 0; padding: 0; box-sizing: inherit; } html { font-size: 62.5%; box-sizing: border-box; } body { font-family: system-ui; color: #555; background-color: #f7f7f7; min-height: 100vh; display: flex; align-items: center; justify-content: center; } .container { display: flex; flex-flow: column; align-items: center; } .countries { /* margin-bottom: 8rem; */ display: flex; font-size: 2rem; opacity: 0; transition: opacity 1s; } .country { background-color: #fff; box-shadow: 0 2rem 5rem 1rem rgba(0, 0, 0, 0.1); font-size: 1.8rem; width: 30rem; border-radius: 0.7rem; margin: 0 3rem; /* overflow: hidden; */ } .neighbour::before { content: 'Neighbour country'; width: 100%; position: absolute; top: -4rem; text-align: center; font-size: 1.8rem; font-weight: 600; text-transform: uppercase; color: #888; } .neighbour { transform: scale(0.8) translateY(1rem); margin-left: 0; } .country__img { width: 30rem; height: 17rem; object-fit: cover; background-color: #eee; border-top-left-radius: 0.7rem; border-top-right-radius: 0.7rem; } .country__data { padding: 2.5rem 3.75rem 3rem 3.75rem; } .country__name { font-size: 2.7rem; margin-bottom: 0.7rem; } .country__region { font-size: 1.4rem; margin-bottom: 2.5rem; text-transform: uppercase; color: #888; } .country__row:not(:last-child) { margin-bottom: 1rem; } .country__row span { display: inline-block; margin-right: 2rem; font-size: 2.4rem; } .btn-country { border: none; font-size: 2rem; padding: 2rem 5rem; border-radius: 0.7rem; color: white; background-color: orangered; cursor: pointer; } .images { display: flex; } .images img { display: block; width: 80rem; margin: 4rem; } .images img.parallel { width: 40rem; margin: 2rem; border: 3rem solid white; box-shadow: 0 2rem 5rem 1rem rgba(0, 0, 0, 0.1); } ================================================ FILE: 16-Asynchronous/starter/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 16-Asynchronous/starter/index.html ================================================ Asynchronous JavaScript
    ================================================ FILE: 16-Asynchronous/starter/script.js ================================================ 'use strict'; const btn = document.querySelector('.btn-country'); const countriesContainer = document.querySelector('.countries'); // NEW COUNTRIES API URL (use instead of the URL shown in videos): // https://restcountries.com/v2/name/portugal // NEW REVERSE GEOCODING API URL (use instead of the URL shown in videos): // https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=${lat}&longitude=${lng} /////////////////////////////////////// ================================================ FILE: 16-Asynchronous/starter/style.css ================================================ * { margin: 0; padding: 0; box-sizing: inherit; } html { font-size: 62.5%; box-sizing: border-box; } body { font-family: system-ui; color: #555; background-color: #f7f7f7; min-height: 100vh; display: flex; align-items: center; justify-content: center; } .container { display: flex; flex-flow: column; align-items: center; } .countries { /* margin-bottom: 8rem; */ display: flex; font-size: 2rem; opacity: 0; transition: opacity 1s; } .country { background-color: #fff; box-shadow: 0 2rem 5rem 1rem rgba(0, 0, 0, 0.1); font-size: 1.8rem; width: 30rem; border-radius: 0.7rem; margin: 0 3rem; /* overflow: hidden; */ } .neighbour::before { content: 'Neighbour country'; width: 100%; position: absolute; top: -4rem; text-align: center; font-size: 1.8rem; font-weight: 600; text-transform: uppercase; color: #888; } .neighbour { transform: scale(0.8) translateY(1rem); margin-left: 0; } .country__img { width: 30rem; height: 17rem; object-fit: cover; background-color: #eee; border-top-left-radius: 0.7rem; border-top-right-radius: 0.7rem; } .country__data { padding: 2.5rem 3.75rem 3rem 3.75rem; } .country__name { font-size: 2.7rem; margin-bottom: 0.7rem; } .country__region { font-size: 1.4rem; margin-bottom: 2.5rem; text-transform: uppercase; color: #888; } .country__row:not(:last-child) { margin-bottom: 1rem; } .country__row span { display: inline-block; margin-right: 2rem; font-size: 2.4rem; } .btn-country { border: none; font-size: 2rem; padding: 2rem 5rem; border-radius: 0.7rem; color: white; background-color: orangered; cursor: pointer; } .images { display: flex; } .images img { display: block; width: 80rem; margin: 4rem; } .images img.parallel { width: 40rem; margin: 2rem; border: 3rem solid white; box-shadow: 0 2rem 5rem 1rem rgba(0, 0, 0, 0.1); } ================================================ FILE: 17-Modern-JS-Modules-Tooling/final/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 17-Modern-JS-Modules-Tooling/final/clean.js ================================================ 'use strict'; const budget = Object.freeze([ { value: 250, description: 'Sold old TV 📺', user: 'jonas' }, { value: -45, description: 'Groceries 🥑', user: 'jonas' }, { value: 3500, description: 'Monthly salary 👩‍💻', user: 'jonas' }, { value: 300, description: 'Freelancing 👩‍💻', user: 'jonas' }, { value: -1100, description: 'New iPhone 📱', user: 'jonas' }, { value: -20, description: 'Candy 🍭', user: 'matilda' }, { value: -125, description: 'Toys 🚂', user: 'matilda' }, { value: -1800, description: 'New Laptop 💻', user: 'jonas' }, ]); const spendingLimits = Object.freeze({ jonas: 1500, matilda: 100, }); // spendingLimits.jay = 200; // const limit = spendingLimits[user] ? spendingLimits[user] : 0; const getLimit = (limits, user) => limits?.[user] ?? 0; // Pure function :D const addExpense = function ( state, limits, value, description, user = 'jonas' ) { const cleanUser = user.toLowerCase(); return value <= getLimit(limits, cleanUser) ? [...state, { value: -value, description, user: cleanUser }] : state; }; const newBudget1 = addExpense(budget, spendingLimits, 10, 'Pizza 🍕'); const newBudget2 = addExpense( newBudget1, spendingLimits, 100, 'Going to movies 🍿', 'Matilda' ); const newBudget3 = addExpense(newBudget2, spendingLimits, 200, 'Stuff', 'Jay'); // const checkExpenses2 = function (state, limits) { // return state.map(entry => { // return entry.value < -getLimit(limits, entry.user) // ? { ...entry, flag: 'limit' } // : entry; // }); // // for (const entry of newBudget3) // // if (entry.value < -getLimit(limits, entry.user)) entry.flag = 'limit'; // }; const checkExpenses = (state, limits) => state.map(entry => entry.value < -getLimit(limits, entry.user) ? { ...entry, flag: 'limit' } : entry ); const finalBudget = checkExpenses(newBudget3, spendingLimits); console.log(finalBudget); // Impure const logBigExpenses = function (state, bigLimit) { const bigExpenses = state .filter(entry => entry.value <= -bigLimit) .map(entry => entry.description.slice(-2)) .join(' / '); // .reduce((str, cur) => `${str} / ${cur.description.slice(-2)}`, ''); console.log(bigExpenses); // let output = ''; // for (const entry of budget) // output += // entry.value <= -bigLimit ? `${entry.description.slice(-2)} / ` : ''; // output = output.slice(0, -2); // Remove last '/ ' // console.log(output); }; logBigExpenses(finalBudget, 500); ================================================ FILE: 17-Modern-JS-Modules-Tooling/final/dist/index.html ================================================ Modern JavaScript Development: Modules and Tooling

    Modern JavaScript Development: Modules and Tooling

    ================================================ FILE: 17-Modern-JS-Modules-Tooling/final/dist/index.js ================================================ // modules are defined as an array // [ module function, map of requires ] // // map of requires is short require name -> numeric require // // anything defined in a previous bundle is accessed via the // orig method which is the require for previous bundles parcelRequire = (function (modules, cache, entry, globalName) { // Save the require from previous bundle to this closure if any var previousRequire = typeof parcelRequire === 'function' && parcelRequire; var nodeRequire = typeof require === 'function' && require; function newRequire(name, jumped) { if (!cache[name]) { if (!modules[name]) { // if we cannot find the module within our internal map or // cache jump to the current global require ie. the last bundle // that was added to the page. var currentRequire = typeof parcelRequire === 'function' && parcelRequire; if (!jumped && currentRequire) { return currentRequire(name, true); } // If there are other bundles on this page the require from the // previous one is saved to 'previousRequire'. Repeat this as // many times as there are bundles until the module is found or // we exhaust the require chain. if (previousRequire) { return previousRequire(name, true); } // Try the node require function if it exists. if (nodeRequire && typeof name === 'string') { return nodeRequire(name); } var err = new Error('Cannot find module \'' + name + '\''); err.code = 'MODULE_NOT_FOUND'; throw err; } localRequire.resolve = resolve; localRequire.cache = {}; var module = cache[name] = new newRequire.Module(name); modules[name][0].call(module.exports, localRequire, module, module.exports, this); } return cache[name].exports; function localRequire(x){ return newRequire(localRequire.resolve(x)); } function resolve(x){ return modules[name][1][x] || x; } } function Module(moduleName) { this.id = moduleName; this.bundle = newRequire; this.exports = {}; } newRequire.isParcelRequire = true; newRequire.Module = Module; newRequire.modules = modules; newRequire.cache = cache; newRequire.parent = previousRequire; newRequire.register = function (id, exports) { modules[id] = [function (require, module) { module.exports = exports; }, {}]; }; var error; for (var i = 0; i < entry.length; i++) { try { newRequire(entry[i]); } catch (e) { // Save first error but execute all entries if (!error) { error = e; } } } if (entry.length) { // Expose entry point to Node, AMD or browser globals // Based on https://github.com/ForbesLindesay/umd/blob/master/template.js var mainExports = newRequire(entry[entry.length - 1]); // CommonJS if (typeof exports === "object" && typeof module !== "undefined") { module.exports = mainExports; // RequireJS } else if (typeof define === "function" && define.amd) { define(function () { return mainExports; }); // Modern JavaScript Development: Modules and Tooling

    Modern JavaScript Development: Modules and Tooling

    ================================================ FILE: 17-Modern-JS-Modules-Tooling/final/package.json ================================================ { "name": "17-modern-js-modules-tooling", "version": "1.0.0", "description": "", "main": "script.js", "scripts": { "start": "parcel index.html", "build": "parcel build index.html" }, "author": "Jonas", "license": "ISC", "dependencies": { "core-js": "^3.6.5", "leaflet": "^1.6.0", "lodash-es": "^4.17.15", "regenerator-runtime": "^0.13.7" }, "devDependencies": { "parcel": "^1.12.4" } } ================================================ FILE: 17-Modern-JS-Modules-Tooling/final/script.js ================================================ /////////////////////////////////////// // Exporting and Importing in ES6 Modules // Importing module // import { addToCart, totalPrice as price, tq } from './shoppingCart.js'; // addToCart('bread', 5); // console.log(price, tq); console.log('Importing module'); // console.log(shippingCost); // import * as ShoppingCart from './shoppingCart.js'; // ShoppingCart.addToCart('bread', 5); // console.log(ShoppingCart.totalPrice); // import add, { addToCart, totalPrice as price, tq } from './shoppingCart.js'; // console.log(price); import add, { cart } from './shoppingCart.js'; add('pizza', 2); add('bread', 5); add('apples', 4); console.log(cart); /* /////////////////////////////////////// // Top-Level Await (ES2022) // console.log('Start fetching'); // const res = await fetch('https://jsonplaceholder.typicode.com/posts'); // const data = await res.json(); // console.log(data); // console.log('Something'); const getLastPost = async function () { const res = await fetch('https://jsonplaceholder.typicode.com/posts'); const data = await res.json(); return { title: data.at(-1).title, text: data.at(-1).body }; }; const lastPost = getLastPost(); console.log(lastPost); // Not very clean // lastPost.then(last => console.log(last)); const lastPost2 = await getLastPost(); console.log(lastPost2); /////////////////////////////////////// // The Module Pattern const ShoppingCart2 = (function () { const cart = []; const shippingCost = 10; const totalPrice = 237; const totalQuantity = 23; const addToCart = function (product, quantity) { cart.push({ product, quantity }); console.log( `${quantity} ${product} added to cart (sipping cost is ${shippingCost})` ); }; const orderStock = function (product, quantity) { console.log(`${quantity} ${product} ordered from supplier`); }; return { addToCart, cart, totalPrice, totalQuantity, }; })(); ShoppingCart2.addToCart('apple', 4); ShoppingCart2.addToCart('pizza', 2); console.log(ShoppingCart2); console.log(ShoppingCart2.shippingCost); /////////////////////////////////////// // CommonJS Modules // Export export.addTocart = function (product, quantity) { cart.push({ product, quantity }); console.log( `${quantity} ${product} added to cart (sipping cost is ${shippingCost})` ); }; // Import const { addTocart } = require('./shoppingCart.js'); */ /////////////////////////////////////// // Introduction to NPM // import cloneDeep from './node_modules/lodash-es/cloneDeep.js'; import cloneDeep from 'lodash-es'; const state = { cart: [ { product: 'bread', quantity: 5 }, { product: 'pizza', quantity: 5 }, ], user: { loggedIn: true }, }; const stateClone = Object.assign({}, state); const stateDeepClone = cloneDeep(state); state.user.loggedIn = false; console.log(stateClone); console.log(stateDeepClone); if (module.hot) { module.hot.accept(); } class Person { #greeting = 'Hey'; constructor(name) { this.name = name; console.log(`${this.#greeting}, ${this.name}`); } } const jonas = new Person('Jonas'); console.log('Jonas' ?? null); console.log(cart.find(el => el.quantity >= 2)); Promise.resolve('TEST').then(x => console.log(x)); import 'core-js/stable'; // import 'core-js/stable/array/find'; // import 'core-js/stable/promise'; // Polifilling async functions import 'regenerator-runtime/runtime'; ================================================ FILE: 17-Modern-JS-Modules-Tooling/final/shoppingCart.js ================================================ // Exporting module console.log('Exporting module'); // Blocking code // console.log('Start fetching users'); // await fetch('https://jsonplaceholder.typicode.com/users'); // console.log('Finish fetching users'); const shippingCost = 10; export const cart = []; export const addToCart = function (product, quantity) { cart.push({ product, quantity }); console.log(`${quantity} ${product} added to cart`); }; const totalPrice = 237; const totalQuantity = 23; export { totalPrice, totalQuantity as tq }; export default function (product, quantity) { cart.push({ product, quantity }); console.log(`${quantity} ${product} added to cart`); } ================================================ FILE: 17-Modern-JS-Modules-Tooling/starter/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 17-Modern-JS-Modules-Tooling/starter/clean.js ================================================ var budget = [ { value: 250, description: 'Sold old TV 📺', user: 'jonas' }, { value: -45, description: 'Groceries 🥑', user: 'jonas' }, { value: 3500, description: 'Monthly salary 👩‍💻', user: 'jonas' }, { value: 300, description: 'Freelancing 👩‍💻', user: 'jonas' }, { value: -1100, description: 'New iPhone 📱', user: 'jonas' }, { value: -20, description: 'Candy 🍭', user: 'matilda' }, { value: -125, description: 'Toys 🚂', user: 'matilda' }, { value: -1800, description: 'New Laptop 💻', user: 'jonas' }, ]; var limits = { jonas: 1500, matilda: 100, }; var add = function (value, description, user) { if (!user) user = 'jonas'; user = user.toLowerCase(); var lim; if (limits[user]) { lim = limits[user]; } else { lim = 0; } if (value <= lim) { budget.push({ value: -value, description: description, user: user }); } }; add(10, 'Pizza 🍕'); add(100, 'Going to movies 🍿', 'Matilda'); add(200, 'Stuff', 'Jay'); console.log(budget); var check = function () { for (var el of budget) { var lim; if (limits[el.user]) { lim = limits[el.user]; } else { lim = 0; } if (el.value < -lim) { el.flag = 'limit'; } } }; check(); console.log(budget); var bigExpenses = function (limit) { var output = ''; for (var el of budget) { if (el.value <= -limit) { output += el.description.slice(-2) + ' / '; // Emojis are 2 chars } } output = output.slice(0, -2); // Remove last '/ ' console.log(output); }; ================================================ FILE: 17-Modern-JS-Modules-Tooling/starter/index.html ================================================ Modern JavaScript Development: Modules and Tooling

    Modern JavaScript Development: Modules and Tooling

    ================================================ FILE: 17-Modern-JS-Modules-Tooling/starter/script.js ================================================ ================================================ FILE: 18-forkify/final/.gitignore ================================================ node_modules dist .parcel-cache .DS_Store ================================================ FILE: 18-forkify/final/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 18-forkify/final/README.md ================================================ # forkify Project Recipe application with custom recipe uploads. ================================================ FILE: 18-forkify/final/index.html ================================================ forkify // Search over 1,000,000 recipes

    Start by searching for a recipe or an ingredient. Have fun!

    ================================================ FILE: 18-forkify/final/package.json ================================================ { "name": "forkify", "version": "1.0.0", "description": "Recipe application", "default": "index.html", "scripts": { "start": "parcel index.html", "build": "parcel build index.html --dist-dir ./dist" }, "author": "Jonas Schedtmann", "license": "ISC", "devDependencies": { "parcel": "^2.0.0-beta.1", "sass": "^1.26.10" }, "dependencies": { "core-js": "^3.6.5", "fractional": "^1.0.0", "regenerator-runtime": "^0.13.7" } } ================================================ FILE: 18-forkify/final/src/js/config.js ================================================ export const API_URL = 'https://forkify-api.jonas.io/api/v2/recipes/'; export const TIMEOUT_SEC = 10; export const RES_PER_PAGE = 10; export const KEY = ''; export const MODAL_CLOSE_SEC = 2.5; ================================================ FILE: 18-forkify/final/src/js/controller.js ================================================ import * as model from './model.js'; import { MODAL_CLOSE_SEC } from './config.js'; import recipeView from './views/recipeView.js'; import searchView from './views/searchView.js'; import resultsView from './views/resultsView.js'; import paginationView from './views/paginationView.js'; import bookmarksView from './views/bookmarksView.js'; import addRecipeView from './views/addRecipeView.js'; import 'core-js/stable'; import 'regenerator-runtime/runtime'; import { async } from 'regenerator-runtime'; const controlRecipes = async function () { try { const id = window.location.hash.slice(1); if (!id) return; recipeView.renderSpinner(); // 0) Update results view to mark selected search result resultsView.update(model.getSearchResultsPage()); // 1) Updating bookmarks view bookmarksView.update(model.state.bookmarks); // 2) Loading recipe await model.loadRecipe(id); // 3) Rendering recipe recipeView.render(model.state.recipe); } catch (err) { recipeView.renderError(); console.error(err); } }; const controlSearchResults = async function () { try { resultsView.renderSpinner(); // 1) Get search query const query = searchView.getQuery(); if (!query) return; // 2) Load search results await model.loadSearchResults(query); // 3) Render results resultsView.render(model.getSearchResultsPage()); // 4) Render initial pagination buttons paginationView.render(model.state.search); } catch (err) { console.log(err); } }; const controlPagination = function (goToPage) { // 1) Render NEW results resultsView.render(model.getSearchResultsPage(goToPage)); // 2) Render NEW pagination buttons paginationView.render(model.state.search); }; const controlServings = function (newServings) { // Update the recipe servings (in state) model.updateServings(newServings); // Update the recipe view recipeView.update(model.state.recipe); }; const controlAddBookmark = function () { // 1) Add/remove bookmark if (!model.state.recipe.bookmarked) model.addBookmark(model.state.recipe); else model.deleteBookmark(model.state.recipe.id); // 2) Update recipe view recipeView.update(model.state.recipe); // 3) Render bookmarks bookmarksView.render(model.state.bookmarks); }; const controlBookmarks = function () { bookmarksView.render(model.state.bookmarks); }; const controlAddRecipe = async function (newRecipe) { try { // Show loading spinner addRecipeView.renderSpinner(); // Upload the new recipe data await model.uploadRecipe(newRecipe); console.log(model.state.recipe); // Render recipe recipeView.render(model.state.recipe); // Success message addRecipeView.renderMessage(); // Render bookmark view bookmarksView.render(model.state.bookmarks); // Change ID in URL window.history.pushState(null, '', `#${model.state.recipe.id}`); // Close form window setTimeout(function () { addRecipeView.toggleWindow(); }, MODAL_CLOSE_SEC * 1000); } catch (err) { console.error('💥', err); addRecipeView.renderError(err.message); } }; const init = function () { bookmarksView.addHandlerRender(controlBookmarks); recipeView.addHandlerRender(controlRecipes); recipeView.addHandlerUpdateServings(controlServings); recipeView.addHandlerAddBookmark(controlAddBookmark); searchView.addHandlerSearch(controlSearchResults); paginationView.addHandlerClick(controlPagination); addRecipeView.addHandlerUpload(controlAddRecipe); }; init(); ================================================ FILE: 18-forkify/final/src/js/helpers.js ================================================ import { async } from 'regenerator-runtime'; import { TIMEOUT_SEC } from './config.js'; const timeout = function (s) { return new Promise(function (_, reject) { setTimeout(function () { reject(new Error(`Request took too long! Timeout after ${s} second`)); }, s * 1000); }); }; export const AJAX = async function (url, uploadData = undefined) { try { const fetchPro = uploadData ? fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(uploadData), }) : fetch(url); const res = await Promise.race([fetchPro, timeout(TIMEOUT_SEC)]); const data = await res.json(); if (!res.ok) throw new Error(`${data.message} (${res.status})`); return data; } catch (err) { throw err; } }; /* export const getJSON = async function (url) { try { const fetchPro = fetch(url); const res = await Promise.race([fetchPro, timeout(TIMEOUT_SEC)]); const data = await res.json(); if (!res.ok) throw new Error(`${data.message} (${res.status})`); return data; } catch (err) { throw err; } }; export const sendJSON = async function (url, uploadData) { try { const fetchPro = fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(uploadData), }); const res = await Promise.race([fetchPro, timeout(TIMEOUT_SEC)]); const data = await res.json(); if (!res.ok) throw new Error(`${data.message} (${res.status})`); return data; } catch (err) { throw err; } }; */ ================================================ FILE: 18-forkify/final/src/js/model.js ================================================ import { async } from 'regenerator-runtime'; import { API_URL, RES_PER_PAGE, KEY } from './config.js'; // import { getJSON, sendJSON } from './helpers.js'; import { AJAX } from './helpers.js'; export const state = { recipe: {}, search: { query: '', results: [], page: 1, resultsPerPage: RES_PER_PAGE, }, bookmarks: [], }; const createRecipeObject = function (data) { const { recipe } = data.data; return { id: recipe.id, title: recipe.title, publisher: recipe.publisher, sourceUrl: recipe.source_url, image: recipe.image_url, servings: recipe.servings, cookingTime: recipe.cooking_time, ingredients: recipe.ingredients, ...(recipe.key && { key: recipe.key }), }; }; export const loadRecipe = async function (id) { try { const data = await AJAX(`${API_URL}${id}?key=${KEY}`); state.recipe = createRecipeObject(data); if (state.bookmarks.some(bookmark => bookmark.id === id)) state.recipe.bookmarked = true; else state.recipe.bookmarked = false; console.log(state.recipe); } catch (err) { // Temp error handling console.error(`${err} 💥💥💥💥`); throw err; } }; export const loadSearchResults = async function (query) { try { state.search.query = query; const data = await AJAX(`${API_URL}?search=${query}&key=${KEY}`); console.log(data); state.search.results = data.data.recipes.map(rec => { return { id: rec.id, title: rec.title, publisher: rec.publisher, image: rec.image_url, ...(rec.key && { key: rec.key }), }; }); state.search.page = 1; } catch (err) { console.error(`${err} 💥💥💥💥`); throw err; } }; export const getSearchResultsPage = function (page = state.search.page) { state.search.page = page; const start = (page - 1) * state.search.resultsPerPage; // 0 const end = page * state.search.resultsPerPage; // 9 return state.search.results.slice(start, end); }; export const updateServings = function (newServings) { state.recipe.ingredients.forEach(ing => { ing.quantity = (ing.quantity * newServings) / state.recipe.servings; // newQt = oldQt * newServings / oldServings // 2 * 8 / 4 = 4 }); state.recipe.servings = newServings; }; const persistBookmarks = function () { localStorage.setItem('bookmarks', JSON.stringify(state.bookmarks)); }; export const addBookmark = function (recipe) { // Add bookmark state.bookmarks.push(recipe); // Mark current recipe as bookmarked if (recipe.id === state.recipe.id) state.recipe.bookmarked = true; persistBookmarks(); }; export const deleteBookmark = function (id) { // Delete bookmark const index = state.bookmarks.findIndex(el => el.id === id); state.bookmarks.splice(index, 1); // Mark current recipe as NOT bookmarked if (id === state.recipe.id) state.recipe.bookmarked = false; persistBookmarks(); }; const init = function () { const storage = localStorage.getItem('bookmarks'); if (storage) state.bookmarks = JSON.parse(storage); }; init(); const clearBookmarks = function () { localStorage.clear('bookmarks'); }; // clearBookmarks(); export const uploadRecipe = async function (newRecipe) { try { const ingredients = Object.entries(newRecipe) .filter(entry => entry[0].startsWith('ingredient') && entry[1] !== '') .map(ing => { const ingArr = ing[1].split(',').map(el => el.trim()); // const ingArr = ing[1].replaceAll(' ', '').split(','); if (ingArr.length !== 3) throw new Error( 'Wrong ingredient fromat! Please use the correct format :)' ); const [quantity, unit, description] = ingArr; return { quantity: quantity ? +quantity : null, unit, description }; }); const recipe = { title: newRecipe.title, source_url: newRecipe.sourceUrl, image_url: newRecipe.image, publisher: newRecipe.publisher, cooking_time: +newRecipe.cookingTime, servings: +newRecipe.servings, ingredients, }; const data = await AJAX(`${API_URL}?key=${KEY}`, recipe); state.recipe = createRecipeObject(data); addBookmark(state.recipe); } catch (err) { throw err; } }; ================================================ FILE: 18-forkify/final/src/js/views/View.js ================================================ import icons from 'url:../../img/icons.svg'; // Parcel 2 export default class View { _data; /** * Render the received object to the DOM * @param {Object | Object[]} data The data to be rendered (e.g. recipe) * @param {boolean} [render=true] If false, create markup string instead of rendering to the DOM * @returns {undefined | string} A markup string is returned if render=false * @this {Object} View instance * @author Jonas Schmedtmann * @todo Finish implementation */ render(data, render = true) { if (!data || (Array.isArray(data) && data.length === 0)) return this.renderError(); this._data = data; const markup = this._generateMarkup(); if (!render) return markup; this._clear(); this._parentElement.insertAdjacentHTML('afterbegin', markup); } update(data) { this._data = data; const newMarkup = this._generateMarkup(); const newDOM = document.createRange().createContextualFragment(newMarkup); const newElements = Array.from(newDOM.querySelectorAll('*')); const curElements = Array.from(this._parentElement.querySelectorAll('*')); newElements.forEach((newEl, i) => { const curEl = curElements[i]; // console.log(curEl, newEl.isEqualNode(curEl)); // Updates changed TEXT if ( !newEl.isEqualNode(curEl) && newEl.firstChild?.nodeValue.trim() !== '' ) { // console.log('💥', newEl.firstChild.nodeValue.trim()); curEl.textContent = newEl.textContent; } // Updates changed ATTRIBUES if (!newEl.isEqualNode(curEl)) Array.from(newEl.attributes).forEach(attr => curEl.setAttribute(attr.name, attr.value) ); }); } _clear() { this._parentElement.innerHTML = ''; } renderSpinner() { const markup = `
    `; this._clear(); this._parentElement.insertAdjacentHTML('afterbegin', markup); } renderError(message = this._errorMessage) { const markup = `

    ${message}

    `; this._clear(); this._parentElement.insertAdjacentHTML('afterbegin', markup); } renderMessage(message = this._message) { const markup = `

    ${message}

    `; this._clear(); this._parentElement.insertAdjacentHTML('afterbegin', markup); } } ================================================ FILE: 18-forkify/final/src/js/views/addRecipeView.js ================================================ import View from './View.js'; import icons from 'url:../../img/icons.svg'; // Parcel 2 class AddRecipeView extends View { _parentElement = document.querySelector('.upload'); _message = 'Recipe was successfully uploaded :)'; _window = document.querySelector('.add-recipe-window'); _overlay = document.querySelector('.overlay'); _btnOpen = document.querySelector('.nav__btn--add-recipe'); _btnClose = document.querySelector('.btn--close-modal'); constructor() { super(); this._addHandlerShowWindow(); this._addHandlerHideWindow(); } toggleWindow() { this._overlay.classList.toggle('hidden'); this._window.classList.toggle('hidden'); } _addHandlerShowWindow() { this._btnOpen.addEventListener('click', this.toggleWindow.bind(this)); } _addHandlerHideWindow() { this._btnClose.addEventListener('click', this.toggleWindow.bind(this)); this._overlay.addEventListener('click', this.toggleWindow.bind(this)); } addHandlerUpload(handler) { this._parentElement.addEventListener('submit', function (e) { e.preventDefault(); const dataArr = [...new FormData(this)]; const data = Object.fromEntries(dataArr); handler(data); }); } _generateMarkup() {} } export default new AddRecipeView(); ================================================ FILE: 18-forkify/final/src/js/views/bookmarksView.js ================================================ import View from './View.js'; import previewView from './previewView.js'; import icons from 'url:../../img/icons.svg'; // Parcel 2 class BookmarksView extends View { _parentElement = document.querySelector('.bookmarks__list'); _errorMessage = 'No bookmarks yet. Find a nice recipe and bookmark it ;)'; _message = ''; addHandlerRender(handler) { window.addEventListener('load', handler); } _generateMarkup() { return this._data .map(bookmark => previewView.render(bookmark, false)) .join(''); } } export default new BookmarksView(); ================================================ FILE: 18-forkify/final/src/js/views/paginationView.js ================================================ import View from './View.js'; import icons from 'url:../../img/icons.svg'; // Parcel 2 class PaginationView extends View { _parentElement = document.querySelector('.pagination'); addHandlerClick(handler) { this._parentElement.addEventListener('click', function (e) { const btn = e.target.closest('.btn--inline'); if (!btn) return; const goToPage = +btn.dataset.goto; handler(goToPage); }); } _generateMarkup() { const curPage = this._data.page; const numPages = Math.ceil( this._data.results.length / this._data.resultsPerPage ); // Page 1, and there are other pages if (curPage === 1 && numPages > 1) { return ` `; } // Last page if (curPage === numPages && numPages > 1) { return ` `; } // Other page if (curPage < numPages) { return ` `; } // Page 1, and there are NO other pages return ''; } } export default new PaginationView(); ================================================ FILE: 18-forkify/final/src/js/views/previewView.js ================================================ import View from './View.js'; import icons from 'url:../../img/icons.svg'; // Parcel 2 class PreviewView extends View { _parentElement = ''; _generateMarkup() { const id = window.location.hash.slice(1); return `
  • ${this._data.title}

    ${this._data.title}

    ${this._data.publisher}

  • `; } } export default new PreviewView(); ================================================ FILE: 18-forkify/final/src/js/views/recipeView.js ================================================ import View from './View.js'; // import icons from '../img/icons.svg'; // Parcel 1 import icons from 'url:../../img/icons.svg'; // Parcel 2 import { Fraction } from 'fractional'; class RecipeView extends View { _parentElement = document.querySelector('.recipe'); _errorMessage = 'We could not find that recipe. Please try another one!'; _message = ''; addHandlerRender(handler) { ['hashchange', 'load'].forEach(ev => window.addEventListener(ev, handler)); } addHandlerUpdateServings(handler) { this._parentElement.addEventListener('click', function (e) { const btn = e.target.closest('.btn--update-servings'); if (!btn) return; const { updateTo } = btn.dataset; if (+updateTo > 0) handler(+updateTo); }); } addHandlerAddBookmark(handler) { this._parentElement.addEventListener('click', function (e) { const btn = e.target.closest('.btn--bookmark'); if (!btn) return; handler(); }); } _generateMarkup() { return `
    ${
      this._data.title
    }

    ${this._data.title}

    ${ this._data.cookingTime } minutes
    ${ this._data.servings } servings

    Recipe ingredients

      ${this._data.ingredients.map(this._generateMarkupIngredient).join('')}

    How to cook it

    This recipe was carefully designed and tested by ${ this._data.publisher }. Please check out directions at their website.

    Directions
    `; } _generateMarkupIngredient(ing) { return `
  • ${ ing.quantity ? new Fraction(ing.quantity).toString() : '' }
    ${ing.unit} ${ing.description}
  • `; } } export default new RecipeView(); ================================================ FILE: 18-forkify/final/src/js/views/resultsView.js ================================================ import View from './View.js'; import previewView from './previewView.js'; import icons from 'url:../../img/icons.svg'; // Parcel 2 class ResultsView extends View { _parentElement = document.querySelector('.results'); _errorMessage = 'No recipes found for your query! Please try again ;)'; _message = ''; _generateMarkup() { return this._data.map(result => previewView.render(result, false)).join(''); } } export default new ResultsView(); ================================================ FILE: 18-forkify/final/src/js/views/searchView.js ================================================ class SearchView { _parentEl = document.querySelector('.search'); getQuery() { const query = this._parentEl.querySelector('.search__field').value; this._clearInput(); return query; } _clearInput() { this._parentEl.querySelector('.search__field').value = ''; } addHandlerSearch(handler) { this._parentEl.addEventListener('submit', function (e) { e.preventDefault(); handler(); }); } } export default new SearchView(); ================================================ FILE: 18-forkify/final/src/sass/_base.scss ================================================ // $color-primary: #f59a83; $color-primary: #f38e82; $color-grad-1: #fbdb89; $color-grad-2: #f48982; $color-grey-light-1: #f9f5f3; // Light background $color-grey-light-2: #f2efee; // Light lines $color-grey-light-3: #d3c7c3; // Light text (placeholder) $color-grey-dark-1: #615551; // Normal text $color-grey-dark-2: #918581; // Lighter text $gradient: linear-gradient(to right bottom, $color-grad-1, $color-grad-2); $bp-large: 78.15em; // 1250px $bp-medium: 61.25em; // 980px $bp-small: 37.5em; // 600px $bp-smallest: 31.25em; // 500px * { margin: 0; padding: 0; } *, *::before, *::after { box-sizing: inherit; } html { box-sizing: border-box; font-size: 62.5%; @media only screen and (max-width: $bp-medium) { font-size: 50%; } } body { font-family: 'Nunito Sans', sans-serif; font-weight: 400; line-height: 1.6; color: $color-grey-dark-1; background-image: $gradient; background-size: cover; background-repeat: no-repeat; min-height: calc(100vh - 2 * 4vw); } .container { max-width: 120rem; min-height: 117rem; margin: 4vw auto; background-color: #fff; border-radius: 9px; overflow: hidden; box-shadow: 0 2rem 6rem 0.5rem rgba($color-grey-dark-1, 0.2); display: grid; grid-template-rows: 10rem minmax(100rem, auto); grid-template-columns: 1fr 2fr; grid-template-areas: 'head head' 'list recipe'; @media only screen and (max-width: $bp-large) { max-width: 100%; margin: 0; border-radius: 0; } } ================================================ FILE: 18-forkify/final/src/sass/_components.scss ================================================ %btn { background-image: $gradient; border-radius: 10rem; border: none; text-transform: uppercase; color: #fff; cursor: pointer; display: flex; align-items: center; transition: all 0.2s; &:hover { transform: scale(1.05); } &:focus { outline: none; } & > *:first-child { margin-right: 1rem; } } .btn { @extend %btn; padding: 1.5rem 4rem; font-size: 1.5rem; font-weight: 600; svg { height: 2.25rem; width: 2.25rem; fill: currentColor; } } .btn--small { &, &:link, &:visited { @extend %btn; font-size: 1.4rem; font-weight: 600; padding: 1.25rem 2.25rem; text-decoration: none; svg { height: 1.75rem; width: 1.75rem; fill: currentColor; } } } .btn--inline { color: $color-primary; font-size: 1.3rem; font-weight: 600; border: none; background-color: $color-grey-light-1; padding: 0.8rem 1.2rem; border-radius: 10rem; cursor: pointer; display: flex; align-items: center; transition: all 0.2s; svg { height: 1.6rem; width: 1.6rem; fill: currentColor; margin: 0 0.2rem; } span { margin: 0 0.4rem; } &:hover { color: $color-grad-2; background-color: $color-grey-light-2; } &:focus { outline: none; } } .btn--round { background-image: $gradient; border-radius: 50%; border: none; cursor: pointer; height: 4.5rem; width: 4.5rem; // margin-left: auto; transition: all 0.2s; display: flex; align-items: center; justify-content: center; &:hover { transform: scale(1.07); } &:focus { outline: none; } svg { height: 2.5rem; width: 2.5rem; fill: #fff; } } .btn--tiny { height: 2rem; width: 2rem; border: none; background: none; cursor: pointer; svg { height: 100%; width: 100%; fill: $color-primary; transition: all 0.3s; } &:focus { outline: none; } &:hover svg { fill: $color-grad-2; transform: translateY(-1px); } &:active svg { fill: $color-grad-2; transform: translateY(0); } &:not(:last-child) { margin-right: 0.3rem; } } .heading--2 { font-size: 2rem; font-weight: 700; color: $color-primary; text-transform: uppercase; margin-bottom: 2.5rem; text-align: center; // transform: skewY(-3deg); } .link:link, .link:visited { color: $color-grey-dark-2; } .spinner { margin: 5rem auto; text-align: center; svg { height: 6rem; width: 6rem; fill: $color-primary; animation: rotate 2s infinite linear; } } @keyframes rotate { 0% { transform: rotate(0); } 100% { transform: rotate(360deg); } } .message, .error { max-width: 40rem; margin: 0 auto; padding: 5rem 4rem; display: flex; svg { height: 3rem; width: 3rem; fill: $color-primary; transform: translateY(-0.3rem); } p { margin-left: 1.5rem; font-size: 1.8rem; line-height: 1.5; font-weight: 600; } } ================================================ FILE: 18-forkify/final/src/sass/_header.scss ================================================ .header { grid-area: head; background-color: $color-grey-light-1; display: flex; align-items: center; justify-content: space-between; &__logo { margin-left: 4rem; height: 4.6rem; display: block; } } .search { background-color: #fff; border-radius: 10rem; display: flex; align-items: center; padding-left: 3rem; transition: all 0.3s; &:focus-within { transform: translateY(-2px); box-shadow: 0 0.7rem 3rem rgba($color-grey-dark-1, 0.08); } &__field { border: none; background: none; font-family: inherit; color: inherit; font-size: 1.7rem; width: 30rem; &:focus { outline: none; } &::placeholder { color: $color-grey-light-3; } @media only screen and (max-width: $bp-medium) { width: auto; &::placeholder { color: white; } } } &__btn { font-weight: 600; font-family: inherit; } } .nav { align-self: stretch; margin-right: 2.5rem; &__list { list-style: none; display: flex; height: 100%; } &__item { position: relative; } &__btn { height: 100%; font-family: inherit; color: inherit; font-size: 1.4rem; font-weight: 700; text-transform: uppercase; background: none; border: none; cursor: pointer; padding: 0 1.5rem; transition: all 0.3s; display: flex; align-items: center; svg { height: 2.4rem; width: 2.4rem; fill: $color-primary; margin-right: 0.7rem; transform: translateY(-1px); } &:focus { outline: none; } &:hover { background-color: $color-grey-light-2; } } } .bookmarks { padding: 1rem 0; position: absolute; // right: 0; right: -2.5rem; z-index: 10; width: 40rem; background-color: #fff; box-shadow: 0 0.8rem 5rem 2rem rgba($color-grey-dark-1, 0.1); visibility: hidden; opacity: 0; transition: all 0.5s 0.2s; &__list { list-style: none; } &__field { cursor: pointer; padding: 0 4rem; display: flex; align-items: center; height: 100%; transition: all 0.3s; &:hover { background-color: $color-grey-light-2; } } &:hover, .nav__btn--bookmarks:hover + & { visibility: visible; opacity: 1; } } ================================================ FILE: 18-forkify/final/src/sass/_preview.scss ================================================ .preview { &__link { &:link, &:visited { display: flex; align-items: center; padding: 1.5rem 3.25rem; transition: all 0.3s; border-right: 1px solid #fff; text-decoration: none; } &:hover { background-color: $color-grey-light-1; transform: translateY(-2px); } &--active { background-color: $color-grey-light-1; } } &__fig { flex: 0 0 5.8rem; border-radius: 50%; overflow: hidden; height: 5.8rem; margin-right: 2rem; position: relative; backface-visibility: hidden; &::before { content: ''; display: block; height: 100%; width: 100%; position: absolute; top: 0; left: 0; background-image: linear-gradient( to right bottom, $color-grad-1, $color-grad-2 ); opacity: 0.4; } img { display: block; width: 100%; height: 100%; object-fit: cover; transition: all 0.3s; } } &__data { display: grid; width: 100%; grid-template-columns: 1fr 2rem; row-gap: 0.1rem; align-items: center; } &__title { grid-column: 1/-1; font-size: 1.45rem; color: $color-primary; text-transform: uppercase; font-weight: 600; // This is how text is truncated! text-overflow: ellipsis; max-width: 25rem; white-space: nowrap; overflow: hidden; } &__publisher { font-size: 1.15rem; color: $color-grey-dark-2; text-transform: uppercase; font-weight: 600; } &__user-generated { background-color: darken($color-grey-light-2, 2%); display: flex; align-items: center; justify-content: center; height: 2rem; width: 2rem; border-radius: 10rem; margin-left: auto; margin-right: 1.75rem; svg { height: 1.2rem; width: 1.2rem; fill: $color-primary; } } } ================================================ FILE: 18-forkify/final/src/sass/_recipe.scss ================================================ .recipe { background-color: $color-grey-light-1; /////////// // FIGURE &__fig { height: 32rem; position: relative; // transform: scale(1.04) translateY(-1px); transform-origin: top; &::before { content: ''; display: block; height: 100%; width: 100%; position: absolute; top: 0; left: 0; background-image: linear-gradient( to right bottom, $color-grad-1, $color-grad-2 ); opacity: 0.6; } } &__img { width: 100%; display: block; height: 100%; object-fit: cover; } &__title { position: absolute; bottom: 0; left: 50%; transform: translate(-50%, 20%) skewY(-6deg); color: #fff; font-weight: 700; font-size: 3.25rem; text-transform: uppercase; width: 50%; line-height: 1.95; text-align: center; span { -webkit-box-decoration-break: clone; box-decoration-break: clone; padding: 1.3rem 2rem; background-image: linear-gradient( to right bottom, $color-grad-1, $color-grad-2 ); } @media only screen and (max-width: $bp-medium) { width: 70%; } } /////////// // DETAILS &__details { display: flex; align-items: center; padding: 7.5rem 8rem 3.5rem 8rem; } &__info { font-size: 1.65rem; text-transform: uppercase; display: flex; align-items: center; &:not(:last-child) { margin-right: 4.5rem; } } &__info-icon { height: 2.35rem; width: 2.35rem; fill: $color-primary; margin-right: 1.15rem; } &__info-data { margin-right: 0.5rem; font-weight: 700; } &__info-buttons { display: flex; margin-left: 1.6rem; transform: translateY(-1px); } &__user-generated { background-color: darken($color-grey-light-2, 2%); display: flex; align-items: center; justify-content: center; height: 4rem; width: 4rem; border-radius: 10rem; margin-left: auto; margin-right: 1.75rem; svg { height: 2.25rem; width: 2.25rem; fill: $color-primary; } } /////////// // INGREDIENTS &__ingredients { padding: 5rem 8rem; font-size: 1.6rem; line-height: 1.4; background-color: $color-grey-light-2; display: flex; flex-direction: column; align-items: center; } &__ingredient-list { display: grid; grid-template-columns: 1fr 1fr; gap: 2.5rem 3rem; list-style: none; } &__ingredient { display: flex; } &__icon { height: 2rem; width: 2rem; fill: $color-primary; margin-right: 1.1rem; flex: 0 0 auto; margin-top: 0.1rem; } &__quantity { margin-right: 0.5rem; flex: 0 0 auto; } /////////// // DIRECTIONS &__directions { padding: 5rem 10rem; padding-bottom: 5rem; display: flex; flex-direction: column; align-items: center; } &__directions-text { font-size: 1.7rem; text-align: center; margin-bottom: 3.5rem; color: $color-grey-dark-2; } &__publisher { font-weight: 700; } } ================================================ FILE: 18-forkify/final/src/sass/_searchResults.scss ================================================ .search-results { padding: 3rem 0; display: flex; flex-direction: column; } .results { list-style: none; margin-bottom: 2rem; } .pagination { margin-top: auto; padding: 0 3.5rem; &::after { content: ''; display: table; clear: both; } &__btn { &--prev { float: left; } &--next { float: right; } } } .copyright { color: $color-grey-dark-2; font-size: 1.2rem; padding: 0 3.5rem; margin-top: 4rem; .twitter-link:link, .twitter-link:visited { color: $color-grey-dark-2; } } ================================================ FILE: 18-forkify/final/src/sass/_upload.scss ================================================ .add-recipe-window { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 100rem; background-color: white; border-radius: 9px; padding: 5rem 6rem; box-shadow: 0 4rem 6rem rgba(0, 0, 0, 0.25); z-index: 1000; transition: all 0.5s; .btn--close-modal { font-family: inherit; color: inherit; position: absolute; top: 0.5rem; right: 1.6rem; font-size: 3.5rem; cursor: pointer; border: none; background: none; } } .overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.4); backdrop-filter: blur(4px); z-index: 100; transition: all 0.5s; } .hidden { visibility: hidden; opacity: 0; } .upload { display: grid; grid-template-columns: 1fr 1fr; gap: 4rem 6rem; &__column { display: grid; grid-template-columns: 1fr 2.8fr; align-items: center; gap: 1.5rem; & label { font-size: 1.5rem; font-weight: 600; color: inherit; } & input { font-size: 1.5rem; padding: 0.8rem 1rem; border: 1px solid #ddd; border-radius: 0.5rem; transition: all 0.2s; &::placeholder { color: $color-grey-light-3; } &:focus { outline: none; border: 1px solid $color-primary; background-color: $color-grey-light-1; } } & button { grid-column: 1 / span 2; justify-self: center; margin-top: 1rem; } } &__heading { font-size: 2.25rem; font-weight: 700; text-transform: uppercase; margin-bottom: 1rem; grid-column: 1/-1; } &__btn { grid-column: 1 / -1; justify-self: center; } } ================================================ FILE: 18-forkify/final/src/sass/main.scss ================================================ @import 'base'; @import 'components'; @import 'header'; @import 'preview'; @import 'searchResults'; @import 'recipe'; @import 'upload'; ================================================ FILE: 18-forkify/starter/.prettierrc ================================================ { "singleQuote": true, "arrowParens": "avoid" } ================================================ FILE: 18-forkify/starter/index.html ================================================ forkify // Search over 1,000,000 recipes

    Start by searching for a recipe or an ingredient. Have fun!

    ================================================ FILE: 18-forkify/starter/src/js/controller.js ================================================ const recipeContainer = document.querySelector('.recipe'); const timeout = function (s) { return new Promise(function (_, reject) { setTimeout(function () { reject(new Error(`Request took too long! Timeout after ${s} second`)); }, s * 1000); }); }; // NEW API URL (instead of the one shown in the video) // https://forkify-api.jonas.io /////////////////////////////////////// ================================================ FILE: 18-forkify/starter/src/sass/_base.scss ================================================ // $color-primary: #f59a83; $color-primary: #f38e82; $color-grad-1: #fbdb89; $color-grad-2: #f48982; $color-grey-light-1: #f9f5f3; // Light background $color-grey-light-2: #f2efee; // Light lines $color-grey-light-3: #d3c7c3; // Light text (placeholder) $color-grey-dark-1: #615551; // Normal text $color-grey-dark-2: #918581; // Lighter text $gradient: linear-gradient(to right bottom, $color-grad-1, $color-grad-2); $bp-large: 78.15em; // 1250px $bp-medium: 61.25em; // 980px $bp-small: 37.5em; // 600px $bp-smallest: 31.25em; // 500px * { margin: 0; padding: 0; } *, *::before, *::after { box-sizing: inherit; } html { box-sizing: border-box; font-size: 62.5%; @media only screen and (max-width: $bp-medium) { font-size: 50%; } } body { font-family: 'Nunito Sans', sans-serif; font-weight: 400; line-height: 1.6; color: $color-grey-dark-1; background-image: $gradient; background-size: cover; background-repeat: no-repeat; min-height: calc(100vh - 2 * 4vw); } .container { max-width: 120rem; min-height: 117rem; margin: 4vw auto; background-color: #fff; border-radius: 9px; overflow: hidden; box-shadow: 0 2rem 6rem 0.5rem rgba($color-grey-dark-1, 0.2); display: grid; grid-template-rows: 10rem minmax(100rem, auto); grid-template-columns: 1fr 2fr; grid-template-areas: 'head head' 'list recipe'; @media only screen and (max-width: $bp-large) { max-width: 100%; margin: 0; border-radius: 0; } } ================================================ FILE: 18-forkify/starter/src/sass/_components.scss ================================================ %btn { background-image: $gradient; border-radius: 10rem; border: none; text-transform: uppercase; color: #fff; cursor: pointer; display: flex; align-items: center; transition: all 0.2s; &:hover { transform: scale(1.05); } &:focus { outline: none; } & > *:first-child { margin-right: 1rem; } } .btn { @extend %btn; padding: 1.5rem 4rem; font-size: 1.5rem; font-weight: 600; svg { height: 2.25rem; width: 2.25rem; fill: currentColor; } } .btn--small { &, &:link, &:visited { @extend %btn; font-size: 1.4rem; font-weight: 600; padding: 1.25rem 2.25rem; text-decoration: none; svg { height: 1.75rem; width: 1.75rem; fill: currentColor; } } } .btn--inline { color: $color-primary; font-size: 1.3rem; font-weight: 600; border: none; background-color: $color-grey-light-1; padding: 0.8rem 1.2rem; border-radius: 10rem; cursor: pointer; display: flex; align-items: center; transition: all 0.2s; svg { height: 1.6rem; width: 1.6rem; fill: currentColor; margin: 0 0.2rem; } span { margin: 0 0.4rem; } &:hover { color: $color-grad-2; background-color: $color-grey-light-2; } &:focus { outline: none; } } .btn--round { background-image: $gradient; border-radius: 50%; border: none; cursor: pointer; height: 4.5rem; width: 4.5rem; // margin-left: auto; transition: all 0.2s; display: flex; align-items: center; justify-content: center; &:hover { transform: scale(1.07); } &:focus { outline: none; } svg { height: 2.5rem; width: 2.5rem; fill: #fff; } } .btn--tiny { height: 2rem; width: 2rem; border: none; background: none; cursor: pointer; svg { height: 100%; width: 100%; fill: $color-primary; transition: all 0.3s; } &:focus { outline: none; } &:hover svg { fill: $color-grad-2; transform: translateY(-1px); } &:active svg { fill: $color-grad-2; transform: translateY(0); } &:not(:last-child) { margin-right: 0.3rem; } } .heading--2 { font-size: 2rem; font-weight: 700; color: $color-primary; text-transform: uppercase; margin-bottom: 2.5rem; text-align: center; // transform: skewY(-3deg); } .link:link, .link:visited { color: $color-grey-dark-2; } .spinner { margin: 5rem auto; text-align: center; svg { height: 6rem; width: 6rem; fill: $color-primary; animation: rotate 2s infinite linear; } } @keyframes rotate { 0% { transform: rotate(0); } 100% { transform: rotate(360deg); } } .message, .error { max-width: 40rem; margin: 0 auto; padding: 5rem 4rem; display: flex; svg { height: 3rem; width: 3rem; fill: $color-primary; transform: translateY(-0.3rem); } p { margin-left: 1.5rem; font-size: 1.8rem; line-height: 1.5; font-weight: 600; } } ================================================ FILE: 18-forkify/starter/src/sass/_header.scss ================================================ .header { grid-area: head; background-color: $color-grey-light-1; display: flex; align-items: center; justify-content: space-between; &__logo { margin-left: 4rem; height: 4.6rem; display: block; } } .search { background-color: #fff; border-radius: 10rem; display: flex; align-items: center; padding-left: 3rem; transition: all 0.3s; &:focus-within { transform: translateY(-2px); box-shadow: 0 0.7rem 3rem rgba($color-grey-dark-1, 0.08); } &__field { border: none; background: none; font-family: inherit; color: inherit; font-size: 1.7rem; width: 30rem; &:focus { outline: none; } &::placeholder { color: $color-grey-light-3; } @media only screen and (max-width: $bp-medium) { width: auto; &::placeholder { color: white; } } } &__btn { font-weight: 600; font-family: inherit; } } .nav { align-self: stretch; margin-right: 2.5rem; &__list { list-style: none; display: flex; height: 100%; } &__item { position: relative; } &__btn { height: 100%; font-family: inherit; color: inherit; font-size: 1.4rem; font-weight: 700; text-transform: uppercase; background: none; border: none; cursor: pointer; padding: 0 1.5rem; transition: all 0.3s; display: flex; align-items: center; svg { height: 2.4rem; width: 2.4rem; fill: $color-primary; margin-right: 0.7rem; transform: translateY(-1px); } &:focus { outline: none; } &:hover { background-color: $color-grey-light-2; } } } .bookmarks { padding: 1rem 0; position: absolute; // right: 0; right: -2.5rem; z-index: 10; width: 40rem; background-color: #fff; box-shadow: 0 0.8rem 5rem 2rem rgba($color-grey-dark-1, 0.1); visibility: hidden; opacity: 0; transition: all 0.5s 0.2s; &__list { list-style: none; } &__field { cursor: pointer; padding: 0 4rem; display: flex; align-items: center; height: 100%; transition: all 0.3s; &:hover { background-color: $color-grey-light-2; } } &:hover, .nav__btn--bookmarks:hover + & { visibility: visible; opacity: 1; } } ================================================ FILE: 18-forkify/starter/src/sass/_preview.scss ================================================ .preview { &__link { &:link, &:visited { display: flex; align-items: center; padding: 1.5rem 3.25rem; transition: all 0.3s; border-right: 1px solid #fff; text-decoration: none; } &:hover { background-color: $color-grey-light-1; transform: translateY(-2px); } &--active { background-color: $color-grey-light-1; } } &__fig { flex: 0 0 5.8rem; border-radius: 50%; overflow: hidden; height: 5.8rem; margin-right: 2rem; position: relative; backface-visibility: hidden; &::before { content: ''; display: block; height: 100%; width: 100%; position: absolute; top: 0; left: 0; background-image: linear-gradient( to right bottom, $color-grad-1, $color-grad-2 ); opacity: 0.4; } img { display: block; width: 100%; height: 100%; object-fit: cover; transition: all 0.3s; } } &__data { display: grid; width: 100%; grid-template-columns: 1fr 2rem; row-gap: 0.1rem; align-items: center; } &__title { grid-column: 1/-1; font-size: 1.45rem; color: $color-primary; text-transform: uppercase; font-weight: 600; // This is how text is truncated! text-overflow: ellipsis; max-width: 25rem; white-space: nowrap; overflow: hidden; } &__publisher { font-size: 1.15rem; color: $color-grey-dark-2; text-transform: uppercase; font-weight: 600; } &__user-generated { background-color: darken($color-grey-light-2, 2%); display: flex; align-items: center; justify-content: center; height: 2rem; width: 2rem; border-radius: 10rem; margin-left: auto; margin-right: 1.75rem; svg { height: 1.2rem; width: 1.2rem; fill: $color-primary; } } } ================================================ FILE: 18-forkify/starter/src/sass/_recipe.scss ================================================ .recipe { background-color: $color-grey-light-1; /////////// // FIGURE &__fig { height: 32rem; position: relative; // transform: scale(1.04) translateY(-1px); transform-origin: top; &::before { content: ''; display: block; height: 100%; width: 100%; position: absolute; top: 0; left: 0; background-image: linear-gradient( to right bottom, $color-grad-1, $color-grad-2 ); opacity: 0.6; } } &__img { width: 100%; display: block; height: 100%; object-fit: cover; } &__title { position: absolute; bottom: 0; left: 50%; transform: translate(-50%, 20%) skewY(-6deg); color: #fff; font-weight: 700; font-size: 3.25rem; text-transform: uppercase; width: 50%; line-height: 1.95; text-align: center; span { -webkit-box-decoration-break: clone; box-decoration-break: clone; padding: 1.3rem 2rem; background-image: linear-gradient( to right bottom, $color-grad-1, $color-grad-2 ); } @media only screen and (max-width: $bp-medium) { width: 70%; } } /////////// // DETAILS &__details { display: flex; align-items: center; padding: 7.5rem 8rem 3.5rem 8rem; } &__info { font-size: 1.65rem; text-transform: uppercase; display: flex; align-items: center; &:not(:last-child) { margin-right: 4.5rem; } } &__info-icon { height: 2.35rem; width: 2.35rem; fill: $color-primary; margin-right: 1.15rem; } &__info-data { margin-right: 0.5rem; font-weight: 700; } &__info-buttons { display: flex; margin-left: 1.6rem; transform: translateY(-1px); } &__user-generated { background-color: darken($color-grey-light-2, 2%); display: flex; align-items: center; justify-content: center; height: 4rem; width: 4rem; border-radius: 10rem; margin-left: auto; margin-right: 1.75rem; svg { height: 2.25rem; width: 2.25rem; fill: $color-primary; } } /////////// // INGREDIENTS &__ingredients { padding: 5rem 8rem; font-size: 1.6rem; line-height: 1.4; background-color: $color-grey-light-2; display: flex; flex-direction: column; align-items: center; } &__ingredient-list { display: grid; grid-template-columns: 1fr 1fr; gap: 2.5rem 3rem; list-style: none; } &__ingredient { display: flex; } &__icon { height: 2rem; width: 2rem; fill: $color-primary; margin-right: 1.1rem; flex: 0 0 auto; margin-top: 0.1rem; } &__quantity { margin-right: 0.5rem; flex: 0 0 auto; } /////////// // DIRECTIONS &__directions { padding: 5rem 10rem; padding-bottom: 5rem; display: flex; flex-direction: column; align-items: center; } &__directions-text { font-size: 1.7rem; text-align: center; margin-bottom: 3.5rem; color: $color-grey-dark-2; } &__publisher { font-weight: 700; } } ================================================ FILE: 18-forkify/starter/src/sass/_searchResults.scss ================================================ .search-results { padding: 3rem 0; display: flex; flex-direction: column; } .results { list-style: none; margin-bottom: 2rem; } .pagination { margin-top: auto; padding: 0 3.5rem; &::after { content: ''; display: table; clear: both; } &__btn { &--prev { float: left; } &--next { float: right; } } } .copyright { color: $color-grey-dark-2; font-size: 1.2rem; padding: 0 3.5rem; margin-top: 4rem; .twitter-link:link, .twitter-link:visited { color: $color-grey-dark-2; } } ================================================ FILE: 18-forkify/starter/src/sass/_upload.scss ================================================ .add-recipe-window { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 100rem; background-color: white; border-radius: 9px; padding: 5rem 6rem; box-shadow: 0 4rem 6rem rgba(0, 0, 0, 0.25); z-index: 1000; transition: all 0.5s; .btn--close-modal { font-family: inherit; color: inherit; position: absolute; top: 0.5rem; right: 1.6rem; font-size: 3.5rem; cursor: pointer; border: none; background: none; } } .overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.4); backdrop-filter: blur(4px); z-index: 100; transition: all 0.5s; } .hidden { visibility: hidden; opacity: 0; } .upload { display: grid; grid-template-columns: 1fr 1fr; gap: 4rem 6rem; &__column { display: grid; grid-template-columns: 1fr 2.8fr; align-items: center; gap: 1.5rem; & label { font-size: 1.5rem; font-weight: 600; color: inherit; } & input { font-size: 1.5rem; padding: 0.8rem 1rem; border: 1px solid #ddd; border-radius: 0.5rem; transition: all 0.2s; &::placeholder { color: $color-grey-light-3; } &:focus { outline: none; border: 1px solid $color-primary; background-color: $color-grey-light-1; } } & button { grid-column: 1 / span 2; justify-self: center; margin-top: 1rem; } } &__heading { font-size: 2.25rem; font-weight: 700; text-transform: uppercase; margin-bottom: 1rem; grid-column: 1/-1; } &__btn { grid-column: 1 / -1; justify-self: center; } } ================================================ FILE: 18-forkify/starter/src/sass/main.scss ================================================ @import 'base'; @import 'components'; @import 'header'; @import 'preview'; @import 'searchResults'; @import 'recipe'; @import 'upload'; ================================================ FILE: README.md ================================================ # Course Material and FAQ for my Complete JavaScript Course This branch of the repo contains starter files and final code for all sections and projects of the course, **exactly as shown in the videos**. Use starter code to start each section, and **final code to compare it with your own code whenever something doesn't work**! There is also a the [updates-and-fixes](https://github.com/jonasschmedtmann/complete-javascript-course/tree/updates-and-fixes) branch which is kept up-to-date with **latest package updates and important bugfixes 🐛** 🚨 **_Please read the following Frequently Asked Questions (FAQ) carefully before starting the course_** 🚨 ## FAQ ### Q1: How do I download the files? **A:** If you're new to GitHub and just want to download the entire code, hit the green button saying "Code", and then choose the "Download ZIP" option. If you can't see the button (on mobile), use [this link](https://github.com/jonasschmedtmann/complete-javascript-course/archive/master.zip) instead. ### Q2: I'm stuck! Where do I get help? **A:** Have you actually tried to fix the problem on your own? **Have you compared your code to the final code?** If you failed fixing your problem, please **post a detailed description of the problem to the Q&A area of that video over at Udemy**, along with a [codepen](https://codepen.io/pen/) containing your code. You will get help there. Please don't send me a personal message or email to fix coding problems. ### Q3: What VSCode theme are you using? **A:** I use Monokai Pro for all my coding and course production. It's a paid theme (I', **not** affiliated with them), but you can actually use the free demo version forever 😅 ### Q4: Can I see a final version of the course projects? **A:** Sure! Here you go: - [Pig Game](https://pig-game-v2.netlify.app) (DOM Manipulation) - [Bankist](https://bankist.netlify.app/) (Arrays, Numbers, Dates, Timers. Fake "log in" with user `js` and PIN `1111`) - [Bankist Site](https://bankist-dom.netlify.app/) (Advanced DOM and Events) - [Mapty](https://mapty.netlify.app/) (OOP, Geolocation, Project planning) - [forkify](https://forkify-v2.jonas.io/) (Final advanced project) ### Q5: Videos don't load, can you fix it? **A:** Unfortunately, there is nothing I can do about it. The course is hosted on Udemy, and sometimes they have technical issues like this. Please just come back a bit later or [contact their support team](https://support.udemy.com/hc/en-us). ### Q6: Videos are blurred / have low quality, can you fix it? **A:** Please open video settings and change the quality from 'Auto' to another value, for example 720p. If that doesn't help, please [contact the Udemy support team](https://support.udemy.com/hc/en-us). ### Q7: Are the videos downloadable? **A:** Videos used to be downloadable, but unfortunately, Udemy has disabled video downloads on all my courses in order to fight piracy. There is nothing I can do about this. ### Q8: I want to put these projects in my portfolio. Is that allowed? **A:** Absolutely! Just make sure you actually built them yourself by following the course, and that you understand what you did. What is **not allowed** is that you create your own course/videos/articles based on this course's content! ### Q9: You mentioned your resources page. Where can I find it? **A:** It's on my website at . You can subscribe for updates 😉 ### Q10: I love your courses and want to get updates on new courses. How? **A:** First, you can subscribe to my email list [at my website](http://codingheroes.io/resources). Plus, I make important announcements on twitter [@jonasschmedtman](https://twitter.com/jonasschmedtman), so you should definitely follow me there 🔥 ### Q11: How do I get my certificate of completion? **A:** A certificate of completion is provided by Udemy after you complete 100% of the course. After completing the course, just click on the "Your progress" indicator in the top right-hand corner of the course page. If you want to change your name on the certificate, please [contact the Udemy support team](https://support.udemy.com/hc/en-us). ### Q12: Can you add subtitles in my language? **A:** No. I provide professional English captions, but Udemy is responsible for subtitles in all other languages (automatic translations). So please [contact the Udemy support team](https://support.udemy.com/hc/en-us) to request your own language. ### Q13: Do you accept pull requests? **A:** No, for the simple reason that I want this repository to contain the _exact_ same code that is shown in the videos. _However, please feel free to add an issue if you found one._ ### Q14: I'm looking for the old course version (v1) files. Where can I find them? **A:** They are in this same repo, but in the [v1 branch](https://github.com/jonasschmedtmann/complete-javascript-course/tree/v1). So just go to [v1](https://github.com/jonasschmedtmann/complete-javascript-course/tree/v1), and download the code from there.