From 00fbd0a0c12c5c83191714b8dd8cc79aabc41b63 Mon Sep 17 00:00:00 2001 From: Sergey Melyukov Date: Mon, 27 Feb 2023 12:00:16 +0300 Subject: [PATCH 1/5] week-2 --- projects/array-objects/index.js | 49 +++++++++++++++++ projects/array-objects/index.spec.js | 78 ++++++++++++++++++++++++++++ projects/loft-photo-1/friends.json | 32 ++++++++++++ projects/loft-photo-1/index.js | 0 projects/loft-photo-1/model.js | 9 ++++ projects/loft-photo-1/photos.json | 72 +++++++++++++++++++++++++ projects/loft-photo-1/readme.md | 71 +++++++++++++++++++++++++ 7 files changed, 311 insertions(+) create mode 100644 projects/array-objects/index.js create mode 100644 projects/array-objects/index.spec.js create mode 100644 projects/loft-photo-1/friends.json create mode 100644 projects/loft-photo-1/index.js create mode 100644 projects/loft-photo-1/model.js create mode 100644 projects/loft-photo-1/photos.json create mode 100644 projects/loft-photo-1/readme.md diff --git a/projects/array-objects/index.js b/projects/array-objects/index.js new file mode 100644 index 000000000..ca1ebc4c6 --- /dev/null +++ b/projects/array-objects/index.js @@ -0,0 +1,49 @@ +/* ДЗ 2 - работа с массивами и объектами */ + +/* + Задание 1: + + Напишите аналог встроенного метода forEach для работы с массивами. + Посмотрите как работает forEach и повторите это поведение для массива, который будет передан в параметре array + + Пример: + forEach([1, 2, 3], (el) => console.log(el)); // выведет каждый элемент массива + */ +function forEach() {} + +/* + Задание 2: + + Напишите аналог встроенного метода map для работы с массивами. + Посмотрите как работает map и повторите это поведение для массива, который будет передан в параметре array + + Пример: + const newArray = map([1, 2, 3], (el) => el ** 2); + console.log(newArray); // выведет [1, 4, 9] + */ +function map() {} + +/* + Задание 3: + + Напишите аналог встроенного метода reduce для работы с массивами. + Посмотрите как работает reduce и повторите это поведение для массива, который будет передан в параметре array + + Пример: + const sum = reduce([1, 2, 3], (all, current) => all + current); + console.log(sum); // выведет 6 + */ +function reduce() {} + +/* + Задание 4: + + Функция должна перебрать все свойства объекта, преобразовать их имена в верхний регистр и вернуть в виде массива + + Пример: + const keys = upperProps({ name: 'Сергей', lastName: 'Петров' }); + console.log(keys) // выведет ['NAME', 'LASTNAME'] + */ +function upperProps() {} + +export { forEach, map, reduce, upperProps }; diff --git a/projects/array-objects/index.spec.js b/projects/array-objects/index.spec.js new file mode 100644 index 000000000..eddc0bfdc --- /dev/null +++ b/projects/array-objects/index.spec.js @@ -0,0 +1,78 @@ +import { forEach, map, reduce, upperProps } from './index'; + +describe('ДЗ 3 - объекты и массивы', () => { + describe('forEach', () => { + it('должна вызывать функцию для каждого элемента массива', () => { + const array = [1, 2, 3]; + const fn = jest.fn(); + + forEach(array, fn); + + for (let i = 0; i < array.length; i++) { + expect(fn).nthCalledWith(i + 1, array[i], i, array); + } + }); + }); + + describe('map', () => { + it('должна вызывать функцию для каждого элемента массива и не изменять оригинальный массив', () => { + const originalArray = [4, 5, 6]; + const array = [...originalArray]; + const modified = array.map((el) => el ** 2); + const fn = jest.fn((el) => el ** 2); + + expect(map(array, fn)).toEqual(modified); + expect(array).toEqual(originalArray); + + for (let i = 0; i < array.length; i++) { + expect(fn).nthCalledWith(i + 1, array[i], i, array); + } + }); + }); + + describe('reduce', () => { + it('должна вызывать функцию для каждого элемента и передавать предыдущий результат первым аргументом', () => { + const originalArray = [7, 8, 9]; + const array = [...originalArray]; + const modified = array.reduce((all, current) => all + current); + const fn = jest.fn((all, current) => all + current); + + expect(reduce(array, fn)).toEqual(modified); + expect(array).toEqual(originalArray); + + let sum = array[0]; + + for (let i = 1; i < array.length; i++) { + expect(fn).nthCalledWith(i, sum, array[i], i, array); + sum += array[i]; + } + }); + + it('должна учитывать initial', () => { + const originalArray = [1, 3, 5]; + const array = [...originalArray]; + const modified = array.reduce((all, current) => all + current, 10); + const fn = jest.fn((all, current) => all + current); + + expect(reduce(array, fn, 10)).toEqual(modified); + expect(array).toEqual(originalArray); + + let sum = 10; + + for (let i = 0; i < array.length; i++) { + expect(fn).nthCalledWith(i + 1, sum, array[i], i, array); + sum += array[i]; + } + }); + }); + + describe('upperProps', () => { + it('должна возвращать массив с именами свойств и преобразовывать эти имена в верхний регистр', () => { + const obj = { a: 1, b: 2 }; + const target = ['A', 'B']; + const result = upperProps(obj); + + expect(result).toEqual(target); + }); + }); +}); diff --git a/projects/loft-photo-1/friends.json b/projects/loft-photo-1/friends.json new file mode 100644 index 000000000..b861e0c00 --- /dev/null +++ b/projects/loft-photo-1/friends.json @@ -0,0 +1,32 @@ +[ + { + "id": 0, + "avatar": "https://via.placeholder.com/100?text=avatar", + "firstName": "Adrian", + "lastName": "Norman" + }, + { + "id": 1, + "avatar": "https://via.placeholder.com/100?text=avatar", + "firstName": "Gail", + "lastName": "Norton" + }, + { + "id": 2, + "avatar": "https://via.placeholder.com/100?text=avatar", + "firstName": "Molina", + "lastName": "Rodgers" + }, + { + "id": 3, + "avatar": "https://via.placeholder.com/100?text=avatar", + "firstName": "Adams", + "lastName": "Parrish" + }, + { + "id": 4, + "avatar": "https://via.placeholder.com/100?text=avatar", + "firstName": "Mercer", + "lastName": "Wiggins" + } +] \ No newline at end of file diff --git a/projects/loft-photo-1/index.js b/projects/loft-photo-1/index.js new file mode 100644 index 000000000..e69de29bb diff --git a/projects/loft-photo-1/model.js b/projects/loft-photo-1/model.js new file mode 100644 index 000000000..1e31e33b3 --- /dev/null +++ b/projects/loft-photo-1/model.js @@ -0,0 +1,9 @@ +// eslint-disable-next-line no-unused-vars +import photosDB from './photos.json'; +// eslint-disable-next-line no-unused-vars +import friendsDB from './friends.json'; + +export default { + getRandomElement(array) {}, + getNextPhoto() {}, +}; diff --git a/projects/loft-photo-1/photos.json b/projects/loft-photo-1/photos.json new file mode 100644 index 000000000..a299e6057 --- /dev/null +++ b/projects/loft-photo-1/photos.json @@ -0,0 +1,72 @@ +{ + "0": [ + { + "id": 10, + "url": "https://via.placeholder.com/360x680?text=photo 1 for Adrian Norman" + }, + { + "id": 11, + "url": "https://via.placeholder.com/360x680?text=photo 2 for Adrian Norman" + }, + { + "id": 12, + "url": "https://via.placeholder.com/360x680?text=photo 3 for Adrian Norman" + } + ], + "1": [ + { + "id": 20, + "url": "https://via.placeholder.com/360x680?text=photo 1 for Gail Norton" + }, + { + "id": 21, + "url": "https://via.placeholder.com/360x680?text=photo 2 for Gail Norton" + }, + { + "id": 22, + "url": "https://via.placeholder.com/360x680?text=photo 3 for Gail Norton" + } + ], + "2": [ + { + "id": 30, + "url": "https://via.placeholder.com/360x680?text=photo 1 for Molina Rodgers" + }, + { + "id": 31, + "url": "https://via.placeholder.com/360x680?text=photo 2 for Molina Rodgers" + }, + { + "id": 32, + "url": "https://via.placeholder.com/360x680?text=photo 3 for Molina Rodgers" + } + ], + "3": [ + { + "id": 40, + "url": "https://via.placeholder.com/360x680?text=photo 1 for Adams Parrish" + }, + { + "id": 41, + "url": "https://via.placeholder.com/360x680?text=photo 2 for Adams Parrish" + }, + { + "id": 42, + "url": "https://via.placeholder.com/360x680?text=photo 3 for Adams Parrish" + } + ], + "4": [ + { + "id": 50, + "url": "https://via.placeholder.com/360x680?text=photo 1 for Mercer Wiggins" + }, + { + "id": 51, + "url": "https://via.placeholder.com/360x680?text=photo 2 for Mercer Wiggins" + }, + { + "id": 52, + "url": "https://via.placeholder.com/360x680?text=photo 3 for Mercer Wiggins" + } + ] +} \ No newline at end of file diff --git a/projects/loft-photo-1/readme.md b/projects/loft-photo-1/readme.md new file mode 100644 index 000000000..cd26a9f0e --- /dev/null +++ b/projects/loft-photo-1/readme.md @@ -0,0 +1,71 @@ +## Массивы и объекты + +Реализуйте объект с двумя методами: + +- `getRandomElement(array)` +- `getNextPhoto()` + +### `getRandomElement(array)` + +Метод принимает массив в параметре `array` и должен вернуть **случайный** элемент из этого масства при каждом вызове. + +Например: + +```js +const fruits = ['банан', 'яблоко', 'груша']; + +console.log(getRandomElement(fruits)); // груша +console.log(getRandomElement(fruits)); // банан +console.log(getRandomElement(fruits)); // банан +``` + +Для получения случайного числа может пригодиться метод [Math.random()](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Math/random). Этот метод возвращает случайное число между 0 и 1 (например 0.548) + +Вот так можно получить число от 0 до 10 (например): + +```js +console.log(Math.random() * 10); // 5.754356 +console.log(Math.random() * 10); // 2.12864 +``` + +А при помощи [parseInt](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/parseInt) можно убрать дробную часть: + +```js +console.log(parseInt(Math.random() * 10)); // 8 +console.log(parseInt(Math.random() * 10)); // 3 +``` + +### `getNextPhoto()` + +При каждом вызове метод должен вернуть информацию со случайным другом и случайной его фотографией. + +Информация должна быть возвращена в виде объекта из двух полей: `friend` и `url`. + +Например: + +```js +let photo = getNextPhoto(); + +console.log(photo.friend); // { firstName: 'Иван' } +console.log(photo.url); // https://... + +photo = getNextPhoto(); + +console.log(photo.friend); // { firstName: 'Сергей' } +console.log(photo.url); // https://... +``` + +Пример списка друзей и фотографий можно найти в файлах [friends.json](friends.json) и [photos.json](photos.json). + +В файле [friends.json](friends.json) хранится массив со списком друзей. У каждого друга есть имя, фамилия и идентификатор. + +В файле [photos.json](photos.json) хранится объект, ключами которого являются идентификаторы друзей, а значениями - массив фотографий этого друга. +У каждой фотографии есть идентификатор и url. + +Вот так, например, можно получить все фотографии друга с идентификатором `1`: + +```js +const photosDB = require('./photos.json'); + +console.log(photosDB[1]) // [ ... ] +``` From e2b4474196a8d2238ab56c737102c9d8ce561f05 Mon Sep 17 00:00:00 2001 From: Sergey Melyukov Date: Sun, 5 Mar 2023 21:27:41 +0300 Subject: [PATCH 2/5] week-2 --- projects/exceptions/index.js | 91 +++++++++++++++ projects/exceptions/index.spec.js | 178 ++++++++++++++++++++++++++++++ 2 files changed, 269 insertions(+) create mode 100644 projects/exceptions/index.js create mode 100644 projects/exceptions/index.spec.js diff --git a/projects/exceptions/index.js b/projects/exceptions/index.js new file mode 100644 index 000000000..d09b05496 --- /dev/null +++ b/projects/exceptions/index.js @@ -0,0 +1,91 @@ +/* ДЗ 3 - работа с исключениями и отладчиком */ + +/* + Задание 1: + + 1.1: Функция isAllTrue принимает массив в параметре array и другую функцию в параметре fn. + Нужно по-очереди запустить функцию fn для всех элементов массива. + isAllTrue должна вернуть true только если fn вернула true для всех элементов массива. + Если хотя бы для одного из элементов массива fn вернула false, то и isAllTrue должна вернуть false. + + 1.2: Необходимо выбрасывать исключение в случаях: + - array не массив или пустой массив (с текстом "empty array") + для проверки на массив вам может помочь функция Array.isArray() + - fn не является функцией (с текстом "fn is not a function") + для проверки на функцию вам может помочь оператор typeof + + Запрещено использовать встроенные методы для работы с массивами + + Пример: + isAllTrue([1, 2, 3, 4, 5], n => n < 10) // вернет true (потому что все элементы массива меньше 10) + isAllTrue([100, 2, 3, 4, 5], n => n < 10) // вернет false (потому что как минимум первый элемент больше 10) + */ +function isAllTrue(array, fn) {} + +/* + Задание 2: + + 2.1: Функция isSomeTrue принимает массив в параметре array и функцию в параметре fn. + Нужно по-очереди запустить функцию fn для всех элементов массива. + isSomeTrue должна вернуть true только если fn вернула true хотя бы для одного из элементов массива. + Если fn не вернула true ни для одного элементов массива, то и isSomeTrue должна вернуть false. + + 2.2: Необходимо выбрасывать исключение в случаях: + - array не массив или пустой массив (с текстом "empty array") + для проверки на массив вам может помочь функция Array.isArray() + - fn не является функцией (с текстом "fn is not a function") + для проверки на функцию вам может помочь оператор typeof + + Запрещено использовать встроенные методы для работы с массивами + + Пример: + isSomeTrue([1, 2, 30, 4, 5], n => n > 20) // вернет true (потому что в массиве есть хотя бы один элемент больше 20) + isSomeTrue([1, 2, 3, 4, 5], n => n > 20) // вернет false (потому что в массиве нет ни одного элемента больше 20) + */ +function isSomeTrue(array, fn) {} + +/* + Задание 3: + + 3.1: Функция returnBadArguments принимает заранее неизвестное количество аргументов, первым из которых является функция fn + returnBadArguments должна поочередно запустить fn для каждого переданного аргумента (кроме самой fn) + + 3.2: returnBadArguments должна вернуть массив аргументов, для которых fn выбросила исключение + + 3.3: Необходимо выбрасывать исключение в случаях: + - fn не является функцией (с текстом "fn is not a function") + для проверки на функцию вам может помочь оператор typeof + */ +function returnBadArguments() {} + +/* + Задание 4: + + 4.1: Функция calculator имеет параметр number (по умолчанию - 0) + + 4.2: Функция calculator должна вернуть объект, у которого должно быть несколько методов: + - sum - складывает number с переданными аргументами + - dif - вычитает из number переданные аргументы + - div - делит number на первый аргумент. Результат делится на следующий аргумент (если передан) и так далее + - mul - умножает number на первый аргумент. Результат умножается на следующий аргумент (если передан) и так далее + + Количество передаваемых в методы аргументов заранее неизвестно + + 4.3: Необходимо выбрасывать исключение в случаях: + - number не является числом (с текстом "number is not a number") + - какой-либо из аргументов div является нулем (с текстом "division by 0") + + Пример: + const myCalc = calculator(10); + + console.log(calc.sum(1, 2, 3)); // выведет 16 (10 + 1 + 2 + 3) + console.log(calc.dif(1, 2, 3)); // выведет 5 (10 - 1 - 2 - 3) + console.log(calc.mul(1, 2, 3)); // выведет 60 (10 * 1 * 2 * 3) + console.log(calc.div(2, 2)); // выведет 2.5 (10 / 2 / 2) + console.log(calc.div(2, 0)); // выбросит исключение, потому что один из аргументов равен 0 + */ +function calculator(number) {} + +/* При решении задач, постарайтесь использовать отладчик */ + +export { isAllTrue, isSomeTrue, returnBadArguments, calculator }; diff --git a/projects/exceptions/index.spec.js b/projects/exceptions/index.spec.js new file mode 100644 index 000000000..bb9b4d0c1 --- /dev/null +++ b/projects/exceptions/index.spec.js @@ -0,0 +1,178 @@ +import { calculator, isAllTrue, isSomeTrue, returnBadArguments } from './index'; + +describe('ДЗ 2 - работа с исключениями и отладчиком', () => { + describe('isAllTrue', () => { + it('должна вызывать fn для всех элементов массива', () => { + const array = ['l', 's']; + const pass = []; + + isAllTrue(array, (e) => pass.push(e)); + + expect(pass).toEqual(array); + }); + + it('должна вернуть true, если fn вернула true для всех элементов массива', () => { + const array = [1, 2, 3]; + const result = isAllTrue(array, Number.isFinite); + + expect(result); + }); + + it('должна вернуть false, если fn вернула false хотя бы для одного элемента массива', () => { + const array = [1, 2, 3]; + + array.push('ls'); + const result = isAllTrue(array, Number.isFinite); + + expect(!result); + }); + + it('должна выбросить исключение, если передан пустой массив', () => { + expect(() => isAllTrue([], () => {})).toThrow('empty array'); + }); + + it('должна выбросить исключение, если передан не массив', () => { + expect(() => isAllTrue(':(', () => {})).toThrow('empty array'); + expect(() => isAllTrue({}, () => {})).toThrow('empty array'); + }); + + it('должна выбросить исключение, если fn не функция', () => { + const array = [1, 2, 3]; + + expect(() => isAllTrue(array, ':(')).toThrow('fn is not a function'); + }); + }); + + describe('isSomeTrue', () => { + it('должна вернуть true, если fn вернула true хотя бы для одного элемента массива', () => { + const array = ['l', 's', 3]; + const result = isSomeTrue(array, Number.isFinite); + + expect(result); + }); + + it('должна вернуть false, если fn не вернула true хотя бы для одного элемента массива', () => { + const array = ['l', 's']; + const result = isSomeTrue(array, Number.isFinite); + + expect(!result); + }); + + it('должна выбросить исключение, если передан пустой массив', () => { + expect(() => isSomeTrue([], () => {})).toThrow('empty array'); + }); + + it('должна выбросить исключение, если передан не массив', () => { + expect(() => isSomeTrue(':(', () => {})).toThrow('empty array'); + expect(() => isSomeTrue({}, () => {})).toThrow('empty array'); + }); + + it('должна выбросить исключение, если fn не функция', () => { + const array = [1, 2, 3]; + + expect(() => isSomeTrue(array, ':(')).toThrow('fn is not a function'); + }); + }); + + describe('returnBadArguments', () => { + it('должна вызывать fn для всех элементов массива', () => { + const array = [1, 2, 3]; + const pass = []; + + returnBadArguments((e) => pass.push(e), ...array); + + expect(pass).toEqual(array); + }); + + it('должна вернуть массив с аргументами, для которых fn выбрасила исключение', () => { + const evenNumbers = [2, 4, 6]; + const oddNumbers = [1, 3, 5]; + const fn = (a) => { + if (a % 2 !== 0) { + throw new Error('not even'); + } + }; + const result = returnBadArguments(fn, ...evenNumbers, ...oddNumbers); + + expect(result).toEqual(oddNumbers); + }); + + it('должна вернуть массив пустой массив, если не передано дополнительных аргументов', () => { + const fn = () => ':)'; + const result = returnBadArguments(fn); + + expect(result.length).toBe(0); + }); + + it('должна выбросить исключение, если fn не функция', () => { + expect(() => returnBadArguments(':(')).toThrow('fn is not a function'); + }); + }); + + describe('calculator', () => { + it('должна возвращать объект с методами', () => { + const calc = calculator(); + + expect(Object.keys(calc)).toEqual(['sum', 'dif', 'div', 'mul']); + }); + + it('метод sum должен складывать аргументы', () => { + const initialValue = 20; + const calc = calculator(initialValue); + const args = [1, 2, 3]; + + expect(calc.sum(...args)).toBe( + args.reduce((prev, current) => prev + current, initialValue) + ); + }); + + it('метод dif должен вычитать аргументы', () => { + const initialValue = 10; + const calc = calculator(initialValue); + const args = [1, 2, 3]; + + expect(calc.dif(...args)).toBe( + args.reduce((prev, current) => prev - current, initialValue) + ); + }); + + it('метод div должен делить аргументы', () => { + const initialValue = 20; + const calc = calculator(initialValue); + const args = [1, 2]; + + expect(calc.div(...args)).toBe( + args.reduce((prev, current) => prev / current, initialValue) + ); + }); + + it('метод div должен выбрасывать исключение, если хотя бы один из аргументов равен 0', () => { + const initialValue = 20; + const calc = calculator(initialValue); + const args = [1, 2, 0]; + + expect(() => calc.div(...args)).toThrow('division by 0'); + }); + + it('метод mul должен умножать аргументы', () => { + const initialValue = 10; + const calc = calculator(initialValue); + const args = [1, 2]; + + expect(calc.mul(...args)).toBe( + args.reduce((prev, current) => prev * current, initialValue) + ); + }); + + it('функция должна выбрасывать исключение, если number не является числом', () => { + expect(() => calculator(':(')).toThrow('number is not a number'); + }); + + it('значение по умолчанию для аргумента number должно быть равно 0', () => { + const calc = calculator(); + const args = [1, 2]; + + expect(calc.sum(...args)).toBe(args.reduce((prev, current) => prev + current)); + }); + }); +}); From f74148b18b1eca4c54f03dc4d10c86e43b5d9c6f Mon Sep 17 00:00:00 2001 From: Sergey Melyukov Date: Sat, 15 Apr 2023 16:54:49 +0300 Subject: [PATCH 3/5] update --- projects/loft-photo-1/readme.md | 71 ---- .../{loft-photo-1 => loft-photo}/friends.json | 0 projects/loft-photo/images/arrow-left.svg | 4 + projects/loft-photo/images/button.svg | 17 + projects/loft-photo/images/chat.svg | 3 + projects/loft-photo/images/exit.svg | 5 + projects/loft-photo/images/heart-red.svg | 3 + projects/loft-photo/images/heart.svg | 3 + projects/loft-photo/images/logo.svg | 11 + projects/loft-photo/images/send.svg | 3 + projects/loft-photo/images/vert1.svg | 22 ++ projects/loft-photo/images/vert2.svg | 22 ++ projects/loft-photo/images/vert3.svg | 22 ++ .../{loft-photo-1 => loft-photo}/index.js | 0 projects/loft-photo/layout.html | 64 ++++ .../{loft-photo-1 => loft-photo}/model.js | 0 .../{loft-photo-1 => loft-photo}/photos.json | 0 projects/loft-photo/server/index.js | 91 +++++ projects/loft-photo/settings.json | 7 + projects/loft-photo/styles.css | 348 ++++++++++++++++++ 20 files changed, 625 insertions(+), 71 deletions(-) delete mode 100644 projects/loft-photo-1/readme.md rename projects/{loft-photo-1 => loft-photo}/friends.json (100%) create mode 100644 projects/loft-photo/images/arrow-left.svg create mode 100644 projects/loft-photo/images/button.svg create mode 100644 projects/loft-photo/images/chat.svg create mode 100644 projects/loft-photo/images/exit.svg create mode 100644 projects/loft-photo/images/heart-red.svg create mode 100644 projects/loft-photo/images/heart.svg create mode 100644 projects/loft-photo/images/logo.svg create mode 100644 projects/loft-photo/images/send.svg create mode 100644 projects/loft-photo/images/vert1.svg create mode 100644 projects/loft-photo/images/vert2.svg create mode 100644 projects/loft-photo/images/vert3.svg rename projects/{loft-photo-1 => loft-photo}/index.js (100%) create mode 100644 projects/loft-photo/layout.html rename projects/{loft-photo-1 => loft-photo}/model.js (100%) rename projects/{loft-photo-1 => loft-photo}/photos.json (100%) create mode 100644 projects/loft-photo/server/index.js create mode 100644 projects/loft-photo/settings.json create mode 100644 projects/loft-photo/styles.css diff --git a/projects/loft-photo-1/readme.md b/projects/loft-photo-1/readme.md deleted file mode 100644 index cd26a9f0e..000000000 --- a/projects/loft-photo-1/readme.md +++ /dev/null @@ -1,71 +0,0 @@ -## Массивы и объекты - -Реализуйте объект с двумя методами: - -- `getRandomElement(array)` -- `getNextPhoto()` - -### `getRandomElement(array)` - -Метод принимает массив в параметре `array` и должен вернуть **случайный** элемент из этого масства при каждом вызове. - -Например: - -```js -const fruits = ['банан', 'яблоко', 'груша']; - -console.log(getRandomElement(fruits)); // груша -console.log(getRandomElement(fruits)); // банан -console.log(getRandomElement(fruits)); // банан -``` - -Для получения случайного числа может пригодиться метод [Math.random()](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Math/random). Этот метод возвращает случайное число между 0 и 1 (например 0.548) - -Вот так можно получить число от 0 до 10 (например): - -```js -console.log(Math.random() * 10); // 5.754356 -console.log(Math.random() * 10); // 2.12864 -``` - -А при помощи [parseInt](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/parseInt) можно убрать дробную часть: - -```js -console.log(parseInt(Math.random() * 10)); // 8 -console.log(parseInt(Math.random() * 10)); // 3 -``` - -### `getNextPhoto()` - -При каждом вызове метод должен вернуть информацию со случайным другом и случайной его фотографией. - -Информация должна быть возвращена в виде объекта из двух полей: `friend` и `url`. - -Например: - -```js -let photo = getNextPhoto(); - -console.log(photo.friend); // { firstName: 'Иван' } -console.log(photo.url); // https://... - -photo = getNextPhoto(); - -console.log(photo.friend); // { firstName: 'Сергей' } -console.log(photo.url); // https://... -``` - -Пример списка друзей и фотографий можно найти в файлах [friends.json](friends.json) и [photos.json](photos.json). - -В файле [friends.json](friends.json) хранится массив со списком друзей. У каждого друга есть имя, фамилия и идентификатор. - -В файле [photos.json](photos.json) хранится объект, ключами которого являются идентификаторы друзей, а значениями - массив фотографий этого друга. -У каждой фотографии есть идентификатор и url. - -Вот так, например, можно получить все фотографии друга с идентификатором `1`: - -```js -const photosDB = require('./photos.json'); - -console.log(photosDB[1]) // [ ... ] -``` diff --git a/projects/loft-photo-1/friends.json b/projects/loft-photo/friends.json similarity index 100% rename from projects/loft-photo-1/friends.json rename to projects/loft-photo/friends.json diff --git a/projects/loft-photo/images/arrow-left.svg b/projects/loft-photo/images/arrow-left.svg new file mode 100644 index 000000000..a4e4c339a --- /dev/null +++ b/projects/loft-photo/images/arrow-left.svg @@ -0,0 +1,4 @@ + + + + diff --git a/projects/loft-photo/images/button.svg b/projects/loft-photo/images/button.svg new file mode 100644 index 000000000..6ce85ea9f --- /dev/null +++ b/projects/loft-photo/images/button.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/projects/loft-photo/images/chat.svg b/projects/loft-photo/images/chat.svg new file mode 100644 index 000000000..fc47d01e1 --- /dev/null +++ b/projects/loft-photo/images/chat.svg @@ -0,0 +1,3 @@ + + + diff --git a/projects/loft-photo/images/exit.svg b/projects/loft-photo/images/exit.svg new file mode 100644 index 000000000..d28c122e1 --- /dev/null +++ b/projects/loft-photo/images/exit.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/projects/loft-photo/images/heart-red.svg b/projects/loft-photo/images/heart-red.svg new file mode 100644 index 000000000..e9985dca6 --- /dev/null +++ b/projects/loft-photo/images/heart-red.svg @@ -0,0 +1,3 @@ + + + diff --git a/projects/loft-photo/images/heart.svg b/projects/loft-photo/images/heart.svg new file mode 100644 index 000000000..4bcdacd80 --- /dev/null +++ b/projects/loft-photo/images/heart.svg @@ -0,0 +1,3 @@ + + + diff --git a/projects/loft-photo/images/logo.svg b/projects/loft-photo/images/logo.svg new file mode 100644 index 000000000..12685673d --- /dev/null +++ b/projects/loft-photo/images/logo.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/projects/loft-photo/images/send.svg b/projects/loft-photo/images/send.svg new file mode 100644 index 000000000..5a55b025c --- /dev/null +++ b/projects/loft-photo/images/send.svg @@ -0,0 +1,3 @@ + + + diff --git a/projects/loft-photo/images/vert1.svg b/projects/loft-photo/images/vert1.svg new file mode 100644 index 000000000..d5d86e658 --- /dev/null +++ b/projects/loft-photo/images/vert1.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/loft-photo/images/vert2.svg b/projects/loft-photo/images/vert2.svg new file mode 100644 index 000000000..0f5e75ed2 --- /dev/null +++ b/projects/loft-photo/images/vert2.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/loft-photo/images/vert3.svg b/projects/loft-photo/images/vert3.svg new file mode 100644 index 000000000..7b481af03 --- /dev/null +++ b/projects/loft-photo/images/vert3.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/loft-photo-1/index.js b/projects/loft-photo/index.js similarity index 100% rename from projects/loft-photo-1/index.js rename to projects/loft-photo/index.js diff --git a/projects/loft-photo/layout.html b/projects/loft-photo/layout.html new file mode 100644 index 000000000..89845cfed --- /dev/null +++ b/projects/loft-photo/layout.html @@ -0,0 +1,64 @@ + + + + + + Loft Photo + + + +
+ + + +
+ + diff --git a/projects/loft-photo-1/model.js b/projects/loft-photo/model.js similarity index 100% rename from projects/loft-photo-1/model.js rename to projects/loft-photo/model.js diff --git a/projects/loft-photo-1/photos.json b/projects/loft-photo/photos.json similarity index 100% rename from projects/loft-photo-1/photos.json rename to projects/loft-photo/photos.json diff --git a/projects/loft-photo/server/index.js b/projects/loft-photo/server/index.js new file mode 100644 index 000000000..e9057c6c4 --- /dev/null +++ b/projects/loft-photo/server/index.js @@ -0,0 +1,91 @@ +const http = require('node:http'); +const https = require('node:https'); +const url = require('node:url'); + +const DB = { + tokens: new Map(), + likes: new Map(), + comments: new Map(), +}; + +const methods = { + like(req, res, url, vkUser) { + // todo + }, + photoStats(req, res, url, vkUser) { + // todo + }, + postComment(req, res, url, vkUser, body) { + // todo + }, + getComments(req, res, url) { + // todo + }, +}; + +http + .createServer(async (req, res) => { + console.log('➡️ Поступил запрос:', req.method, req.url); + const token = req.headers['vk_token']; + const parsed = new url.URL(req.url, 'http://localhost'); + const vkUser = await getMe(token); + const body = await readBody(req); + const method = parsed.searchParams.get('method'); + const responseData = await methods[method]?.(req, res, parsed, vkUser, body); + + res.end(JSON.stringify(responseData ?? null)); + }) + .listen('8888', () => { + console.log('🚀 Сервер запущен'); + }); + +async function readBody(req) { + if (req.method === 'GET') { + return null; + } + + return new Promise((resolve) => { + let body = ''; + req + .on('data', (chunk) => { + body += chunk; + }) + .on('end', () => resolve(JSON.parse(body))); + }); +} + +async function getVKUser(token) { + const body = await new Promise((resolve, reject) => + https + .get( + `https://api.vk.com/method/users.get?access_token=${token}&fields=photo_50&v=5.120` + ) + .on('response', (res) => { + let body = ''; + + res.setEncoding('utf8'); + res + .on('data', (chunk) => { + body += chunk; + }) + .on('end', () => resolve(JSON.parse(body))); + }) + .on('error', reject) + ); + + return body.response[0]; +} + +async function getMe(token) { + const existing = DB.tokens.get(token); + + if (existing) { + return existing; + } + + const user = getVKUser(token); + + DB.tokens.set(token, user); + + return user; +} diff --git a/projects/loft-photo/settings.json b/projects/loft-photo/settings.json new file mode 100644 index 000000000..3d20b4405 --- /dev/null +++ b/projects/loft-photo/settings.json @@ -0,0 +1,7 @@ +{ + "proxy": { + "/loft-photo/api/": { + "target": "http://localhost:8888" + } + } +} diff --git a/projects/loft-photo/styles.css b/projects/loft-photo/styles.css new file mode 100644 index 000000000..62d5fba21 --- /dev/null +++ b/projects/loft-photo/styles.css @@ -0,0 +1,348 @@ +/* base */ + +body { + font-family: "Roboto Light", Geneva, Arial, Helvetica, sans-serif; +} + +.hidden { + display: none !important; +} + +a { + text-decoration: none; +} + +/* app */ + +#app { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + display: flex; + + align-items: center; + justify-content: center; +} + +.page { + height: 100%; + width: 360px; + position: relative; +} + +/* page login */ + +.page-login { + display: flex; + justify-content: center; + background: #1C1B1F; +} + +.page-login-button { + border: none; + background: url('images/button.svg'); + width: 219px; + height: 40px; + position: absolute; + bottom: 60px; + margin: 0 auto; +} + +.page-login-logo { + top: 429px; + position: absolute; + gap: 16px; + display: flex; + flex-direction: column; + align-items: center; +} + +.page-login-image { + width: 147px; + height: 24px; + background: url('images/logo.svg'); +} + +.page-login-text { + font-size: 14px; + line-height: 20px; + text-align: center; + width: 237px; + color: #B0B0B0; +} + +.page-login-vert1, .page-login-vert2, .page-login-vert3 { + width: 71px; + height: 333px; + position: absolute; +} + +.page-login-vert1 { + top: 59px; + left: 49px; + background: linear-gradient(180deg, rgba(28, 27, 31, 0) 80%, #1C1B1F 100%), url('images/vert1.svg'); +} + +.page-login-vert2 { + top: 81px; + left: 144px; + background: linear-gradient(180deg, rgba(28, 27, 31, 0) 80%, #1C1B1F 100%), url('images/vert2.svg'); +} + +.page-login-vert3 { + top: 59px; + left: 239px; + background: linear-gradient(180deg, rgba(28, 27, 31, 0) 80%, #1C1B1F 100%), url('images/vert3.svg'); +} + +/* page main */ + +.page-main .component-header { + position: absolute; + display: flex; + height: 80px; + top: 0; + left: 0; + right: 0; + background: rgba(0 0 0 / 25%); + padding: 0 24px; +} + +.page-main .component-header-profile-link { + display: flex; + align-items: center; +} + +.page-main .component-header-photo { + width: 40px; + height: 40px; + border-radius: 50%; + flex-shrink: 0; +} + +.page-main .component-header-name { + margin-left: 8px; + font-weight: 400; + font-size: 16px; + color: white; +} + +.page-main .component-footer { + position: absolute; + display: flex; + height: 80px; + bottom: 0; + left: 0; + right: 0; + background: rgba(0 0 0 / 25%); + padding: 0 24px; +} + +.page-main .component-footer-container { + display: flex; + align-items: center; + width: 100%; +} + +.page-main .component-footer-container-profile-link { + margin-left: auto; +} + +.page-main .component-footer-photo { + width: 40px; + height: 40px; + border-radius: 50%; +} + +.page-main .component-footer-container-social-comments, +.page-main .component-footer-container-social-likes { + color: white; + display: flex; + align-items: center; +} + +.page-main .component-footer-container-social-comments:before, +.page-main .component-footer-container-social-likes:before { + display: inline-block; + content: ''; + width: 20px; + height: 20px; + margin-right: 6px; +} + +.page-main .component-footer-container-social-comments:before { + background: url("images/chat.svg"); +} + +.page-main .component-footer-container-social-likes:before { + background: url("images/heart.svg"); + margin-left: 18px; +} + +.page-main .component-footer-container-social-likes.liked:before { + background: url("images/heart-red.svg"); + margin-left: 18px; +} + +.page-main .component-photo { + height: 100%; + width: 360px; + position: relative; + + background-size: cover; + background-position: center; +} + +.component-comments { + position: fixed; + bottom: 0; + left: 0; + right: 0; + top: 0; + background: rgba(0, 0, 0, 0.4); +} + +.component-comments-container { + position: absolute; + display: flex; + flex-direction: column; + top: 50vh; + bottom: 0; + left: 0; + right: 0; + padding: 16px; + border-radius: 28px 28px 0 0; + background: white; +} + +.component-comments-container-title { + font-size: 14px; + text-align: center; + width: 100%; +} + +.component-comments-container-list { + margin-top: 24px; + flex-grow: 1; + display: flex; + gap: 12px; + flex-direction: column; + overflow-y: auto; + margin-bottom: 14px +} + +.component-comments-container-form { + display: flex; + align-items: center; + gap: 16px; + height: 48px; +} + +.component-comments-container-form-input { + box-sizing: border-box; + border: 1px solid #E0E0E0; + border-radius: 32px; + flex-grow: 1; + height: 48px; +} + +.component-comments-container-form-input, +.component-comments-container-form-input, +.component-comments-container-form-input, +.component-comments-container-form-input { + padding: 14px 16px; +} + +.component-comments-container-form-send { + background: url('images/send.svg'); + width: 40px; + height: 40px; +} + +.component-comment { + display: flex; + gap: 8px +} + +.component-comment-photo { + width: 24px; + height: 24px; + border-radius: 50%; + background-position: center; + background-size: cover; +} + +.component-comment-content { + flex-direction: column; +} + +.component-comment-name { + font-size: 12px; +} + +.component-comment-text { + font-size: 14px; +} + +/* page profile */ + +.page-profile { + margin-top: 52px; +} + +.page-profile-back { + background: url('images/arrow-left.svg'); + width: 24px; + height: 24px; + + position: absolute; + left: 24px; +} + +.page-profile-exit { + background: url('images/exit.svg'); + width: 24px; + height: 24px; + + position: absolute; + right: 24px; +} + +.component-user-photos { + display: flex; + flex-wrap: wrap; + gap: 8px; + padding: 24px 16px 16px 16px; +} + +.component-user-photo { + width: 104px; + height: 104px; + background-size: cover; + background-repeat: no-repeat; + background-position: center; +} + +.page-profile .component-user-info { + display: flex; + flex-direction: column; + align-items: center; +} + +.page-profile .component-user-info-photo { + height: 72px; + width: 72px; + border-radius: 50%; + + background-size: cover; + background-position: center; +} + +.page-profile .component-user-info-name { + font-weight: 400; + font-size: 18px; + line-height: 26px; + margin-top: 8px; +} \ No newline at end of file From c6beac52ed208f73fec2bae297d321a14ce5aba5 Mon Sep 17 00:00:00 2001 From: Sergey Melyukov Date: Mon, 17 Apr 2023 19:06:20 +0300 Subject: [PATCH 4/5] fix --- projects/loft-photo/friends.json | 32 -- projects/loft-photo/images/arrow-left.svg | 4 - projects/loft-photo/images/button.svg | 17 -- projects/loft-photo/images/chat.svg | 3 - projects/loft-photo/images/exit.svg | 5 - projects/loft-photo/images/heart-red.svg | 3 - projects/loft-photo/images/heart.svg | 3 - projects/loft-photo/images/logo.svg | 11 - projects/loft-photo/images/send.svg | 3 - projects/loft-photo/images/vert1.svg | 22 -- projects/loft-photo/images/vert2.svg | 22 -- projects/loft-photo/images/vert3.svg | 22 -- projects/loft-photo/index.js | 0 projects/loft-photo/layout.html | 64 ---- projects/loft-photo/model.js | 9 - projects/loft-photo/photos.json | 72 ----- projects/loft-photo/server/index.js | 91 ------ projects/loft-photo/settings.json | 7 - projects/loft-photo/styles.css | 348 ---------------------- 19 files changed, 738 deletions(-) delete mode 100644 projects/loft-photo/friends.json delete mode 100644 projects/loft-photo/images/arrow-left.svg delete mode 100644 projects/loft-photo/images/button.svg delete mode 100644 projects/loft-photo/images/chat.svg delete mode 100644 projects/loft-photo/images/exit.svg delete mode 100644 projects/loft-photo/images/heart-red.svg delete mode 100644 projects/loft-photo/images/heart.svg delete mode 100644 projects/loft-photo/images/logo.svg delete mode 100644 projects/loft-photo/images/send.svg delete mode 100644 projects/loft-photo/images/vert1.svg delete mode 100644 projects/loft-photo/images/vert2.svg delete mode 100644 projects/loft-photo/images/vert3.svg delete mode 100644 projects/loft-photo/index.js delete mode 100644 projects/loft-photo/layout.html delete mode 100644 projects/loft-photo/model.js delete mode 100644 projects/loft-photo/photos.json delete mode 100644 projects/loft-photo/server/index.js delete mode 100644 projects/loft-photo/settings.json delete mode 100644 projects/loft-photo/styles.css diff --git a/projects/loft-photo/friends.json b/projects/loft-photo/friends.json deleted file mode 100644 index b861e0c00..000000000 --- a/projects/loft-photo/friends.json +++ /dev/null @@ -1,32 +0,0 @@ -[ - { - "id": 0, - "avatar": "https://via.placeholder.com/100?text=avatar", - "firstName": "Adrian", - "lastName": "Norman" - }, - { - "id": 1, - "avatar": "https://via.placeholder.com/100?text=avatar", - "firstName": "Gail", - "lastName": "Norton" - }, - { - "id": 2, - "avatar": "https://via.placeholder.com/100?text=avatar", - "firstName": "Molina", - "lastName": "Rodgers" - }, - { - "id": 3, - "avatar": "https://via.placeholder.com/100?text=avatar", - "firstName": "Adams", - "lastName": "Parrish" - }, - { - "id": 4, - "avatar": "https://via.placeholder.com/100?text=avatar", - "firstName": "Mercer", - "lastName": "Wiggins" - } -] \ No newline at end of file diff --git a/projects/loft-photo/images/arrow-left.svg b/projects/loft-photo/images/arrow-left.svg deleted file mode 100644 index a4e4c339a..000000000 --- a/projects/loft-photo/images/arrow-left.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/projects/loft-photo/images/button.svg b/projects/loft-photo/images/button.svg deleted file mode 100644 index 6ce85ea9f..000000000 --- a/projects/loft-photo/images/button.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/projects/loft-photo/images/chat.svg b/projects/loft-photo/images/chat.svg deleted file mode 100644 index fc47d01e1..000000000 --- a/projects/loft-photo/images/chat.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/projects/loft-photo/images/exit.svg b/projects/loft-photo/images/exit.svg deleted file mode 100644 index d28c122e1..000000000 --- a/projects/loft-photo/images/exit.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/projects/loft-photo/images/heart-red.svg b/projects/loft-photo/images/heart-red.svg deleted file mode 100644 index e9985dca6..000000000 --- a/projects/loft-photo/images/heart-red.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/projects/loft-photo/images/heart.svg b/projects/loft-photo/images/heart.svg deleted file mode 100644 index 4bcdacd80..000000000 --- a/projects/loft-photo/images/heart.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/projects/loft-photo/images/logo.svg b/projects/loft-photo/images/logo.svg deleted file mode 100644 index 12685673d..000000000 --- a/projects/loft-photo/images/logo.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/projects/loft-photo/images/send.svg b/projects/loft-photo/images/send.svg deleted file mode 100644 index 5a55b025c..000000000 --- a/projects/loft-photo/images/send.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/projects/loft-photo/images/vert1.svg b/projects/loft-photo/images/vert1.svg deleted file mode 100644 index d5d86e658..000000000 --- a/projects/loft-photo/images/vert1.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/projects/loft-photo/images/vert2.svg b/projects/loft-photo/images/vert2.svg deleted file mode 100644 index 0f5e75ed2..000000000 --- a/projects/loft-photo/images/vert2.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/projects/loft-photo/images/vert3.svg b/projects/loft-photo/images/vert3.svg deleted file mode 100644 index 7b481af03..000000000 --- a/projects/loft-photo/images/vert3.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/projects/loft-photo/index.js b/projects/loft-photo/index.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/projects/loft-photo/layout.html b/projects/loft-photo/layout.html deleted file mode 100644 index 89845cfed..000000000 --- a/projects/loft-photo/layout.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - Loft Photo - - - -
- - - -
- - diff --git a/projects/loft-photo/model.js b/projects/loft-photo/model.js deleted file mode 100644 index 1e31e33b3..000000000 --- a/projects/loft-photo/model.js +++ /dev/null @@ -1,9 +0,0 @@ -// eslint-disable-next-line no-unused-vars -import photosDB from './photos.json'; -// eslint-disable-next-line no-unused-vars -import friendsDB from './friends.json'; - -export default { - getRandomElement(array) {}, - getNextPhoto() {}, -}; diff --git a/projects/loft-photo/photos.json b/projects/loft-photo/photos.json deleted file mode 100644 index a299e6057..000000000 --- a/projects/loft-photo/photos.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "0": [ - { - "id": 10, - "url": "https://via.placeholder.com/360x680?text=photo 1 for Adrian Norman" - }, - { - "id": 11, - "url": "https://via.placeholder.com/360x680?text=photo 2 for Adrian Norman" - }, - { - "id": 12, - "url": "https://via.placeholder.com/360x680?text=photo 3 for Adrian Norman" - } - ], - "1": [ - { - "id": 20, - "url": "https://via.placeholder.com/360x680?text=photo 1 for Gail Norton" - }, - { - "id": 21, - "url": "https://via.placeholder.com/360x680?text=photo 2 for Gail Norton" - }, - { - "id": 22, - "url": "https://via.placeholder.com/360x680?text=photo 3 for Gail Norton" - } - ], - "2": [ - { - "id": 30, - "url": "https://via.placeholder.com/360x680?text=photo 1 for Molina Rodgers" - }, - { - "id": 31, - "url": "https://via.placeholder.com/360x680?text=photo 2 for Molina Rodgers" - }, - { - "id": 32, - "url": "https://via.placeholder.com/360x680?text=photo 3 for Molina Rodgers" - } - ], - "3": [ - { - "id": 40, - "url": "https://via.placeholder.com/360x680?text=photo 1 for Adams Parrish" - }, - { - "id": 41, - "url": "https://via.placeholder.com/360x680?text=photo 2 for Adams Parrish" - }, - { - "id": 42, - "url": "https://via.placeholder.com/360x680?text=photo 3 for Adams Parrish" - } - ], - "4": [ - { - "id": 50, - "url": "https://via.placeholder.com/360x680?text=photo 1 for Mercer Wiggins" - }, - { - "id": 51, - "url": "https://via.placeholder.com/360x680?text=photo 2 for Mercer Wiggins" - }, - { - "id": 52, - "url": "https://via.placeholder.com/360x680?text=photo 3 for Mercer Wiggins" - } - ] -} \ No newline at end of file diff --git a/projects/loft-photo/server/index.js b/projects/loft-photo/server/index.js deleted file mode 100644 index e9057c6c4..000000000 --- a/projects/loft-photo/server/index.js +++ /dev/null @@ -1,91 +0,0 @@ -const http = require('node:http'); -const https = require('node:https'); -const url = require('node:url'); - -const DB = { - tokens: new Map(), - likes: new Map(), - comments: new Map(), -}; - -const methods = { - like(req, res, url, vkUser) { - // todo - }, - photoStats(req, res, url, vkUser) { - // todo - }, - postComment(req, res, url, vkUser, body) { - // todo - }, - getComments(req, res, url) { - // todo - }, -}; - -http - .createServer(async (req, res) => { - console.log('➡️ Поступил запрос:', req.method, req.url); - const token = req.headers['vk_token']; - const parsed = new url.URL(req.url, 'http://localhost'); - const vkUser = await getMe(token); - const body = await readBody(req); - const method = parsed.searchParams.get('method'); - const responseData = await methods[method]?.(req, res, parsed, vkUser, body); - - res.end(JSON.stringify(responseData ?? null)); - }) - .listen('8888', () => { - console.log('🚀 Сервер запущен'); - }); - -async function readBody(req) { - if (req.method === 'GET') { - return null; - } - - return new Promise((resolve) => { - let body = ''; - req - .on('data', (chunk) => { - body += chunk; - }) - .on('end', () => resolve(JSON.parse(body))); - }); -} - -async function getVKUser(token) { - const body = await new Promise((resolve, reject) => - https - .get( - `https://api.vk.com/method/users.get?access_token=${token}&fields=photo_50&v=5.120` - ) - .on('response', (res) => { - let body = ''; - - res.setEncoding('utf8'); - res - .on('data', (chunk) => { - body += chunk; - }) - .on('end', () => resolve(JSON.parse(body))); - }) - .on('error', reject) - ); - - return body.response[0]; -} - -async function getMe(token) { - const existing = DB.tokens.get(token); - - if (existing) { - return existing; - } - - const user = getVKUser(token); - - DB.tokens.set(token, user); - - return user; -} diff --git a/projects/loft-photo/settings.json b/projects/loft-photo/settings.json deleted file mode 100644 index 3d20b4405..000000000 --- a/projects/loft-photo/settings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "proxy": { - "/loft-photo/api/": { - "target": "http://localhost:8888" - } - } -} diff --git a/projects/loft-photo/styles.css b/projects/loft-photo/styles.css deleted file mode 100644 index 62d5fba21..000000000 --- a/projects/loft-photo/styles.css +++ /dev/null @@ -1,348 +0,0 @@ -/* base */ - -body { - font-family: "Roboto Light", Geneva, Arial, Helvetica, sans-serif; -} - -.hidden { - display: none !important; -} - -a { - text-decoration: none; -} - -/* app */ - -#app { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - display: flex; - - align-items: center; - justify-content: center; -} - -.page { - height: 100%; - width: 360px; - position: relative; -} - -/* page login */ - -.page-login { - display: flex; - justify-content: center; - background: #1C1B1F; -} - -.page-login-button { - border: none; - background: url('images/button.svg'); - width: 219px; - height: 40px; - position: absolute; - bottom: 60px; - margin: 0 auto; -} - -.page-login-logo { - top: 429px; - position: absolute; - gap: 16px; - display: flex; - flex-direction: column; - align-items: center; -} - -.page-login-image { - width: 147px; - height: 24px; - background: url('images/logo.svg'); -} - -.page-login-text { - font-size: 14px; - line-height: 20px; - text-align: center; - width: 237px; - color: #B0B0B0; -} - -.page-login-vert1, .page-login-vert2, .page-login-vert3 { - width: 71px; - height: 333px; - position: absolute; -} - -.page-login-vert1 { - top: 59px; - left: 49px; - background: linear-gradient(180deg, rgba(28, 27, 31, 0) 80%, #1C1B1F 100%), url('images/vert1.svg'); -} - -.page-login-vert2 { - top: 81px; - left: 144px; - background: linear-gradient(180deg, rgba(28, 27, 31, 0) 80%, #1C1B1F 100%), url('images/vert2.svg'); -} - -.page-login-vert3 { - top: 59px; - left: 239px; - background: linear-gradient(180deg, rgba(28, 27, 31, 0) 80%, #1C1B1F 100%), url('images/vert3.svg'); -} - -/* page main */ - -.page-main .component-header { - position: absolute; - display: flex; - height: 80px; - top: 0; - left: 0; - right: 0; - background: rgba(0 0 0 / 25%); - padding: 0 24px; -} - -.page-main .component-header-profile-link { - display: flex; - align-items: center; -} - -.page-main .component-header-photo { - width: 40px; - height: 40px; - border-radius: 50%; - flex-shrink: 0; -} - -.page-main .component-header-name { - margin-left: 8px; - font-weight: 400; - font-size: 16px; - color: white; -} - -.page-main .component-footer { - position: absolute; - display: flex; - height: 80px; - bottom: 0; - left: 0; - right: 0; - background: rgba(0 0 0 / 25%); - padding: 0 24px; -} - -.page-main .component-footer-container { - display: flex; - align-items: center; - width: 100%; -} - -.page-main .component-footer-container-profile-link { - margin-left: auto; -} - -.page-main .component-footer-photo { - width: 40px; - height: 40px; - border-radius: 50%; -} - -.page-main .component-footer-container-social-comments, -.page-main .component-footer-container-social-likes { - color: white; - display: flex; - align-items: center; -} - -.page-main .component-footer-container-social-comments:before, -.page-main .component-footer-container-social-likes:before { - display: inline-block; - content: ''; - width: 20px; - height: 20px; - margin-right: 6px; -} - -.page-main .component-footer-container-social-comments:before { - background: url("images/chat.svg"); -} - -.page-main .component-footer-container-social-likes:before { - background: url("images/heart.svg"); - margin-left: 18px; -} - -.page-main .component-footer-container-social-likes.liked:before { - background: url("images/heart-red.svg"); - margin-left: 18px; -} - -.page-main .component-photo { - height: 100%; - width: 360px; - position: relative; - - background-size: cover; - background-position: center; -} - -.component-comments { - position: fixed; - bottom: 0; - left: 0; - right: 0; - top: 0; - background: rgba(0, 0, 0, 0.4); -} - -.component-comments-container { - position: absolute; - display: flex; - flex-direction: column; - top: 50vh; - bottom: 0; - left: 0; - right: 0; - padding: 16px; - border-radius: 28px 28px 0 0; - background: white; -} - -.component-comments-container-title { - font-size: 14px; - text-align: center; - width: 100%; -} - -.component-comments-container-list { - margin-top: 24px; - flex-grow: 1; - display: flex; - gap: 12px; - flex-direction: column; - overflow-y: auto; - margin-bottom: 14px -} - -.component-comments-container-form { - display: flex; - align-items: center; - gap: 16px; - height: 48px; -} - -.component-comments-container-form-input { - box-sizing: border-box; - border: 1px solid #E0E0E0; - border-radius: 32px; - flex-grow: 1; - height: 48px; -} - -.component-comments-container-form-input, -.component-comments-container-form-input, -.component-comments-container-form-input, -.component-comments-container-form-input { - padding: 14px 16px; -} - -.component-comments-container-form-send { - background: url('images/send.svg'); - width: 40px; - height: 40px; -} - -.component-comment { - display: flex; - gap: 8px -} - -.component-comment-photo { - width: 24px; - height: 24px; - border-radius: 50%; - background-position: center; - background-size: cover; -} - -.component-comment-content { - flex-direction: column; -} - -.component-comment-name { - font-size: 12px; -} - -.component-comment-text { - font-size: 14px; -} - -/* page profile */ - -.page-profile { - margin-top: 52px; -} - -.page-profile-back { - background: url('images/arrow-left.svg'); - width: 24px; - height: 24px; - - position: absolute; - left: 24px; -} - -.page-profile-exit { - background: url('images/exit.svg'); - width: 24px; - height: 24px; - - position: absolute; - right: 24px; -} - -.component-user-photos { - display: flex; - flex-wrap: wrap; - gap: 8px; - padding: 24px 16px 16px 16px; -} - -.component-user-photo { - width: 104px; - height: 104px; - background-size: cover; - background-repeat: no-repeat; - background-position: center; -} - -.page-profile .component-user-info { - display: flex; - flex-direction: column; - align-items: center; -} - -.page-profile .component-user-info-photo { - height: 72px; - width: 72px; - border-radius: 50%; - - background-size: cover; - background-position: center; -} - -.page-profile .component-user-info-name { - font-weight: 400; - font-size: 18px; - line-height: 26px; - margin-top: 8px; -} \ No newline at end of file From 88dce8a0c6cd6b68d339286276a836120827297f Mon Sep 17 00:00:00 2001 From: Polina <109180069+JimmyCoid@users.noreply.github.com> Date: Sat, 22 Jul 2023 12:30:52 +0300 Subject: [PATCH 5/5] =?UTF-8?q?=D0=97=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=B2=D1=82=D0=BE=D1=80=D0=BE=D0=B9=20=D0=BD=D0=B5=D0=B4?= =?UTF-8?q?=D0=B5=D0=BB=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- projects/array-objects/index.js | 36 +++++++++++++-- projects/exceptions/index.js | 82 +++++++++++++++++++++++++++++++-- 2 files changed, 110 insertions(+), 8 deletions(-) diff --git a/projects/array-objects/index.js b/projects/array-objects/index.js index ca1ebc4c6..8b6c23ce3 100644 --- a/projects/array-objects/index.js +++ b/projects/array-objects/index.js @@ -9,7 +9,11 @@ Пример: forEach([1, 2, 3], (el) => console.log(el)); // выведет каждый элемент массива */ -function forEach() {} +function forEach(array, fn) { + for (const [ix, el] of array.entries()) { + fn (el, ix, array) + } +} /* Задание 2: @@ -21,7 +25,15 @@ function forEach() {} const newArray = map([1, 2, 3], (el) => el ** 2); console.log(newArray); // выведет [1, 4, 9] */ -function map() {} +function map(array, fn) { + const newArray = []; + + for (const [ix, el] of array.entries()) { + newArray.push(fn(el, ix, array)); + } + + return newArray; +} /* Задание 3: @@ -33,7 +45,21 @@ function map() {} const sum = reduce([1, 2, 3], (all, current) => all + current); console.log(sum); // выведет 6 */ -function reduce() {} +function reduce(array, fn, initial) { + let startIndex = 0; + let all = initial; + + if (initial = undefined) { + startIndex = 1; + all = array[0]; + } + + for (let i = startIndex; i < array.length; i++) { + all = fn(all, array[i], i, array) + } + + return all; +} /* Задание 4: @@ -44,6 +70,8 @@ function reduce() {} const keys = upperProps({ name: 'Сергей', lastName: 'Петров' }); console.log(keys) // выведет ['NAME', 'LASTNAME'] */ -function upperProps() {} +function upperProps(obj) { + return Object.keys(obj).map(k => k.toUpperCase()) +} export { forEach, map, reduce, upperProps }; diff --git a/projects/exceptions/index.js b/projects/exceptions/index.js index d09b05496..c9b544498 100644 --- a/projects/exceptions/index.js +++ b/projects/exceptions/index.js @@ -20,7 +20,23 @@ isAllTrue([1, 2, 3, 4, 5], n => n < 10) // вернет true (потому что все элементы массива меньше 10) isAllTrue([100, 2, 3, 4, 5], n => n < 10) // вернет false (потому что как минимум первый элемент больше 10) */ -function isAllTrue(array, fn) {} +function isAllTrue(array, fn) { + if (!Array.isArray(array) || array.length === 0) { + throw new Error ('empty array (пустой массив)'); + } + + if (typeof fn !== 'function') { + throw new Error('fn is not a function (функции не совпадают)'); + } + + for (const el of array) { + if (!fn(el)) { + return false; + } + } + + return true; +} /* Задание 2: @@ -42,7 +58,23 @@ function isAllTrue(array, fn) {} isSomeTrue([1, 2, 30, 4, 5], n => n > 20) // вернет true (потому что в массиве есть хотя бы один элемент больше 20) isSomeTrue([1, 2, 3, 4, 5], n => n > 20) // вернет false (потому что в массиве нет ни одного элемента больше 20) */ -function isSomeTrue(array, fn) {} +function isSomeTrue(array, fn) { + if (!Array.isArray(array) || array.length === 0) { + throw new Error ('empty array (пустой массив)'); + } + + if (typeof fn !== 'function') { + throw new Error('fn is not a function (не является функцией)'); + } + + for (const el of array) { + if (fn(el)) { + return true; + } + } + + return false; +} /* Задание 3: @@ -56,7 +88,23 @@ function isSomeTrue(array, fn) {} - fn не является функцией (с текстом "fn is not a function") для проверки на функцию вам может помочь оператор typeof */ -function returnBadArguments() {} +function returnBadArguments(fn, ...args) { + if (typeof fn !== 'function') { + throw new Error('fn is not a function (не является функцией)'); + } + + const badArgs = []; + + for (const arg of args) { + try { + fn(arg); + } catch { + badArgs.push(arg); + } + } + + return badArgs; +} /* Задание 4: @@ -84,7 +132,33 @@ function returnBadArguments() {} console.log(calc.div(2, 2)); // выведет 2.5 (10 / 2 / 2) console.log(calc.div(2, 0)); // выбросит исключение, потому что один из аргументов равен 0 */ -function calculator(number) {} +function calculator(number = 0) { + if (typeof number !== 'number') { + throw new Error('number is not a number (не является числом)'); + } + + return { + sum(...args) { + return args.reduce((all, current) => all + current, number); + }, + + dif(...args) { + return args.reduce((all, current) => all - current, number); + }, + + div(...args) { + if (args.some(a => a === 0)) { + throw new Error ('division by 0 (деление на 0)') + } + + return args.reduce((all, current) => all / current, number) + }, + + mul(...args) { + return args.reduce((all, current) => all * current, number); + } + } +} /* При решении задач, постарайтесь использовать отладчик */