├── .eslintignore ├── favicon.ico ├── photos ├── 1.jpg ├── 2.jpg ├── 3.jpg ├── 4.jpg ├── 5.jpg ├── 6.jpg ├── 7.jpg ├── 8.jpg ├── 9.jpg ├── 10.jpg ├── 11.jpg ├── 12.jpg ├── 13.jpg ├── 14.jpg ├── 15.jpg ├── 16.jpg ├── 17.jpg ├── 18.jpg ├── 19.jpg ├── 20.jpg ├── 21.jpg ├── 22.jpg ├── 23.jpg ├── 24.jpg └── 25.jpg ├── img ├── sprite.png ├── logo-mask.png ├── icon-arrow.png ├── logo-background-1.jpg ├── logo-background-2.jpg ├── logo-background-3.jpg ├── upload-button-bg.png ├── upload-default-image.jpg ├── icon-cross.svg ├── icon-warning.svg ├── avatar-1.svg ├── avatar-3.svg ├── avatar-2.svg ├── avatar-4.svg ├── avatar-6.svg ├── spinner-blue.svg ├── spinner.svg ├── htmla-logo.svg ├── upload-button.svg └── avatar-5.svg ├── .gitattributes ├── fonts ├── OpenSans-Regular.woff ├── OpenSans-Regular.woff2 ├── OpenSansCondensed-Bold.woff └── OpenSansCondensed-Bold.woff2 ├── .gitignore ├── js ├── main.js ├── render.js ├── load.js ├── drawThumbnails.js ├── data.js ├── draw.js ├── validate.js ├── scale.js ├── form.js ├── imageScale.js ├── util.js ├── effect.js ├── api.js ├── addEffect.js ├── bigPicture.js └── loaderForm.js ├── .eslintrc ├── .github └── workflows │ └── check.yml ├── .editorconfig ├── package.json ├── Contributing.md ├── Readme.md ├── Workflow.md ├── pristine └── pristine.min.js ├── nouislider └── nouislider.css ├── css ├── normalize.css └── style.css └── index.html /.eslintignore: -------------------------------------------------------------------------------- 1 | webpack.config.js 2 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/favicon.ico -------------------------------------------------------------------------------- /photos/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/1.jpg -------------------------------------------------------------------------------- /photos/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/2.jpg -------------------------------------------------------------------------------- /photos/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/3.jpg -------------------------------------------------------------------------------- /photos/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/4.jpg -------------------------------------------------------------------------------- /photos/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/5.jpg -------------------------------------------------------------------------------- /photos/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/6.jpg -------------------------------------------------------------------------------- /photos/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/7.jpg -------------------------------------------------------------------------------- /photos/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/8.jpg -------------------------------------------------------------------------------- /photos/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/9.jpg -------------------------------------------------------------------------------- /img/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/img/sprite.png -------------------------------------------------------------------------------- /photos/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/10.jpg -------------------------------------------------------------------------------- /photos/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/11.jpg -------------------------------------------------------------------------------- /photos/12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/12.jpg -------------------------------------------------------------------------------- /photos/13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/13.jpg -------------------------------------------------------------------------------- /photos/14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/14.jpg -------------------------------------------------------------------------------- /photos/15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/15.jpg -------------------------------------------------------------------------------- /photos/16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/16.jpg -------------------------------------------------------------------------------- /photos/17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/17.jpg -------------------------------------------------------------------------------- /photos/18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/18.jpg -------------------------------------------------------------------------------- /photos/19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/19.jpg -------------------------------------------------------------------------------- /photos/20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/20.jpg -------------------------------------------------------------------------------- /photos/21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/21.jpg -------------------------------------------------------------------------------- /photos/22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/22.jpg -------------------------------------------------------------------------------- /photos/23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/23.jpg -------------------------------------------------------------------------------- /photos/24.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/24.jpg -------------------------------------------------------------------------------- /photos/25.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/photos/25.jpg -------------------------------------------------------------------------------- /img/logo-mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/img/logo-mask.png -------------------------------------------------------------------------------- /img/icon-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/img/icon-arrow.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.jpg -text 3 | *.png -text 4 | *.gif -text 5 | *.mp4 -text 6 | -------------------------------------------------------------------------------- /img/logo-background-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/img/logo-background-1.jpg -------------------------------------------------------------------------------- /img/logo-background-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/img/logo-background-2.jpg -------------------------------------------------------------------------------- /img/logo-background-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/img/logo-background-3.jpg -------------------------------------------------------------------------------- /img/upload-button-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/img/upload-button-bg.png -------------------------------------------------------------------------------- /fonts/OpenSans-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/fonts/OpenSans-Regular.woff -------------------------------------------------------------------------------- /fonts/OpenSans-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/fonts/OpenSans-Regular.woff2 -------------------------------------------------------------------------------- /img/upload-default-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/img/upload-default-image.jpg -------------------------------------------------------------------------------- /fonts/OpenSansCondensed-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/fonts/OpenSansCondensed-Bold.woff -------------------------------------------------------------------------------- /fonts/OpenSansCondensed-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkzung/kekstogram/HEAD/fonts/OpenSansCondensed-Bold.woff2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | *.ai 4 | *.log 5 | *.pdf 6 | *.psd 7 | *.sublime* 8 | build/ 9 | node_modules/ 10 | npm-debug.log* 11 | Thumbs.db 12 | .vscode/ 13 | -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | /* eslint-disable no-unused-vars */ 3 | import { getData } from './load.js'; 4 | import { renderPhotos } from './render.js'; 5 | import { showAlert } from './util.js'; 6 | import './validate.js'; 7 | 8 | getData(renderPhotos, showAlert); 9 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | root: true 2 | 3 | parserOptions: 4 | ecmaVersion: 2018 5 | sourceType: "module" 6 | 7 | env: 8 | es2017: true 9 | browser: true 10 | 11 | globals: 12 | _: readonly 13 | L: readonly 14 | noUiSlider: readonly 15 | Pristine: readonly 16 | 17 | extends: 18 | "htmlacademy/vanilla" 19 | -------------------------------------------------------------------------------- /img/icon-cross.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | pull_request: 6 | branches: '*' 7 | 8 | name: Project check 9 | jobs: 10 | check: 11 | name: Check 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Set up Node.js 15 | uses: actions/setup-node@v1 16 | with: 17 | node-version: '16' 18 | 19 | - uses: actions/checkout@master 20 | name: Checkout 21 | 22 | - name: Install dependencies 23 | run: | 24 | npm install 25 | 26 | - name: Run checks 27 | run: | 28 | npm run lint 29 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Файл с настройками для редактора. 2 | # 3 | # Если вы разрабатываете в редакторе WebStorm, BBEdit, Coda или SourceLair 4 | # этот файл уже поддерживается и не нужно производить никаких дополнительных 5 | # действий. 6 | # 7 | # Если вы ведёте разработку в другом редакторе, зайдите 8 | # на http://editorconfig.org и в разделе «Download a Plugin» 9 | # скачайте дополнение для вашего редактора. 10 | 11 | 12 | root = true 13 | 14 | [*] 15 | charset = utf-8 16 | indent_style = space 17 | indent_size = 2 18 | end_of_line = lf 19 | trim_trailing_whitespace = true 20 | insert_final_newline = true 21 | -------------------------------------------------------------------------------- /js/render.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-unused-vars 2 | export function renderPhotos(photos){ 3 | const fragment = document.createDocumentFragment(); 4 | const listElement = document.querySelector('.pictures'); 5 | const pictureTemplate = document.querySelector('#picture').content.querySelector('a'); 6 | 7 | photos.forEach((photo) => { 8 | const pictureElement = pictureTemplate.cloneNode(true); 9 | pictureElement.querySelector('img').src = photo.url; 10 | pictureElement.querySelector('.picture__comments').textContent = photo.comments; 11 | pictureElement.querySelector('.picture__likes').textContent = photo.likes; 12 | fragment.appendChild(pictureElement); 13 | }); 14 | listElement.appendChild(fragment); 15 | } 16 | -------------------------------------------------------------------------------- /js/load.js: -------------------------------------------------------------------------------- 1 | export function getData (onSuccess, onFail) { 2 | fetch('https://27.javascript.pages.academy/kekstagram-simple/data') 3 | .then((response) => response.json()) 4 | .then((photos) => { 5 | onSuccess(photos); 6 | }) 7 | .catch(() => { 8 | onFail('При загрузке данных с сервера произошла ошибка' ); 9 | }); 10 | } 11 | 12 | export function sendData (onSuccess, onFail, body) { 13 | fetch( 14 | 'https://27.javascript.pages.academy/kekstagram-simple', 15 | { 16 | method: 'POST', 17 | body, 18 | }, 19 | ).then((response) => { 20 | if (response.ok) { 21 | onSuccess(); 22 | } else { 23 | onFail(); 24 | } 25 | }).catch(() => { 26 | onFail(); 27 | }); 28 | } 29 | 30 | -------------------------------------------------------------------------------- /js/drawThumbnails.js: -------------------------------------------------------------------------------- 1 | import {getBigPicture} from './bigPicture.js'; 2 | 3 | 4 | const pictureContainer = document.querySelector('.pictures'); 5 | const pictureTemplate = document.querySelector('#picture').content.querySelector('.picture'); 6 | 7 | 8 | function drawThumbnails(photos) { 9 | photos.forEach((pic) => { 10 | const newPicture = pictureTemplate.cloneNode(true); 11 | newPicture.querySelector('img').src = pic.url; 12 | newPicture.querySelector('.picture__likes').textContent = pic.likes; 13 | newPicture.querySelector('.picture__comments').textContent = pic.comments.length; 14 | 15 | newPicture.addEventListener('click', () => { 16 | getBigPicture(pic); 17 | }); 18 | pictureContainer.append(newPicture); 19 | }); 20 | } 21 | 22 | export {drawThumbnails}; -------------------------------------------------------------------------------- /js/data.js: -------------------------------------------------------------------------------- 1 | const chrome = { 2 | name: 'chrome', 3 | filter: 'grayscale', 4 | min: 0, 5 | max: 1, 6 | step: 0.1, 7 | start: 1, 8 | }; 9 | 10 | const sepia = { 11 | name: 'sepia', 12 | filter: 'sepia', 13 | min: 0, 14 | max: 1, 15 | step: 0.1, 16 | start: 1, 17 | }; 18 | 19 | const marvin = { 20 | name: 'marvin', 21 | filter: 'invert', 22 | min: 0, 23 | max: 100, 24 | step: 1, 25 | start: 100, 26 | symbol: '%', 27 | }; 28 | 29 | const phobos = { 30 | name: 'phobos', 31 | filter: 'blur', 32 | min: 0, 33 | max: 3, 34 | step: 0.1, 35 | start: 3, 36 | symbol: 'px', 37 | }; 38 | 39 | const heat = { 40 | name: 'heat', 41 | filter: 'brightness', 42 | min: 1, 43 | max: 3, 44 | step: 0.1, 45 | start: 3, 46 | }; 47 | 48 | export const settings = [chrome, sepia, marvin, phobos, heat]; 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "name": "kekstagram", 4 | "version": "26.0.0", 5 | "private": true, 6 | "description": "Личный проект «Кекстаграм (простой)» от HTML Academy", 7 | "repository": { 8 | "type": "git", 9 | "url": "git@github.com:htmlacademy-univer-javascript-1/2346039-kekstagram-3.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/htmlacademy-univer-javascript-1/2346039-kekstagram-3/issues" 13 | }, 14 | "devDependencies": { 15 | "browser-sync": "2.27.10", 16 | "eslint": "8.15.0", 17 | "eslint-config-htmlacademy": "5.1.2" 18 | }, 19 | "license": "MIT", 20 | "dependencies": {}, 21 | "scripts": { 22 | "start": "browser-sync start --server --no-ui --files \"js/**/*.js\"", 23 | "lint": "eslint js/" 24 | }, 25 | "engines": { 26 | "node": "16.x", 27 | "npm": "8.x" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /img/icon-warning.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 21 | ! 22 | 23 | 24 | -------------------------------------------------------------------------------- /img/avatar-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /js/draw.js: -------------------------------------------------------------------------------- 1 | const pictureTemplate = document.querySelector('#picture').content; 2 | const pictureExp = pictureTemplate.querySelector('.picture'); 3 | const pictures = document.querySelector('.pictures'); 4 | 5 | const createPicture = function (data) { 6 | const newPic = pictureExp.cloneNode(true); 7 | 8 | const newPicImg = newPic.querySelector('.picture__img'); 9 | newPicImg.src = data.url; 10 | const newPicInfo = newPic.querySelector('.picture__info'); 11 | const newPicComments = newPicInfo.querySelector('.picture__comments'); 12 | newPicComments.textContent = data.comments; 13 | const newPicLikes = newPicInfo.querySelector('.picture__likes'); 14 | newPicLikes.textContent = data.likes; 15 | 16 | return newPic; 17 | }; 18 | 19 | export const drawPictures = function (data) { 20 | const res = new DocumentFragment(); 21 | for (let i = 0; i < data.length; i++) { 22 | const newPic = createPicture(data[i]); 23 | res.append(newPic); 24 | pictures.append(res); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /js/validate.js: -------------------------------------------------------------------------------- 1 | import {sendData} from './load.js'; 2 | import {closeOverlay} from './form.js'; 3 | import {createSystemMessage} from './util.js'; 4 | 5 | const submitButton = document.querySelector('#upload-submit'); 6 | const form = document.querySelector('#upload-select-image'); 7 | 8 | const pristine = new Pristine(form, { 9 | classTo: 'img-upload__field-wrapper', 10 | errorTextParent: 'img-upload__field-wrapper', 11 | errorTextClass: 'img-upload__error-text' 12 | }); 13 | 14 | form.addEventListener('submit', (evt) => { 15 | evt.preventDefault(); 16 | const isValid = pristine.validate(); 17 | if (isValid) { 18 | submitButton.disabled = true; 19 | sendData( 20 | () => { 21 | closeOverlay(false); 22 | createSystemMessage('success'); 23 | submitButton.disabled = false; 24 | }, 25 | () => { 26 | closeOverlay(true); 27 | createSystemMessage('error'); 28 | submitButton.disabled = false; 29 | }, 30 | new FormData(evt.target), 31 | ); 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /js/scale.js: -------------------------------------------------------------------------------- 1 | const buttonSmaller = document.querySelector('.scale__control--smaller'); 2 | const buttonBigger = document.querySelector('.scale__control--bigger'); 3 | const scaleInput = document.querySelector('.scale__control--value'); 4 | const scaleValue = scaleInput.value; 5 | 6 | let scale = scaleValue.substring(0, scaleValue.length - 1); 7 | 8 | export const img = document.querySelector('.img-upload__preview').querySelector('img'); 9 | const scaleDefault = 100; 10 | 11 | export function resetScale() { 12 | scaleInput.value = `${scaleDefault }%`; 13 | img.style.transform = `scale(${scaleDefault / 100})`; 14 | scale = scaleDefault; 15 | } 16 | 17 | buttonSmaller.addEventListener('click', () => { 18 | if (scale > 25) { 19 | scale -= 25; 20 | scaleInput.value = `${scale}%`; 21 | img.style.transform = `scale(${scale / 100})`; 22 | } 23 | }); 24 | 25 | buttonBigger.addEventListener('click', () => { 26 | if (scale < 100) { 27 | scale += 25; 28 | scaleInput.value = `${scale}%`; 29 | img.style.transform = `scale(${scale / 100})`; 30 | } 31 | }); 32 | -------------------------------------------------------------------------------- /img/avatar-3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # Как работать с Git на проекте 2 | 3 | Поддерживайте ваш репозиторий обновлённым. Когда наставник принимает ваш пулреквест, он попадает в репозиторий Академии, но не в ваш форк. 4 | 5 | #### 1. Не коммитьте ничего самостоятельно в `master` вашего репозитория 6 | 7 | Это помешает вам аккуратно обновлять ваш репозиторий, могут возникнуть конфликты. 8 | 9 | #### 2. Прежде чем приступать к новому заданию, обновите `master` 10 | 11 | Обновить свой репозиторий из репозитория Академии можно так: 12 | 13 | ``` 14 | # В вашей локальной копии переключитесь в ветку master 15 | git checkout master 16 | 17 | # Заберите изменения из репозитория Академии¹ 18 | git pull academy master 19 | 20 | # Отправьте изменения в ваш форк на Гитхабе 21 | git push 22 | ``` 23 | 24 | ¹ В `academy` должна быть ссылка на репозиторий Академии. Если его там нет, добавьте: 25 | 26 | ``` 27 | git remote add academy git@github.com:htmlacademy-univer-javascript-1/2346377-kekstagram-3.git 28 | ``` 29 | 30 | Когда вы обновили `master`, создайте ветку для нового задания: 31 | 32 | ``` 33 | git checkout -b module2-task1 34 | ``` 35 | 36 | `module2-task1` — это название ветки. Под описанием каждого задания в интерфейсе интенсива для вас будет указано правильное название ветки. 37 | 38 | --- 39 | 40 | #### Есть вопрос? 41 | 42 | Посмотрите [коллекцию часто задаваемых вопросов по Git](http://firstaidgit.ru). 43 | -------------------------------------------------------------------------------- /js/form.js: -------------------------------------------------------------------------------- 1 | import {resetScale} from './scale.js'; 2 | import {resetEffects} from './effect.js'; 3 | 4 | const imgUpload = document.querySelector('#upload-file'); 5 | const imgUploadOverlay = document.querySelector('.img-upload__overlay'); 6 | const body = document.querySelector('body'); 7 | const closeButton = imgUploadOverlay.querySelector('#upload-cancel'); 8 | const hashtag = document.querySelector('.text__hashtags'); 9 | const description = document.querySelector('.text__description'); 10 | 11 | const onPopupEscKeydown = (evt) => { 12 | if (evt.key === 'Escape') { 13 | evt.preventDefault(); 14 | closeOverlay(false); 15 | } 16 | }; 17 | 18 | function openOverlay () { 19 | imgUploadOverlay.classList.remove('hidden'); 20 | body.classList.add('modal-open'); 21 | document.addEventListener('keydown', onPopupEscKeydown); 22 | } 23 | 24 | export function closeOverlay (saveData) { 25 | imgUploadOverlay.classList.add('hidden'); 26 | body.classList.remove('modal-open'); 27 | document.removeEventListener('keydown', onPopupEscKeydown); 28 | imgUpload.value = ''; 29 | if (!saveData) { 30 | resetScale(); 31 | resetEffects(); 32 | hashtag.value = ''; 33 | description.value = ''; 34 | } 35 | } 36 | 37 | imgUpload.addEventListener('change', () => { 38 | openOverlay(); 39 | }); 40 | 41 | closeButton.addEventListener('click', () => { 42 | closeOverlay(false); 43 | }); 44 | -------------------------------------------------------------------------------- /img/avatar-2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /js/imageScale.js: -------------------------------------------------------------------------------- 1 | const imgUploadScaleElement = document.querySelector('.img-upload__scale'); 2 | export const scaleControlSmallerElement = imgUploadScaleElement.querySelector('.scale__control--smaller'); 3 | export const scaleControlBiggerElement = imgUploadScaleElement.querySelector('.scale__control--bigger'); 4 | const scaleControlValue = imgUploadScaleElement.querySelector('.scale__control--value'); 5 | export const imgUploadPreview = document.querySelector('.img-upload__preview'); 6 | 7 | const STEP_COUNTER = 0.25; 8 | const MAXIMUM_SCALE_VALUE = 1; 9 | let defaultScaleNumber = 1; 10 | 11 | export const resetScaleSettings = () => { 12 | defaultScaleNumber = 1; 13 | imgUploadPreview.style.transform = 'scale(1.00)'; 14 | }; 15 | 16 | export const controlScaleButtonHandler = (element, button) => { 17 | scaleControlValue.value = `${Math.round(defaultScaleNumber * 100)}%`; 18 | element.addEventListener('click', button); 19 | }; 20 | 21 | const changeDefaultScaleValue = (defaultNum) => { 22 | scaleControlValue.value = `${Math.round(defaultNum * 100)}%`; 23 | imgUploadPreview.style.transform = `scale(${defaultNum})`; 24 | scaleControlValue.readonly = scaleControlValue.value; 25 | }; 26 | 27 | export const getScaleDecrease = () => { 28 | if (defaultScaleNumber !== STEP_COUNTER) { 29 | defaultScaleNumber -= STEP_COUNTER; 30 | changeDefaultScaleValue(defaultScaleNumber); 31 | } 32 | }; 33 | 34 | export const getScaleIncrease = () => { 35 | if (defaultScaleNumber !== MAXIMUM_SCALE_VALUE) { 36 | defaultScaleNumber += STEP_COUNTER; 37 | changeDefaultScaleValue(defaultScaleNumber); 38 | } 39 | }; -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Личный проект «Кекстаграм (простой)» 2 | 3 | * Студент: [Максим Горбук](https://up.htmlacademy.ru/univer-js1/3/user/2346377). 4 | * Наставник: Владимир Шишлянников. 5 | 6 | --- 7 | 8 | _Не удаляйте и не изменяйте самовольно файлы и папки:_ 9 | _`.editorconfig`, `.eslintrc`, `.gitattributes`, `.gitignore`, `package-lock.json`, `package.json`., `.github`_ 10 | 11 | --- 12 | 13 | [Как работать с Git на проекте](Contributing.md) | [Как работать над проектом](Workflow.md) 14 | 15 | ### Памятка 16 | 17 | #### 1. Зарегистрируйтесь на Гитхабе 18 | 19 | Если у вас ещё нет аккаунта на [github.com](https://github.com/join), скорее зарегистрируйтесь. 20 | 21 | #### 2. Создайте форк 22 | 23 | Откройте репозиторий и нажмите кнопку «Fork» в правом верхнем углу. Репозиторий из Академии будет скопирован в ваш аккаунт. 24 | 25 | 26 | 27 | Получится вот так: 28 | 29 | 30 | 31 | #### 3. Клонируйте репозиторий на свой компьютер 32 | 33 | Будьте внимательны: нужно клонировать свой репозиторий (форк), а не репозиторий Академии. Также обратите внимание, что клонировать репозиторий нужно через SSH, а не через HTTPS. Нажмите зелёную кнопку в правой части экрана, чтобы скопировать SSH-адрес вашего репозитория: 34 | 35 | 36 | 37 | Клонировать репозиторий можно так: 38 | 39 | ``` 40 | git clone SSH-адрес_вашего_форка 41 | ``` 42 | 43 | Команда клонирует репозиторий на ваш компьютер и подготовит всё необходимое для старта работы. 44 | 45 | #### 4. Начинайте обучение! 46 | -------------------------------------------------------------------------------- /js/util.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | export function createSystemMessage (downloadResult) { 4 | const body = document.querySelector('body'); 5 | const successTemplate = document.querySelector(`#${downloadResult}`).content.cloneNode(true); 6 | body.appendChild(successTemplate); 7 | 8 | const message = document.querySelector(`.${downloadResult}`); 9 | const closeButton = message.querySelector('button'); 10 | 11 | 12 | const onPopupEscKeydown = (evt) => { 13 | if (evt.key === 'Escape') { 14 | evt.preventDefault(); 15 | message.remove(); 16 | removeListeners(); 17 | } 18 | }; 19 | 20 | document.addEventListener('keydown', onPopupEscKeydown); 21 | 22 | const onOutsideOrButton = (evt) => { 23 | const clickButton = evt.composedPath().includes(closeButton); 24 | const clickInside = evt.composedPath().includes(message.querySelector('div')); 25 | if (clickButton || !clickInside) { 26 | message.remove(); 27 | removeListeners(); 28 | } 29 | }; 30 | 31 | document.addEventListener('click', onOutsideOrButton); 32 | 33 | function removeListeners () { 34 | document.removeEventListener('click', onOutsideOrButton); 35 | document.removeEventListener('keydown', onPopupEscKeydown); 36 | } 37 | } 38 | 39 | export const showAlert = (message) => { 40 | const alertContainer = document.createElement('div'); 41 | alertContainer.style.zIndex = '100'; 42 | alertContainer.style.position = 'absolute'; 43 | alertContainer.style.left = '0'; 44 | alertContainer.style.top = '0'; 45 | alertContainer.style.right = '0'; 46 | alertContainer.style.padding = '10px 3px'; 47 | alertContainer.style.fontSize = '20px'; 48 | alertContainer.style.textAlign = 'center'; 49 | alertContainer.style.backgroundColor = 'red'; 50 | 51 | alertContainer.textContent = message; 52 | document.body.append(alertContainer); 53 | 54 | setTimeout(() => { 55 | alertContainer.remove(); 56 | }, 5000); 57 | }; 58 | -------------------------------------------------------------------------------- /Workflow.md: -------------------------------------------------------------------------------- 1 | # Как работать над проектом 2 | 3 | ## Окружение 4 | 5 | Для удобства работы над проектом используются инструменты из **Node.js** и **npm**. Все необходимые настройки произведены. Всё, что от вас требуется,— это убедиться, что на рабочем компьютере установлена **Node.js**, а после в терминале перейти в директорию с проектом и _единожды_ запустить команду: 6 | 7 | ```bash 8 | npm install 9 | ``` 10 | 11 | Данная команда запустит процесс установки зависимостей проекта из **npm**. 12 | 13 | После успешной установки зависимостей вы сможете использовать инструменты для разработки, вроде **ESLint** и **Browsersync**, которые идут с проектом. Для этого в файле `package.json` предусмотрены следующие сценарии... 14 | 15 | ### `npm run start` 16 | 17 | Запускает локальный сервер с помощью **Browsersync**. После запуска сайт будет доступен для просмотра в браузере по адресу `http://localhost:3001`. 18 | 19 | При сохранении изменений в любом js-файле в директории `/js` страница автоматически перезагрузится в браузере. Таким образом, вы можете следить за разработкой проекта в режиме реального времени. 20 | 21 | > Обратите внимание, после запуска **Browsersync** продолжит работу, пока вы самостоятельно не остановите его, нажав в терминале сочетание клавиш `Ctrl` + `C`. 22 | 23 | ### `npm run lint` 24 | 25 | Запускает **ESLint** для линтинга js-файлов в директории `/js` по правилам и требованиям к JavaScript-коду, принятым в Академии. 26 | 27 | ## Структура проекта 28 | 29 | ### `css/`, `fonts/`, `img/` 30 | 31 | Директории со статическими файлами проекта: стилями, изображениями, шрифтами и т.д. 32 | 33 | ### `photos/` 34 | 35 | Директория с фотографиями, которые нужно будет отобразить на странице в одном из заданий. 36 | 37 | ### `js/` 38 | 39 | Ваша главная рабочая директория, в которой будут храниться все скрипты проекта. 40 | 41 | ### `index.html` 42 | 43 | Главная страница проекта. 44 | 45 | ### Остальное 46 | 47 | Все остальные файлы в проекте являются служебными. Пожалуйста, не удаляйте и не изменяйте их самовольно. Только если того требует задание или наставник. 48 | -------------------------------------------------------------------------------- /js/effect.js: -------------------------------------------------------------------------------- 1 | import {img} from './scale.js'; 2 | import {settings} from './data.js'; 3 | 4 | const sliderElement = document.querySelector('.effect-level__slider'); 5 | const valueElement = document.querySelector('.effect-level__value'); 6 | const effects = document.querySelector('.effects__list'); 7 | const defaultEffect = effects.querySelector('#effect-none'); 8 | 9 | let currentSettings = settings[0]; 10 | let effect = 'none'; 11 | 12 | noUiSlider.create(sliderElement, { 13 | range: { 14 | min: 0, 15 | max: 1, 16 | }, 17 | start: 1, 18 | connect: 'lower', 19 | }); 20 | 21 | function updateEffect (currentEffect) { 22 | sliderElement.noUiSlider.updateOptions({ 23 | range: { 24 | min: currentEffect.min, 25 | max: currentEffect.max, 26 | }, 27 | step: currentEffect.step, 28 | start: currentEffect.start, 29 | }); 30 | } 31 | 32 | sliderElement.noUiSlider.on('update', () => { 33 | if (effect === 'none') { 34 | valueElement.value = ''; 35 | img.style.filter = effect; 36 | sliderElement.classList.add('hidden'); 37 | } else { 38 | valueElement.value = sliderElement.noUiSlider.get(); 39 | let style; 40 | if (currentSettings.symbol) { 41 | style = `${currentSettings.filter}(${valueElement.value}${currentSettings.symbol})`; 42 | } else { 43 | style = `${currentSettings.filter}(${valueElement.value})`; 44 | } 45 | img.style.filter = style; 46 | } 47 | }); 48 | 49 | effects.addEventListener('click', (evt) => { 50 | if (evt.target.matches('input[type="radio"]')) { 51 | img.classList.remove(`effects__preview--${effect}`); 52 | effect = evt.target.value; 53 | img.classList.add(`effects__preview--${effect}`); 54 | 55 | if (effect === 'none') { 56 | updateEffect(currentSettings); 57 | } else { 58 | settings.forEach((element) => { 59 | if (element.name === effect) { 60 | currentSettings = element; 61 | updateEffect(currentSettings); 62 | sliderElement.classList.remove('hidden'); 63 | } 64 | }); 65 | } 66 | } 67 | }); 68 | 69 | export function resetEffects () { 70 | img.classList.remove(`effects__preview--${effect}`); 71 | defaultEffect.checked = true; 72 | valueElement.value = ''; 73 | img.style.filter = 'none'; 74 | sliderElement.classList.add('hidden'); 75 | } 76 | -------------------------------------------------------------------------------- /img/avatar-4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /img/avatar-6.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /js/api.js: -------------------------------------------------------------------------------- 1 | const INITIAL_EFFECT = 'none'; 2 | 3 | const imgUploadForm = document.querySelector('.img-upload__form'); 4 | const picture = imgUploadForm.querySelector('.img-upload__preview img'); 5 | const effectsList = imgUploadForm.querySelector('.effects__list'); 6 | const buttons = effectsList.querySelectorAll('.effects__radio'); 7 | const effectLevelSlider = imgUploadForm.querySelector('.effect-level__slider'); 8 | const effectLevelValue = imgUploadForm.querySelector('.effect-level__value'); 9 | 10 | const PICTURES_EFFECTS = { 11 | chrome: { 12 | filter: 'grayscale', 13 | range: {min: 0, max: 1.0}, 14 | step: 0.1, 15 | measurementUnit: ''}, 16 | sepia: { 17 | filter: 'sepia', 18 | range: {min: 0, max: 1.0}, 19 | step: 0.1, 20 | measurementUnit: ''}, 21 | marvin: { 22 | filter: 'invert', 23 | range: {min: 0, max: 100}, 24 | step: 1, 25 | measurementUnit: '%'}, 26 | phobos: { 27 | filter: 'blur', 28 | range: {min: 0, max: 3.0}, 29 | step: 0.1, 30 | measurementUnit: 'px'}, 31 | heat: { 32 | filter: 'brightness', 33 | range: {min: 1, max: 3.0}, 34 | step: 0.1, 35 | measurementUnit: ''} 36 | }; 37 | 38 | let currentEffect = INITIAL_EFFECT; 39 | 40 | const createEffectSlider = () => { 41 | noUiSlider.create(effectLevelSlider, { 42 | range: { 43 | min: 0, 44 | max: 1.0, 45 | }, 46 | start: 1.0, 47 | step: 0.1, 48 | connect: 'lower', 49 | }); 50 | }; 51 | 52 | const sliderConnector = () => { 53 | if (currentEffect !== 'none') { 54 | const effect = PICTURES_EFFECTS[currentEffect]; 55 | picture.style.filter = `${effect.filter}(${effectLevelSlider.noUiSlider.get()}${effect.measurementUnit})`; 56 | effectLevelValue.value = `${effectLevelSlider.noUiSlider.get()}${effect.measurementUnit}`; 57 | } else { 58 | picture.style.filter = ''; 59 | } 60 | }; 61 | 62 | const changeEffect = (effect) => { 63 | if ((currentEffect === 'none') !== (effectLevelSlider.classList.contains('hidden'))){ 64 | effectLevelSlider.classList.toggle('hidden'); 65 | } 66 | if (currentEffect !== 'none') { 67 | const effectObj = PICTURES_EFFECTS[effect]; 68 | effectLevelSlider.noUiSlider.updateOptions({ 69 | range: { 70 | min: effectObj.range.min, 71 | max: effectObj.range.max, 72 | }, 73 | start: effectObj.range.max, 74 | step: effectObj.step 75 | }); 76 | } 77 | else { 78 | picture.style.filter = ''; 79 | } 80 | }; 81 | 82 | const effectRadiosListener = () => { 83 | picture.classList.remove(`effects__preview--${currentEffect}`); 84 | buttons.forEach((button) => { 85 | if (button.checked) { 86 | currentEffect = button.value; 87 | changeEffect(currentEffect); 88 | } 89 | }); 90 | picture.classList.add(`effects__preview--${currentEffect}`); 91 | }; 92 | 93 | export const enableEffectPreview = () => { 94 | effectsList.addEventListener('click', effectRadiosListener); 95 | createEffectSlider(); 96 | changeEffect(currentEffect); 97 | effectLevelSlider.noUiSlider.set(parseFloat(effectLevelValue.value)); 98 | effectLevelSlider.noUiSlider.on('update', sliderConnector); 99 | if (currentEffect === 'none') { 100 | effectLevelSlider.classList.add('hidden'); 101 | } 102 | }; 103 | 104 | export const disableEffectPreview = () => { 105 | picture.classList.remove(`effects__preview--${currentEffect}`); 106 | effectsList.removeEventListener('click', effectRadiosListener); 107 | effectLevelSlider.noUiSlider.destroy(); 108 | }; 109 | 110 | export const resetEffect = () => { 111 | currentEffect = INITIAL_EFFECT; 112 | imgUploadForm.reset(); 113 | }; -------------------------------------------------------------------------------- /js/addEffect.js: -------------------------------------------------------------------------------- 1 | const INITIAL_EFFECT = 'none'; 2 | 3 | const imgUploadForm = document.querySelector('.img-upload__form'); 4 | const picture = imgUploadForm.querySelector('.img-upload__preview img'); 5 | const effectsList = imgUploadForm.querySelector('.effects__list'); 6 | const buttons = effectsList.querySelectorAll('.effects__radio'); 7 | const effectLevelSlider = imgUploadForm.querySelector('.effect-level__slider'); 8 | const effectLevelValue = imgUploadForm.querySelector('.effect-level__value'); 9 | 10 | const PICTURES_EFFECTS = { 11 | chrome: { 12 | filter: 'grayscale', 13 | range: {min: 0, max: 1.0}, 14 | step: 0.1, 15 | measurementUnit: ''}, 16 | sepia: { 17 | filter: 'sepia', 18 | range: {min: 0, max: 1.0}, 19 | step: 0.1, 20 | measurementUnit: ''}, 21 | marvin: { 22 | filter: 'invert', 23 | range: {min: 0, max: 100}, 24 | step: 1, 25 | measurementUnit: '%'}, 26 | phobos: { 27 | filter: 'blur', 28 | range: {min: 0, max: 3.0}, 29 | step: 0.1, 30 | measurementUnit: 'px'}, 31 | heat: { 32 | filter: 'brightness', 33 | range: {min: 1, max: 3.0}, 34 | step: 0.1, 35 | measurementUnit: ''} 36 | }; 37 | 38 | let currentEffect = INITIAL_EFFECT; 39 | 40 | const createEffectSlider = () => { 41 | noUiSlider.create(effectLevelSlider, { 42 | range: { 43 | min: 0, 44 | max: 1.0, 45 | }, 46 | start: 1.0, 47 | step: 0.1, 48 | connect: 'lower', 49 | }); 50 | }; 51 | 52 | const sliderConnector = () => { 53 | if (currentEffect !== 'none') { 54 | const effect = PICTURES_EFFECTS[currentEffect]; 55 | picture.style.filter = `${effect.filter}(${effectLevelSlider.noUiSlider.get()}${effect.measurementUnit})`; 56 | effectLevelValue.value = `${effectLevelSlider.noUiSlider.get()}${effect.measurementUnit}`; 57 | } else { 58 | picture.style.filter = ''; 59 | } 60 | }; 61 | 62 | const changeEffect = (effect) => { 63 | if ((currentEffect === 'none') !== (effectLevelSlider.classList.contains('hidden'))){ 64 | effectLevelSlider.classList.toggle('hidden'); 65 | } 66 | if (currentEffect !== 'none') { 67 | const effectObj = PICTURES_EFFECTS[effect]; 68 | effectLevelSlider.noUiSlider.updateOptions({ 69 | range: { 70 | min: effectObj.range.min, 71 | max: effectObj.range.max, 72 | }, 73 | start: effectObj.range.max, 74 | step: effectObj.step 75 | }); 76 | } 77 | else { 78 | picture.style.filter = ''; 79 | } 80 | }; 81 | 82 | const effectRadiosListener = () => { 83 | picture.classList.remove(`effects__preview--${currentEffect}`); 84 | buttons.forEach((button) => { 85 | if (button.checked) { 86 | currentEffect = button.value; 87 | changeEffect(currentEffect); 88 | } 89 | }); 90 | picture.classList.add(`effects__preview--${currentEffect}`); 91 | }; 92 | 93 | export const enableEffectPreview = () => { 94 | effectsList.addEventListener('click', effectRadiosListener); 95 | createEffectSlider(); 96 | changeEffect(currentEffect); 97 | effectLevelSlider.noUiSlider.set(parseFloat(effectLevelValue.value)); 98 | effectLevelSlider.noUiSlider.on('update', sliderConnector); 99 | if (currentEffect === 'none') { 100 | effectLevelSlider.classList.add('hidden'); 101 | } 102 | }; 103 | 104 | export const disableEffectPreview = () => { 105 | picture.classList.remove(`effects__preview--${currentEffect}`); 106 | effectsList.removeEventListener('click', effectRadiosListener); 107 | effectLevelSlider.noUiSlider.destroy(); 108 | }; 109 | 110 | export const resetEffect = () => { 111 | currentEffect = INITIAL_EFFECT; 112 | imgUploadForm.reset(); 113 | }; -------------------------------------------------------------------------------- /img/spinner-blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /js/bigPicture.js: -------------------------------------------------------------------------------- 1 | import {closeModal, isEscapeKey, openModal} from './util.js'; 2 | 3 | const COMMENTS_NUMBER_LOAD = 5; 4 | 5 | const bigPicture = document.querySelector('.big-picture'); 6 | const fullSizePictureImage = bigPicture.querySelector('.big-picture__img').querySelector('img'); 7 | const bigPictureCloseButton = bigPicture.querySelector('.big-picture__cancel'); 8 | const commentsCount = bigPicture.querySelector('.comments-count'); 9 | const likesCount = bigPicture.querySelector('span.likes-count'); 10 | const socialComments = bigPicture.querySelector('.social__comments'); 11 | const photoDescription = bigPicture.querySelector('.social__caption'); 12 | const socialCommentsCounter = bigPicture.querySelector('.social__comment-count'); 13 | const commentsLoader = bigPicture.querySelector('.comments-loader'); 14 | const bodyContainer = document.querySelector('body'); 15 | 16 | let shownCommentsCounter = 0; 17 | let actualComments = []; 18 | 19 | 20 | const closeBigPicture = () => { 21 | shownCommentsCounter = 0; 22 | closeModal(bigPicture, bodyContainer); 23 | }; 24 | 25 | const getClosedByEscape = (evt) => { 26 | if (isEscapeKey(evt)) { 27 | evt.preventDefault(); 28 | shownCommentsCounter = 0; 29 | closeBigPicture(); 30 | } 31 | }; 32 | 33 | const removeCommentsLoader = () => { 34 | commentsLoader.classList.add('hidden'); 35 | }; 36 | 37 | const removeDefaultSocialComments = () => { 38 | while (socialComments.firstChild) { 39 | socialComments.removeChild(socialComments.lastChild); 40 | } 41 | }; 42 | 43 | const makeElementTemplate = (tagName, className) => { 44 | const element = document.createElement(tagName); 45 | element.classList.add(className); 46 | return element; 47 | }; 48 | 49 | const createSocialCommentsTemplate = (data) => { 50 | const AVATAR_WIDTH = '35'; 51 | const AVATAR_HEIGHT = '35'; 52 | const socialComment = makeElementTemplate('li', 'social__comment'); 53 | const avatarImage = makeElementTemplate('img', 'social__picture'); 54 | avatarImage.src = data.avatar; 55 | avatarImage.alt = data.name; 56 | avatarImage.width = AVATAR_WIDTH; 57 | avatarImage.height = AVATAR_HEIGHT; 58 | socialComment.append(avatarImage); 59 | const paragraphElement = makeElementTemplate('p', 'social__text'); 60 | paragraphElement.textContent = data.message; 61 | socialComment.appendChild(paragraphElement); 62 | return socialComment; 63 | }; 64 | 65 | const getCommentsNumber = (count, totalComments) => { 66 | let commentsDeclination; 67 | if (totalComments % 10 === 1 && totalComments % 100 !== 11) { 68 | commentsDeclination = 'комментария'; 69 | } else { 70 | commentsDeclination = 'комментариев'; 71 | } 72 | socialCommentsCounter.innerHTML = `${count} из ${totalComments} ${commentsDeclination}`; 73 | }; 74 | 75 | const getComment = () => { 76 | const commentFragment = document.createDocumentFragment(); 77 | const currentComments = actualComments.splice(0, COMMENTS_NUMBER_LOAD); 78 | shownCommentsCounter += currentComments.length; 79 | 80 | currentComments.forEach((comment) => commentFragment.append(createSocialCommentsTemplate(comment))); 81 | if (actualComments.length < 1) { 82 | removeCommentsLoader(); 83 | } 84 | getCommentsNumber(shownCommentsCounter, commentsCount.textContent); 85 | socialComments.append(commentFragment); 86 | }; 87 | 88 | export const getBigPicture = (picture) => { 89 | openModal(bigPicture, bodyContainer); 90 | commentsLoader.classList.remove('hidden'); 91 | commentsLoader.addEventListener('click', getComment); 92 | bigPictureCloseButton.addEventListener('click', closeBigPicture); 93 | document.addEventListener('keydown', getClosedByEscape); 94 | removeDefaultSocialComments(); 95 | photoDescription.textContent = picture.description; 96 | fullSizePictureImage.src = picture.url; 97 | likesCount.textContent = picture.likes; 98 | commentsCount.textContent = String(picture.comments.length); 99 | actualComments = picture.comments.slice(); 100 | getComment(actualComments); 101 | }; -------------------------------------------------------------------------------- /img/spinner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /js/loaderForm.js: -------------------------------------------------------------------------------- 1 | import {openModal, closeModal, isEscapeKey} from './util.js'; 2 | import {pristine, validateForm} from './validetion.js'; 3 | import {controlScaleButtonHandler, getScaleDecrease, getScaleIncrease, 4 | scaleControlBiggerElement, scaleControlSmallerElement, resetScaleSettings} from './imageScale.js'; 5 | import {enableEffectPreview, disableEffectPreview, resetEffect} from './addEffect.js'; 6 | import {sendData} from './api.js'; 7 | 8 | const body = document.querySelector('body'); 9 | const imgUploadForm = document.querySelector('.img-upload__form'); 10 | const uploadFile = imgUploadForm.querySelector('.img-upload__input'); 11 | const imgUploadOverlay = imgUploadForm.querySelector('.img-upload__overlay'); 12 | const closeButton = imgUploadForm.querySelector('.img-upload__cancel'); 13 | const textHashtagsInput = imgUploadForm.querySelector('.text__hashtags'); 14 | const textDescriptionInput = imgUploadForm.querySelector('.text__description'); 15 | const uploadFormSubmitButtonElement = imgUploadForm.querySelector('.img-upload__submit'); 16 | const successTemplate = document.querySelector('#success').content.querySelector('section'); 17 | const errorTemplate = document.querySelector('#error').content.querySelector('section'); 18 | 19 | const propagationStopper = (evt) => evt.stopPropagation(); 20 | 21 | const closeOverlay = () => { 22 | closeModal(imgUploadOverlay, body); 23 | closeButton.removeEventListener('click', closeButtonListener); 24 | document.removeEventListener('keydown', escListener); 25 | textHashtagsInput.removeEventListener('keydown', propagationStopper); 26 | textDescriptionInput.removeEventListener('keydown', propagationStopper); 27 | resetScaleSettings(); 28 | disableEffectPreview(); 29 | }; 30 | 31 | const renderImageEditor = () => { 32 | openModal(imgUploadOverlay, body); 33 | textHashtagsInput.addEventListener('keydown', propagationStopper); 34 | textHashtagsInput.addEventListener('input', validateForm); 35 | textDescriptionInput.addEventListener('keydown', propagationStopper); 36 | closeButton.addEventListener('click', closeButtonListener); 37 | document.addEventListener('keydown', escListener); 38 | controlScaleButtonHandler(scaleControlSmallerElement, getScaleDecrease); 39 | controlScaleButtonHandler(scaleControlBiggerElement, getScaleIncrease); 40 | enableEffectPreview(); 41 | }; 42 | 43 | function closeButtonListener() { 44 | closeOverlay(); 45 | resetEffect(); 46 | } 47 | 48 | function escListener(evt) { 49 | if (isEscapeKey(evt)) { 50 | closeOverlay(); 51 | resetEffect(); 52 | } 53 | } 54 | 55 | const blockSubmitButton = () => { 56 | uploadFormSubmitButtonElement.disabled = true; 57 | }; 58 | 59 | const unblockSubmitButton = () => { 60 | uploadFormSubmitButtonElement.disabled = false; 61 | }; 62 | 63 | const openOrCloseMessage = (message) => { 64 | body.appendChild(message); 65 | document.addEventListener('keydown', closeByEsc); 66 | const closeMessage = () => { 67 | message.remove(); 68 | document.removeEventListener('keydown', closeByEsc); 69 | }; 70 | message.addEventListener('click', (evt) => { 71 | if (evt.target.tagName !== 'DIV' && evt.target.tagName !== 'H2'){ 72 | closeMessage(); 73 | } 74 | }); 75 | function closeByEsc(evt) { 76 | if (isEscapeKey(evt)) { 77 | closeMessage(); 78 | } 79 | } 80 | }; 81 | 82 | const showErrorMessageModal = () => { 83 | const message = errorTemplate.cloneNode(true); 84 | openOrCloseMessage(message); 85 | }; 86 | 87 | const showSuccessMessageModal = () => { 88 | const message = successTemplate.cloneNode(true); 89 | openOrCloseMessage(message); 90 | }; 91 | 92 | imgUploadForm.addEventListener('submit', (evt) => { 93 | evt.preventDefault(); 94 | if (pristine.validate()) { 95 | blockSubmitButton(); 96 | sendData ( 97 | () => { 98 | unblockSubmitButton(); 99 | closeOverlay(); 100 | showSuccessMessageModal(); 101 | resetEffect(); 102 | }, 103 | () => { 104 | showErrorMessageModal(); 105 | closeOverlay(); 106 | unblockSubmitButton(); 107 | uploadFile.value = ''; 108 | }, 109 | new FormData(imgUploadForm) 110 | ); 111 | } 112 | }); 113 | 114 | uploadFile.addEventListener('change', (evt) => { 115 | evt.preventDefault(); 116 | renderImageEditor(); 117 | }); -------------------------------------------------------------------------------- /img/htmla-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /pristine/pristine.min.js: -------------------------------------------------------------------------------- 1 | !function(e,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):(e="undefined"!=typeof globalThis?globalThis:e||self).Pristine=r()}(this,(function(){"use strict";var e={en:{required:"This field is required",email:"This field requires a valid e-mail address",number:"This field requires a number",integer:"This field requires an integer value",url:"This field requires a valid website URL",tel:"This field requires a valid telephone number",maxlength:"This fields length must be < ${1}",minlength:"This fields length must be > ${1}",min:"Minimum value for this field is ${1}",max:"Maximum value for this field is ${1}",pattern:"Please match the requested format",equals:"The two fields do not match"}};function r(e){var r=arguments;return this.replace(/\${([^{}]*)}/g,(function(e,t){return r[t]}))}function t(e){return e.pristine.self.form.querySelectorAll('input[name="'+e.getAttribute("name")+'"]:checked').length}var n={classTo:"form-group",errorClass:"has-danger",successClass:"has-success",errorTextParent:"form-group",errorTextTag:"div",errorTextClass:"text-help"},i=["required","min","max","minlength","maxlength","pattern"],s=/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,a=/-message(?:-([a-z]{2}(?:_[A-Z]{2})?))?/,o="en",l={},u=function(e,r){r.name=e,void 0===r.priority&&(r.priority=1),l[e]=r};function f(t,s,u){var f=this;function c(e,r,t,n){var i=l[t];if(i&&(e.push(i),n)){var s="pattern"===t?[n]:n.split(",");s.unshift(null),r[t]=s}}function p(t){for(var n=[],i=!0,s=0;t.validators[s];s++){var a=t.validators[s],l=t.params[a.name]?t.params[a.name]:[];if(l[0]=t.input.value,!a.fn.apply(t.input,l)&&(i=!1,"function"==typeof a.msg?n.push(a.msg(t.input.value,l)):"string"==typeof a.msg?n.push(r.apply(a.msg,l)):a.msg===Object(a.msg)&&a.msg[o]?n.push(r.apply(a.msg[o],l)):t.messages[o]&&t.messages[o][a.name]?n.push(r.apply(t.messages[o][a.name],l)):e[o]&&e[o][a.name]&&n.push(r.apply(e[o][a.name],l)),!0===a.halt))break}return t.errors=n,i}function m(e){if(e.errorElements)return e.errorElements;var r=function(e,r){for(;(e=e.parentElement)&&!e.classList.contains(r););return e}(e.input,f.config.classTo),t=null,n=null;return(t=f.config.classTo===f.config.errorTextParent?r:r.querySelector("."+f.config.errorTextParent))&&((n=t.querySelector(".pristine-error"))||((n=document.createElement(f.config.errorTextTag)).className="pristine-error "+f.config.errorTextClass,t.appendChild(n),n.pristineDisplay=n.style.display)),e.errorElements=[r,n]}function d(e){var r=m(e),t=r[0],n=r[1];t&&(t.classList.remove(f.config.successClass),t.classList.add(f.config.errorClass)),n&&(n.innerHTML=e.errors.join("
"),n.style.display=n.pristineDisplay||"")}function h(e){var r=function(e){var r=m(e),t=r[0],n=r[1];return t&&(t.classList.remove(f.config.errorClass),t.classList.remove(f.config.successClass)),n&&(n.innerHTML="",n.style.display="none"),r}(e)[0];r&&r.classList.add(f.config.successClass)}return function(e,r,t){e.setAttribute("novalidate","true"),f.form=e,f.config=function(e,r){for(var t in r)t in e||(e[t]=r[t]);return e}(r||{},n),f.live=!(!1===t),f.fields=Array.from(e.querySelectorAll("input:not([type^=hidden]):not([type^=submit]), select, textarea")).map(function(e){var r=[],t={},n={};return[].forEach.call(e.attributes,(function(e){if(/^data-pristine-/.test(e.name)){var s=e.name.substr(14),o=s.match(a);if(null!==o){var l=void 0===o[1]?"en":o[1];return n.hasOwnProperty(l)||(n[l]={}),void(n[l][s.slice(0,s.length-o[0].length)]=e.value)}"type"===s&&(s=e.value),c(r,t,s,e.value)}else~i.indexOf(e.name)?c(r,t,e.name,e.value):"type"===e.name&&c(r,t,e.value)})),r.sort((function(e,r){return r.priority-e.priority})),f.live&&e.addEventListener(~["radio","checkbox"].indexOf(e.getAttribute("type"))?"change":"input",function(e){f.validate(e.target)}.bind(f)),e.pristine={input:e,validators:r,params:t,messages:n,self:f}}.bind(f))}(t,s,u),f.validate=function(e,r){r=e&&!0===r||!0===e;var t=f.fields;!0!==e&&!1!==e&&(e instanceof HTMLElement?t=[e.pristine]:(e instanceof NodeList||e instanceof(window.$||Array)||e instanceof Array)&&(t=Array.from(e).map((function(e){return e.pristine}))));for(var n=!0,i=0;t[i];i++){var s=t[i];p(s)?!r&&h(s):(n=!1,!r&&d(s))}return n},f.getErrors=function(e){if(!e){for(var r=[],t=0;t=parseInt(r)}}),u("maxlength",{fn:function(e,r){return!e||e.length<=parseInt(r)}}),u("min",{fn:function(e,r){return!e||("checkbox"===this.type?t(this)>=parseInt(r):parseFloat(e)>=parseFloat(r))}}),u("max",{fn:function(e,r){return!e||("checkbox"===this.type?t(this)<=parseInt(r):parseFloat(e)<=parseFloat(r))}}),u("pattern",{fn:function(e,r){var t=r.match(new RegExp("^/(.*?)/([gimy]*)$"));return!e||new RegExp(t[1],t[2]).test(e)}}),u("equals",{fn:function(e,r){var t=document.querySelector(r);return t&&(!e&&!t.value||t.value===e)}}),f.addValidator=function(e,r,t,n,i){u(e,{fn:r,msg:t,priority:n,halt:i})},f.addMessages=function(r,t){var n=e.hasOwnProperty(r)?e[r]:e[r]={};Object.keys(t).forEach((function(e,r){n[e]=t[e]}))},f.setLocale=function(e){o=e},f})); 2 | -------------------------------------------------------------------------------- /img/upload-button.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /nouislider/nouislider.css: -------------------------------------------------------------------------------- 1 | /*! nouislider - 15.6.0 - 05/01/2022 */ 2 | /* Functional styling; 3 | * These styles are required for noUiSlider to function. 4 | * You don't need to change these rules to apply your design. 5 | */ 6 | .noUi-target, 7 | .noUi-target * { 8 | -webkit-touch-callout: none; 9 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 10 | -webkit-user-select: none; 11 | -ms-touch-action: none; 12 | touch-action: none; 13 | -ms-user-select: none; 14 | -moz-user-select: none; 15 | user-select: none; 16 | -moz-box-sizing: border-box; 17 | box-sizing: border-box; 18 | } 19 | .noUi-target { 20 | position: relative; 21 | } 22 | .noUi-base, 23 | .noUi-connects { 24 | width: 100%; 25 | height: 100%; 26 | position: relative; 27 | z-index: 1; 28 | } 29 | /* Wrapper for all connect elements. 30 | */ 31 | .noUi-connects { 32 | overflow: hidden; 33 | z-index: 0; 34 | } 35 | .noUi-connect, 36 | .noUi-origin { 37 | will-change: transform; 38 | position: absolute; 39 | z-index: 1; 40 | top: 0; 41 | right: 0; 42 | height: 100%; 43 | width: 100%; 44 | -ms-transform-origin: 0 0; 45 | -webkit-transform-origin: 0 0; 46 | -webkit-transform-style: preserve-3d; 47 | transform-origin: 0 0; 48 | transform-style: flat; 49 | } 50 | /* Offset direction 51 | */ 52 | .noUi-txt-dir-rtl.noUi-horizontal .noUi-origin { 53 | left: 0; 54 | right: auto; 55 | } 56 | /* Give origins 0 height/width so they don't interfere with clicking the 57 | * connect elements. 58 | */ 59 | .noUi-vertical .noUi-origin { 60 | top: -100%; 61 | width: 0; 62 | } 63 | .noUi-horizontal .noUi-origin { 64 | height: 0; 65 | } 66 | .noUi-handle { 67 | -webkit-backface-visibility: hidden; 68 | backface-visibility: hidden; 69 | position: absolute; 70 | } 71 | .noUi-touch-area { 72 | height: 100%; 73 | width: 100%; 74 | } 75 | .noUi-state-tap .noUi-connect, 76 | .noUi-state-tap .noUi-origin { 77 | -webkit-transition: transform 0.3s; 78 | transition: transform 0.3s; 79 | } 80 | .noUi-state-drag * { 81 | cursor: inherit !important; 82 | } 83 | /* Slider size and handle placement; 84 | */ 85 | .noUi-horizontal { 86 | height: 18px; 87 | } 88 | .noUi-horizontal .noUi-handle { 89 | width: 34px; 90 | height: 28px; 91 | right: -17px; 92 | top: -6px; 93 | } 94 | .noUi-vertical { 95 | width: 18px; 96 | } 97 | .noUi-vertical .noUi-handle { 98 | width: 28px; 99 | height: 34px; 100 | right: -6px; 101 | bottom: -17px; 102 | } 103 | .noUi-txt-dir-rtl.noUi-horizontal .noUi-handle { 104 | left: -17px; 105 | right: auto; 106 | } 107 | /* Styling; 108 | * Giving the connect element a border radius causes issues with using transform: scale 109 | */ 110 | .noUi-target { 111 | background: #FAFAFA; 112 | border-radius: 4px; 113 | border: 1px solid #D3D3D3; 114 | box-shadow: inset 0 1px 1px #F0F0F0, 0 3px 6px -5px #BBB; 115 | } 116 | .noUi-connects { 117 | border-radius: 3px; 118 | } 119 | .noUi-connect { 120 | background: #3FB8AF; 121 | } 122 | /* Handles and cursors; 123 | */ 124 | .noUi-draggable { 125 | cursor: ew-resize; 126 | } 127 | .noUi-vertical .noUi-draggable { 128 | cursor: ns-resize; 129 | } 130 | .noUi-handle { 131 | border: 1px solid #D9D9D9; 132 | border-radius: 3px; 133 | background: #FFF; 134 | cursor: default; 135 | box-shadow: inset 0 0 1px #FFF, inset 0 1px 7px #EBEBEB, 0 3px 6px -3px #BBB; 136 | } 137 | .noUi-active { 138 | box-shadow: inset 0 0 1px #FFF, inset 0 1px 7px #DDD, 0 3px 6px -3px #BBB; 139 | } 140 | /* Handle stripes; 141 | */ 142 | .noUi-handle:before, 143 | .noUi-handle:after { 144 | content: ""; 145 | display: block; 146 | position: absolute; 147 | height: 14px; 148 | width: 1px; 149 | background: #E8E7E6; 150 | left: 14px; 151 | top: 6px; 152 | } 153 | .noUi-handle:after { 154 | left: 17px; 155 | } 156 | .noUi-vertical .noUi-handle:before, 157 | .noUi-vertical .noUi-handle:after { 158 | width: 14px; 159 | height: 1px; 160 | left: 6px; 161 | top: 14px; 162 | } 163 | .noUi-vertical .noUi-handle:after { 164 | top: 17px; 165 | } 166 | /* Disabled state; 167 | */ 168 | [disabled] .noUi-connect { 169 | background: #B8B8B8; 170 | } 171 | [disabled].noUi-target, 172 | [disabled].noUi-handle, 173 | [disabled] .noUi-handle { 174 | cursor: not-allowed; 175 | } 176 | /* Base; 177 | * 178 | */ 179 | .noUi-pips, 180 | .noUi-pips * { 181 | -moz-box-sizing: border-box; 182 | box-sizing: border-box; 183 | } 184 | .noUi-pips { 185 | position: absolute; 186 | color: #999; 187 | } 188 | /* Values; 189 | * 190 | */ 191 | .noUi-value { 192 | position: absolute; 193 | white-space: nowrap; 194 | text-align: center; 195 | } 196 | .noUi-value-sub { 197 | color: #ccc; 198 | font-size: 10px; 199 | } 200 | /* Markings; 201 | * 202 | */ 203 | .noUi-marker { 204 | position: absolute; 205 | background: #CCC; 206 | } 207 | .noUi-marker-sub { 208 | background: #AAA; 209 | } 210 | .noUi-marker-large { 211 | background: #AAA; 212 | } 213 | /* Horizontal layout; 214 | * 215 | */ 216 | .noUi-pips-horizontal { 217 | padding: 10px 0; 218 | height: 80px; 219 | top: 100%; 220 | left: 0; 221 | width: 100%; 222 | } 223 | .noUi-value-horizontal { 224 | -webkit-transform: translate(-50%, 50%); 225 | transform: translate(-50%, 50%); 226 | } 227 | .noUi-rtl .noUi-value-horizontal { 228 | -webkit-transform: translate(50%, 50%); 229 | transform: translate(50%, 50%); 230 | } 231 | .noUi-marker-horizontal.noUi-marker { 232 | margin-left: -1px; 233 | width: 2px; 234 | height: 5px; 235 | } 236 | .noUi-marker-horizontal.noUi-marker-sub { 237 | height: 10px; 238 | } 239 | .noUi-marker-horizontal.noUi-marker-large { 240 | height: 15px; 241 | } 242 | /* Vertical layout; 243 | * 244 | */ 245 | .noUi-pips-vertical { 246 | padding: 0 10px; 247 | height: 100%; 248 | top: 0; 249 | left: 100%; 250 | } 251 | .noUi-value-vertical { 252 | -webkit-transform: translate(0, -50%); 253 | transform: translate(0, -50%); 254 | padding-left: 25px; 255 | } 256 | .noUi-rtl .noUi-value-vertical { 257 | -webkit-transform: translate(0, 50%); 258 | transform: translate(0, 50%); 259 | } 260 | .noUi-marker-vertical.noUi-marker { 261 | width: 5px; 262 | height: 2px; 263 | margin-top: -1px; 264 | } 265 | .noUi-marker-vertical.noUi-marker-sub { 266 | width: 10px; 267 | } 268 | .noUi-marker-vertical.noUi-marker-large { 269 | width: 15px; 270 | } 271 | .noUi-tooltip { 272 | display: block; 273 | position: absolute; 274 | border: 1px solid #D9D9D9; 275 | border-radius: 3px; 276 | background: #fff; 277 | color: #000; 278 | padding: 5px; 279 | text-align: center; 280 | white-space: nowrap; 281 | } 282 | .noUi-horizontal .noUi-tooltip { 283 | -webkit-transform: translate(-50%, 0); 284 | transform: translate(-50%, 0); 285 | left: 50%; 286 | bottom: 120%; 287 | } 288 | .noUi-vertical .noUi-tooltip { 289 | -webkit-transform: translate(0, -50%); 290 | transform: translate(0, -50%); 291 | top: 50%; 292 | right: 120%; 293 | } 294 | .noUi-horizontal .noUi-origin > .noUi-tooltip { 295 | -webkit-transform: translate(50%, 0); 296 | transform: translate(50%, 0); 297 | left: auto; 298 | bottom: 10px; 299 | } 300 | .noUi-vertical .noUi-origin > .noUi-tooltip { 301 | -webkit-transform: translate(0, -18px); 302 | transform: translate(0, -18px); 303 | top: auto; 304 | right: 28px; 305 | } 306 | -------------------------------------------------------------------------------- /img/avatar-5.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.scss v0.1.0 | MIT License | based on git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `block-borderlate` element in IE 8/9/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | block-borderlate { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability when focused and also mouse hovered in all browsers. 95 | */ 96 | 97 | a:active, 98 | a:hover { 99 | outline: 0; 100 | } 101 | 102 | /* Text-level semantics 103 | ========================================================================== */ 104 | 105 | /** 106 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 107 | */ 108 | 109 | abbr[title] { 110 | border-bottom: 1px dotted; 111 | } 112 | 113 | /** 114 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 115 | */ 116 | 117 | b, 118 | strong { 119 | font-weight: bold; 120 | } 121 | 122 | /** 123 | * Address styling not present in Safari and Chrome. 124 | */ 125 | 126 | dfn { 127 | font-style: italic; 128 | } 129 | 130 | /** 131 | * Address variable `h1` font-size and margin within `section` and `article` 132 | * contexts in Firefox 4+, Safari, and Chrome. 133 | */ 134 | 135 | h1 { 136 | font-size: 2em; 137 | margin: 0.67em 0; 138 | } 139 | 140 | /** 141 | * Address styling not present in IE 8/9. 142 | */ 143 | 144 | mark { 145 | background: #ff0; 146 | color: #000; 147 | } 148 | 149 | /** 150 | * Address inconsistent and variable font size in all browsers. 151 | */ 152 | 153 | small { 154 | font-size: 80%; 155 | } 156 | 157 | /** 158 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 159 | */ 160 | 161 | sub, 162 | sup { 163 | font-size: 75%; 164 | line-height: 0; 165 | position: relative; 166 | vertical-align: baseline; 167 | } 168 | 169 | sup { 170 | top: -0.5em; 171 | } 172 | 173 | sub { 174 | bottom: -0.25em; 175 | } 176 | 177 | /* Embedded content 178 | ========================================================================== */ 179 | 180 | /** 181 | * Remove border when inside `a` element in IE 8/9/10. 182 | */ 183 | 184 | img { 185 | border: 0; 186 | } 187 | 188 | /** 189 | * Correct overflow not hidden in IE 9/10/11. 190 | */ 191 | 192 | svg:not(:root) { 193 | overflow: hidden; 194 | } 195 | 196 | /* Grouping content 197 | ========================================================================== */ 198 | 199 | /** 200 | * Address margin not present in IE 8/9 and Safari. 201 | */ 202 | 203 | figure { 204 | margin: 1em 40px; 205 | } 206 | 207 | /** 208 | * Address differences between Firefox and other browsers. 209 | */ 210 | 211 | hr { 212 | -moz-box-sizing: content-box; 213 | box-sizing: content-box; 214 | height: 0; 215 | } 216 | 217 | /** 218 | * Contain overflow in all browsers. 219 | */ 220 | 221 | pre { 222 | overflow: auto; 223 | } 224 | 225 | /** 226 | * Address odd `em`-unit font size rendering in all browsers. 227 | */ 228 | 229 | code, 230 | kbd, 231 | pre, 232 | samp { 233 | font-family: monospace, monospace; 234 | font-size: 1em; 235 | } 236 | 237 | /* Forms 238 | ========================================================================== */ 239 | 240 | /** 241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 242 | * styling of `select`, unless a `border` property is set. 243 | */ 244 | 245 | /** 246 | * 1. Correct color not being inherited. 247 | * Known issue: affects color of disabled elements. 248 | * 2. Correct font properties not being inherited. 249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | color: inherit; /* 1 */ 258 | font: inherit; /* 2 */ 259 | margin: 0; /* 3 */ 260 | } 261 | 262 | /** 263 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 264 | */ 265 | 266 | button { 267 | overflow: visible; 268 | } 269 | 270 | /** 271 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 272 | * All other form control elements do not inherit `text-transform` values. 273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 274 | * Correct `select` style inheritance in Firefox. 275 | */ 276 | 277 | button, 278 | select { 279 | text-transform: none; 280 | } 281 | 282 | /** 283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 284 | * and `video` controls. 285 | * 2. Correct inability to style clickable `input` types in iOS. 286 | * 3. Improve usability and consistency of cursor style between image-type 287 | * `input` and others. 288 | */ 289 | 290 | button, 291 | html input[type="button"], /* 1 */ 292 | input[type="reset"], 293 | input[type="submit"] { 294 | -webkit-appearance: button; /* 2 */ 295 | cursor: pointer; /* 3 */ 296 | } 297 | 298 | /** 299 | * Re-set default cursor for disabled elements. 300 | */ 301 | 302 | button[disabled], 303 | html input[disabled] { 304 | cursor: default; 305 | } 306 | 307 | /** 308 | * Remove inner padding and border in Firefox 4+. 309 | */ 310 | 311 | button::-moz-focus-inner, 312 | input::-moz-focus-inner { 313 | border: 0; 314 | padding: 0; 315 | } 316 | 317 | /** 318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 319 | * the UA stylesheet. 320 | */ 321 | 322 | input { 323 | line-height: normal; 324 | } 325 | 326 | /** 327 | * It's recommended that you don't atblock-bordert to style these elements. 328 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 329 | * 330 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 331 | * 2. Remove excess padding in IE 8/9/10. 332 | */ 333 | 334 | input[type="checkbox"], 335 | input[type="radio"] { 336 | box-sizing: border-box; /* 1 */ 337 | padding: 0; /* 2 */ 338 | } 339 | 340 | /** 341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 342 | * `font-size` values of the `input`, it causes the cursor style of the 343 | * decrement button to change from `default` to `text`. 344 | */ 345 | 346 | input[type="number"]::-webkit-inner-spin-button, 347 | input[type="number"]::-webkit-outer-spin-button { 348 | height: auto; 349 | } 350 | 351 | /** 352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 354 | * (include `-moz` to future-proof). 355 | */ 356 | 357 | input[type="search"] { 358 | -webkit-appearance: textfield; /* 1 */ 359 | -moz-box-sizing: content-box; 360 | -webkit-box-sizing: content-box; /* 2 */ 361 | box-sizing: content-box; 362 | } 363 | 364 | /** 365 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 366 | * Safari (but not Chrome) clips the cancel button when the search input has 367 | * padding (and `textfield` appearance). 368 | */ 369 | 370 | input[type="search"]::-webkit-search-cancel-button, 371 | input[type="search"]::-webkit-search-decoration { 372 | -webkit-appearance: none; 373 | } 374 | 375 | /** 376 | * Define consistent border, margin, and padding. 377 | */ 378 | 379 | fieldset { 380 | border: 1px solid #c0c0c0; 381 | margin: 0 2px; 382 | padding: 0.35em 0.625em 0.75em; 383 | } 384 | 385 | /** 386 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 387 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 388 | */ 389 | 390 | legend { 391 | border: 0; /* 1 */ 392 | padding: 0; /* 2 */ 393 | } 394 | 395 | /** 396 | * Remove default vertical scrollbar in IE 8/9/10/11. 397 | */ 398 | 399 | textarea { 400 | overflow: auto; 401 | } 402 | 403 | /** 404 | * Don't inherit the `font-weight` (applied by a rule above). 405 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 406 | */ 407 | 408 | optgroup { 409 | font-weight: bold; 410 | } 411 | 412 | /* Tables 413 | ========================================================================== */ 414 | 415 | /** 416 | * Remove most spacing between table cells. 417 | */ 418 | 419 | table { 420 | border-collapse: collapse; 421 | border-spacing: 0; 422 | } 423 | 424 | td, 425 | th { 426 | padding: 0; 427 | } 428 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Кекстаграм 11 | 12 | 13 | 14 |
15 | 16 | 17 |
18 |

Фильтр фотографий

19 |
20 | 21 | 22 | 23 |
24 |
25 | 26 | 27 |
28 |

Фотографии других пользователей

29 | 30 | 31 |
32 |
33 |

Загрузка фотографии

34 |
35 | 36 |
37 | 38 | 39 |
40 | 41 | 42 | 134 |
135 |
136 |
137 | 138 | 139 | 140 |
141 | 142 | 143 | 188 |
189 | 190 | 202 | 203 | 204 | 213 | 214 | 215 | 223 | 224 | 225 | 233 | 234 | 235 | 238 | 239 | 240 | 241 | 242 | 243 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | /* Общие стили. Каркас 2 | ========================================================================== */ 3 | 4 | @font-face { 5 | font-family: "Open Sans"; 6 | src: url("../fonts/OpenSansCondensed-Bold.woff2") format("woff2"), 7 | url("../fonts/OpenSansCondensed-Bold.woff") format("woff"); 8 | font-weight: 700; 9 | font-style: normal; 10 | font-display: swap; 11 | } 12 | 13 | @font-face { 14 | font-family: "Open Sans"; 15 | src: url("../fonts/OpenSans-Regular.woff2") format("woff2"), 16 | url("../fonts/OpenSans-Regular.woff") format("woff"); 17 | font-weight: 400; 18 | font-style: normal; 19 | font-display: swap; 20 | } 21 | 22 | *, 23 | *::before, 24 | *::after { 25 | -webkit-box-sizing: border-box; 26 | box-sizing: border-box; 27 | } 28 | 29 | body { 30 | font-family: "Open Sans", "Arial", sans-serif; 31 | font-weight: 400; 32 | font-size: 14px; 33 | line-height: 20px; 34 | color: #ffffff; 35 | text-transform: uppercase; 36 | 37 | background-color: #232321; 38 | } 39 | 40 | main { 41 | position: relative; 42 | } 43 | 44 | .content-box-component { 45 | -webkit-box-sizing: content-box; 46 | box-sizing: content-box; 47 | } 48 | 49 | .container { 50 | width: 1360px; 51 | margin: 0 auto; 52 | } 53 | 54 | .modal-open { 55 | height: 100vh; 56 | overflow: hidden; 57 | } 58 | 59 | 60 | /** 61 | * Кнопка закрытия модального окна 62 | */ 63 | 64 | .cancel { 65 | display: block; 66 | margin: 0; 67 | padding: 0; 68 | 69 | width: 42px; 70 | height: 42px; 71 | 72 | font-size: 0; 73 | 74 | background-color: rgba(255, 255, 255, 0.2); 75 | background-image: url("../img/icon-cross.svg"); 76 | background-position: center; 77 | background-repeat: no-repeat; 78 | 79 | border: 0; 80 | border-radius: 2px; 81 | 82 | opacity: 0.6; 83 | } 84 | 85 | .cancel:hover, 86 | .cancel:focus { 87 | background-color: rgba(255, 255, 255, 0.4); 88 | opacity: 1; 89 | } 90 | 91 | .cancel:active { 92 | -webkit-transform: translateY(1px); 93 | -ms-transform: translateY(1px); 94 | transform: translateY(1px); 95 | } 96 | 97 | 98 | /** 99 | * Оверлей под экранами 100 | */ 101 | 102 | .overlay { 103 | position: fixed; 104 | top: 0; 105 | left: 0; 106 | z-index: 2; 107 | 108 | -webkit-box-sizing: border-box; 109 | box-sizing: border-box; 110 | 111 | width: 100%; 112 | height: 100%; 113 | padding-top: 60px; 114 | overflow: auto; 115 | 116 | background-color: rgba(0, 0, 0, 0.8); 117 | } 118 | 119 | 120 | /* Блок с фильтрами изображений других пользователей 121 | ========================================================================== */ 122 | 123 | .img-filters { 124 | padding: 20px 0; 125 | 126 | display: -webkit-box; 127 | display: -ms-flexbox; 128 | display: flex; 129 | 130 | -webkit-box-pack: center; 131 | -ms-flex-pack: center; 132 | justify-content: center; 133 | } 134 | 135 | .img-filters--inactive { 136 | opacity: 0; 137 | } 138 | 139 | .img-filters__button { 140 | padding: 5px 15px; 141 | 142 | font-family: "Open Sans", "Arial", sans-serif; 143 | font-weight: 700; 144 | font-size: 18px; 145 | line-height: auto; 146 | text-transform: uppercase; 147 | 148 | cursor: pointer; 149 | background-color: #232321; 150 | border: none; 151 | } 152 | 153 | .img-filters__button:active, 154 | .img-filters__button--active { 155 | color: #ff4e4e; 156 | 157 | background-color: #ffffff; 158 | border-radius: 2px; 159 | } 160 | 161 | .img-filters__label { 162 | padding: 5px 15px; 163 | 164 | font-family: "Open Sans", "Arial", sans-serif; 165 | font-weight: 700; 166 | font-size: 18px; 167 | line-height: auto; 168 | 169 | cursor: pointer; 170 | } 171 | 172 | .img-filters__radio:focus + .img-filters__label { 173 | outline: auto 5px highlight; 174 | } 175 | 176 | .img-filters__radio:checked + .img-filters__label { 177 | color: #ff4e4e; 178 | text-decoration: none; 179 | 180 | background-color: #ffffff; 181 | border-radius: 2px; 182 | } 183 | 184 | 185 | /* Блок, в котором размещаются фотографии других пользователей 186 | ========================================================================== */ 187 | 188 | .pictures { 189 | display: -ms-grid; 190 | display: grid; 191 | grid-gap: 10px; 192 | 193 | -ms-grid-columns: (1fr)[7]; 194 | grid-template-columns: repeat(7, 1fr); 195 | } 196 | 197 | .picture { 198 | position: relative; 199 | line-height: 0; 200 | } 201 | 202 | .picture__img { 203 | max-width: auto; 204 | width: 100%; 205 | height: auto; 206 | } 207 | 208 | .picture__info { 209 | display: none; 210 | 211 | margin: 0; 212 | padding: 15px; 213 | position: absolute; 214 | bottom: 10px; 215 | left: 50%; 216 | 217 | background-color: rgba(0, 0, 0, 0.5); 218 | border-radius: 5px; 219 | 220 | -webkit-transform: translateX(-50%); 221 | -ms-transform: translateX(-50%); 222 | transform: translateX(-50%); 223 | } 224 | 225 | .picture:hover .picture__info, 226 | .picture:focus .picture__info { 227 | display: -webkit-box; 228 | display: -ms-flexbox; 229 | display: flex; 230 | } 231 | 232 | .picture__comments, 233 | .picture__likes { 234 | padding-left: 30px; 235 | position: relative; 236 | 237 | font-family: "Open Sans", "Arial", sans-serif; 238 | font-weight: 400; 239 | color: #ffffff; 240 | } 241 | 242 | .picture__comments { 243 | margin-right: 10px; 244 | } 245 | 246 | .picture__comments::before, 247 | .picture__likes::before { 248 | content: ""; 249 | position: absolute; 250 | left: 5px; 251 | top: -7px; 252 | 253 | background-image: url("../img/sprite.png"); 254 | background-repeat: no-repeat; 255 | } 256 | 257 | .picture__comments::before { 258 | width: 20px; 259 | height: 16px; 260 | 261 | background-position: -5px -5px; 262 | } 263 | 264 | .picture__likes::before { 265 | width: 20px; 266 | height: 16px; 267 | 268 | background-position: -5px -31px; 269 | } 270 | 271 | 272 | /* Поле для загрузки нового изображения 273 | ========================================================================== */ 274 | 275 | .img-upload { 276 | -ms-grid-column: 3; 277 | -ms-grid-column-span: 3; 278 | grid-column: 3 / span 3; 279 | 280 | -ms-grid-row: 1; 281 | -ms-grid-row-span: 3; 282 | grid-row: 1 / span 3; 283 | } 284 | 285 | .img-upload__wrapper { 286 | margin: 0 auto; 287 | padding-bottom: 60px; 288 | width: 582px; 289 | 290 | text-align: center; 291 | } 292 | 293 | .img-upload__form { 294 | position: relative; 295 | min-height: 569px; 296 | 297 | background-color: #eed21e; 298 | background-image: url("../img/logo-background-1.jpg"); 299 | background-repeat: no-repeat; 300 | background-position: center; 301 | 302 | border: none; 303 | } 304 | 305 | .img-upload__start { 306 | position: relative; 307 | 308 | margin: 0; 309 | padding: 0; 310 | min-height: 569px; 311 | 312 | background-image: url("../img/logo-mask.png"); 313 | background-repeat: no-repeat; 314 | background-position: center; 315 | 316 | border: none; 317 | } 318 | 319 | .img-upload__label { 320 | position: absolute; 321 | 322 | top: 194px; 323 | left: 203px; 324 | 325 | width: 180px; 326 | height: 181px; 327 | 328 | font-size: 0; 329 | 330 | background-image: url("../img/upload-button-bg.png"); 331 | background-repeat: no-repeat; 332 | background-position: center; 333 | 334 | cursor: pointer; 335 | } 336 | 337 | .img-upload__label::after { 338 | content: ""; 339 | position: absolute; 340 | 341 | top: 32px; 342 | left: 47px; 343 | 344 | width: 86px; 345 | height: 114px; 346 | 347 | background-image: url("../img/upload-button.svg"); 348 | background-repeat: no-repeat; 349 | background-position: center; 350 | 351 | -webkit-filter: drop-shadow(0 0 10px #943106); 352 | filter: drop-shadow(0 0 10px #943106); 353 | } 354 | 355 | .img-upload__label:hover::after, 356 | .img-upload__label:active::after { 357 | -webkit-filter: drop-shadow(0 0 10px #622104); 358 | filter: drop-shadow(0 0 10px #622104); 359 | 360 | -webkit-transform: scale(1.1); 361 | -ms-transform: scale(1.1); 362 | transform: scale(1.1); 363 | } 364 | 365 | .img-upload__input:focus + .img-upload__label { 366 | outline: auto 5px highlight; 367 | } 368 | 369 | 370 | /* Окно для редактирования изображения 371 | ========================================================================== */ 372 | 373 | .img-upload__overlay { 374 | position: fixed; 375 | top: 0; 376 | left: 0; 377 | z-index: 2; 378 | 379 | -webkit-box-sizing: border-box; 380 | box-sizing: border-box; 381 | 382 | width: 100%; 383 | height: 100%; 384 | 385 | padding-top: 60px; 386 | overflow: auto; 387 | 388 | background-color: rgba(0, 0, 0, 0.8); 389 | } 390 | 391 | .img-upload__preview-container { 392 | position: relative; 393 | margin-bottom: 30px; 394 | } 395 | 396 | 397 | /** 398 | * Кнопки изменения масштаба изобраения 399 | */ 400 | 401 | .img-upload__scale { 402 | position: absolute; 403 | top: 0; 404 | left: 50%; 405 | z-index: 1; 406 | 407 | font-family: "Open Sans", "Arial", sans-serif; 408 | font-weight: 400; 409 | 410 | -webkit-transform: translateX(-50%); 411 | -ms-transform: translateX(-50%); 412 | transform: translateX(-50%); 413 | } 414 | 415 | .scale { 416 | margin: 0; 417 | padding: 10px; 418 | 419 | font-size: 0; 420 | border: 0; 421 | } 422 | 423 | .scale__control { 424 | position: relative; 425 | 426 | width: 33px; 427 | height: 33px; 428 | margin: 0; 429 | padding: 0; 430 | 431 | text-align: center; 432 | vertical-align: middle; 433 | 434 | background-color: rgba(0, 0, 0, 0.6); 435 | border: 0; 436 | } 437 | 438 | .scale__control:hover, 439 | .scale__control:focus { 440 | background-color: rgba(0, 0, 0, 0.3); 441 | } 442 | 443 | .scale__control:active { 444 | -webkit-transform: translateY(1px); 445 | -ms-transform: translateY(1px); 446 | transform: translateY(1px); 447 | } 448 | 449 | .scale__control--smaller { 450 | border-radius: 50% 0 0 50%; 451 | } 452 | 453 | .scale__control--smaller::before { 454 | content: "–"; 455 | position: absolute; 456 | top: 50%; 457 | left: 50%; 458 | 459 | font-size: 20px; 460 | line-height: 0; 461 | color: #ffffff; 462 | 463 | -webkit-transform: translate(-50%, -50%); 464 | -ms-transform: translate(-50%, -50%); 465 | transform: translate(-50%, -50%); 466 | } 467 | 468 | .scale__control--value { 469 | width: 60px; 470 | 471 | font-size: 16px; 472 | color: #ffffff; 473 | 474 | border: solid rgba(255, 255, 255, 0.5); 475 | border-width: 0 2px; 476 | } 477 | 478 | .scale__control--bigger { 479 | border-radius: 0 50% 50% 0; 480 | } 481 | 482 | .scale__control--bigger::before { 483 | content: "+"; 484 | position: absolute; 485 | top: 50%; 486 | left: 50%; 487 | 488 | font-size: 20px; 489 | line-height: 0; 490 | color: #ffffff; 491 | 492 | -webkit-transform: translate(-50%, -50%); 493 | -ms-transform: translate(-50%, -50%); 494 | transform: translate(-50%, -50%); 495 | } 496 | 497 | 498 | /** 499 | * Превью изображения 500 | */ 501 | 502 | .img-upload__preview { 503 | display: -webkit-box; 504 | display: -ms-flexbox; 505 | display: flex; 506 | -webkit-box-align: center; 507 | -ms-flex-align: center; 508 | align-items: center; 509 | -webkit-box-pack: center; 510 | -ms-flex-pack: center; 511 | justify-content: center; 512 | 513 | width: 600px; 514 | height: 600px; 515 | 516 | background-color: #ffffff; 517 | } 518 | 519 | .img-upload__preview img { 520 | display: block; 521 | max-width: 600px; 522 | max-height: 600px; 523 | } 524 | 525 | 526 | /** 527 | * Шкала регулирования глубины фильтра 528 | */ 529 | 530 | .effect-level { 531 | position: absolute; 532 | bottom: 9px; 533 | left: 50%; 534 | 535 | padding: 7px 11px 0; 536 | 537 | width: 495px; 538 | height: 34px; 539 | 540 | font-size: 12px; 541 | text-align: center; 542 | color: #ffffff; 543 | white-space: nowrap; 544 | 545 | background-color: rgba(0, 0, 0, 0.6); 546 | border-radius: 34px; 547 | 548 | -webkit-transform: translateX(-50%); 549 | -ms-transform: translateX(-50%); 550 | transform: translateX(-50%); 551 | } 552 | 553 | .effect-level .noUi-target { 554 | border-radius: 16px; 555 | } 556 | 557 | .effect-level .noUi-connects { 558 | border-radius: 13px; 559 | } 560 | 561 | .effect-level .noUi-connect { 562 | background: #ffe753; 563 | } 564 | 565 | .effect-level__value { 566 | display: none; 567 | } 568 | 569 | 570 | /** 571 | * Блок с эффектами, которые можно применить к изображению 572 | */ 573 | 574 | .effects { 575 | margin: 0; 576 | padding: 0; 577 | margin-bottom: 30px; 578 | border: none; 579 | } 580 | 581 | .effects__list { 582 | margin: 0; 583 | padding: 0; 584 | 585 | display: -webkit-box; 586 | display: -ms-flexbox; 587 | display: flex; 588 | 589 | -webkit-box-pack: center; 590 | -ms-flex-pack: center; 591 | justify-content: center; 592 | 593 | list-style: none; 594 | text-align: center; 595 | } 596 | 597 | .effects__item { 598 | margin-right: 5px; 599 | } 600 | 601 | .effects__label { 602 | font-family: "Open Sans", "Arial", sans-serif; 603 | font-weight: 700; 604 | 605 | cursor: pointer; 606 | } 607 | 608 | .effects__label:hover, 609 | .effects__label:focus, 610 | .effects__radio:checked + .effects__label { 611 | color: #ffe753; 612 | } 613 | 614 | .effects__preview { 615 | display: block; 616 | width: 70px; 617 | height: 70px; 618 | margin-bottom: 5px; 619 | 620 | overflow: hidden; 621 | 622 | font-size: 0; 623 | 624 | background-color: #314359; 625 | background-image: url("../img/upload-default-image.jpg"); 626 | background-repeat: no-repeat; 627 | background-position: center; 628 | background-size: 100% auto; 629 | 630 | border: solid 5px transparent; 631 | border-radius: 5px; 632 | } 633 | 634 | .effects__preview:last-of-type { 635 | margin-right: 0; 636 | } 637 | 638 | .effects__radio:checked + .effects__label .effects__preview { 639 | border: 5px solid #ffe753; 640 | } 641 | 642 | .effects__radio:focus + .effects__label .effects__preview { 643 | outline: auto 5px highlight; 644 | } 645 | 646 | .effects__preview--chrome { 647 | -webkit-filter: grayscale(1); 648 | filter: grayscale(1); 649 | } 650 | 651 | .effects__preview--sepia { 652 | -webkit-filter: sepia(1); 653 | filter: sepia(1); 654 | } 655 | 656 | .effects__preview--marvin { 657 | -webkit-filter: invert(100%); 658 | filter: invert(100%); 659 | } 660 | 661 | .effects__preview--phobos { 662 | -webkit-filter: blur(3px); 663 | filter: blur(3px); 664 | } 665 | 666 | .effects__preview--heat { 667 | -webkit-filter: brightness(3); 668 | filter: brightness(3); 669 | } 670 | 671 | 672 | /** 673 | * Блок добавления хэштегов и комментария к изображению 674 | */ 675 | 676 | .text { 677 | margin: 0; 678 | padding: 0; 679 | margin-bottom: 30px; 680 | 681 | text-align: center; 682 | border: none; 683 | } 684 | 685 | .text__hashtags { 686 | width: 450px; 687 | 688 | margin: 0 auto; 689 | margin-bottom: 20px; 690 | padding: 5px 10px; 691 | 692 | font-family: "Open Sans", "Arial", sans-serif; 693 | font-weight: 400; 694 | color: #000000; 695 | 696 | border-radius: 5px; 697 | } 698 | 699 | .text__description { 700 | width: 450px; 701 | height: 100px; 702 | 703 | margin: 0 auto; 704 | padding: 15px 10px; 705 | 706 | font-family: "Open Sans", "Arial", sans-serif; 707 | font-weight: 400; 708 | color: #000000; 709 | 710 | border-radius: 5px; 711 | } 712 | 713 | 714 | /** 715 | * Кнопка для отправки изображения 716 | */ 717 | 718 | .img-upload__submit { 719 | padding: 10px 20px; 720 | 721 | color: #ffe753; 722 | text-align: center; 723 | text-transform: uppercase; 724 | font-family: "Open Sans", "Arial", sans-serif; 725 | font-weight: 700; 726 | 727 | background-color: rgba(255, 231, 82, 0.2); 728 | 729 | border: 0; 730 | border-radius: 4px; 731 | } 732 | 733 | .img-upload__submit:hover, 734 | .img-upload__submit:focus { 735 | background-color: rgba(255, 231, 82, 0.4); 736 | } 737 | 738 | .img-upload__submit:active { 739 | -webkit-transform: translateY(1px); 740 | -ms-transform: translateY(1px); 741 | transform: translateY(1px); 742 | } 743 | 744 | .img-upload__submit:disabled, 745 | .img-upload__submit--disabled { 746 | opacity: 0.3; 747 | } 748 | 749 | 750 | /* 751 | * Кнопка для закрытия окна редактирования изображения 752 | */ 753 | 754 | .img-upload__cancel { 755 | position: absolute; 756 | top: 0; 757 | left: 610px; 758 | } 759 | 760 | .img-upload__cancel:active { 761 | -webkit-transform: translateY(1px); 762 | -ms-transform: translateY(1px); 763 | transform: translateY(1px); 764 | } 765 | 766 | 767 | /* Окно полноэкранного просмотра изображения 768 | ========================================================================== */ 769 | 770 | .big-picture__preview { 771 | position: absolute; 772 | top: 60px; 773 | left: 50%; 774 | 775 | max-width: 600px; 776 | min-height: 600px; 777 | 778 | padding-bottom: 60px; 779 | 780 | -webkit-transform: translateX(-50%); 781 | -ms-transform: translateX(-50%); 782 | transform: translateX(-50%); 783 | } 784 | 785 | .big-picture__img img { 786 | display: block; 787 | } 788 | 789 | .social { 790 | display: -webkit-box; 791 | display: -ms-flexbox; 792 | display: flex; 793 | 794 | -webkit-box-orient: vertical; 795 | -webkit-box-direction: normal; 796 | -ms-flex-direction: column; 797 | flex-direction: column; 798 | 799 | max-width: 600px; 800 | 801 | color: #000000; 802 | font-family: "Open Sans", "Arial", sans-serif; 803 | font-weight: 400; 804 | text-transform: none; 805 | 806 | background-color: #ffffff; 807 | } 808 | 809 | 810 | /* 811 | * Подпись к изображению 812 | */ 813 | 814 | .social__header { 815 | padding: 15px 15px; 816 | 817 | display: -webkit-box; 818 | display: -ms-flexbox; 819 | display: flex; 820 | 821 | -webkit-box-pack: start; 822 | -ms-flex-pack: start; 823 | justify-content: flex-start; 824 | 825 | -webkit-box-align: center; 826 | -ms-flex-align: center; 827 | align-items: center; 828 | 829 | border-bottom: 1px solid #cccccc; 830 | } 831 | 832 | .social__picture { 833 | display: block; 834 | -ms-flex-negative: 0; 835 | flex-shrink: 0; 836 | margin-right: 15px; 837 | } 838 | 839 | .social__caption { 840 | margin: 0; 841 | margin-right: 15px; 842 | } 843 | 844 | .social__likes { 845 | margin: 0; 846 | margin-left: auto; 847 | min-width: 125px; 848 | } 849 | 850 | .likes-count { 851 | position: relative; 852 | padding-left: 30px; 853 | 854 | color: #f48181; 855 | 856 | cursor: pointer; 857 | } 858 | 859 | .likes-count:hover { 860 | color: #e90000; 861 | } 862 | 863 | .likes-count::before { 864 | content: ""; 865 | position: absolute; 866 | 867 | top: 3px; 868 | left: 5px; 869 | 870 | width: 20px; 871 | height: 18px; 872 | 873 | background-image: url("../img/sprite.png"); 874 | background-repeat: no-repeat; 875 | 876 | background-position: -5px -56px; 877 | opacity: 0.5; 878 | } 879 | 880 | .likes-count:hover::before, 881 | .likes-count:focus::before { 882 | opacity: 1; 883 | } 884 | 885 | .likes-count:active::before, 886 | .likes-count--active::before { 887 | opacity: 1; 888 | background-position: -5px -81px; 889 | } 890 | 891 | 892 | /* 893 | * Комментарии к изображению 894 | */ 895 | 896 | .social__comments { 897 | -ms-flex-preferred-size: 100%; 898 | flex-basis: 100%; 899 | 900 | list-style: none; 901 | 902 | margin: 0; 903 | padding: 0; 904 | padding-top: 20px; 905 | } 906 | 907 | .social__comment { 908 | padding-left: 15px; 909 | padding-right: 15px; 910 | padding-bottom: 20px; 911 | 912 | display: -webkit-box; 913 | display: -ms-flexbox; 914 | display: flex; 915 | 916 | -webkit-box-align: center; 917 | -ms-flex-align: center; 918 | align-items: center; 919 | } 920 | 921 | .social__comment-count { 922 | padding-top: 20px; 923 | padding-left: 65px; 924 | } 925 | 926 | .social__comments-loader { 927 | padding: 0; 928 | padding-left: 65px; 929 | padding-bottom: 20px; 930 | position: relative; 931 | 932 | text-align: left; 933 | color: #3b77c0; 934 | 935 | background: none; 936 | border: none; 937 | } 938 | 939 | .comments-loader { 940 | text-align: left; 941 | color: #3b77c0; 942 | 943 | background: none; 944 | border: none; 945 | } 946 | 947 | .social__text { 948 | margin: 0; 949 | } 950 | 951 | 952 | /** 953 | * Спиннер загрузки комментариев и его анимация 954 | */ 955 | 956 | .spinner::after, 957 | .comments-loader:active::after { 958 | content: ""; 959 | position: absolute; 960 | left: 200px; 961 | top: 7px; 962 | 963 | color: #3b77c0; 964 | font-size: 6px; 965 | 966 | width: 1em; 967 | height: 1em; 968 | border-radius: 50%; 969 | 970 | animation: spinner 1.3s infinite linear; 971 | transform: translateZ(0); 972 | } 973 | 974 | @keyframes spinner { 975 | 0%, 976 | 100% { 977 | box-shadow: 0em -2.6em 0em 0em #3b77c0, 1.8em -1.8em 0 0em rgba(51,102,204, 0.2), 2.5em 0em 0 0em rgba(51,102,204, 0.2), 1.75em 1.75em 0 0em rgba(51,102,204, 0.2), 0em 2.5em 0 0em rgba(51,102,204, 0.2), -1.8em 1.8em 0 0em rgba(51,102,204, 0.2), -2.6em 0em 0 0em rgba(51,102,204, 0.5), -1.8em -1.8em 0 0em rgba(51,102,204, 0.7); 978 | } 979 | 12.5% { 980 | box-shadow: 0em -2.6em 0em 0em rgba(51,102,204, 0.7), 1.8em -1.8em 0 0em #3b77c0, 2.5em 0em 0 0em rgba(51,102,204, 0.2), 1.75em 1.75em 0 0em rgba(51,102,204, 0.2), 0em 2.5em 0 0em rgba(51,102,204, 0.2), -1.8em 1.8em 0 0em rgba(51,102,204, 0.2), -2.6em 0em 0 0em rgba(51,102,204, 0.2), -1.8em -1.8em 0 0em rgba(51,102,204, 0.5); 981 | } 982 | 25% { 983 | box-shadow: 0em -2.6em 0em 0em rgba(51,102,204, 0.5), 1.8em -1.8em 0 0em rgba(51,102,204, 0.7), 2.5em 0em 0 0em #3b77c0, 1.75em 1.75em 0 0em rgba(51,102,204, 0.2), 0em 2.5em 0 0em rgba(51,102,204, 0.2), -1.8em 1.8em 0 0em rgba(51,102,204, 0.2), -2.6em 0em 0 0em rgba(51,102,204, 0.2), -1.8em -1.8em 0 0em rgba(51,102,204, 0.2); 984 | } 985 | 37.5% { 986 | box-shadow: 0em -2.6em 0em 0em rgba(51,102,204, 0.2), 1.8em -1.8em 0 0em rgba(51,102,204, 0.5), 2.5em 0em 0 0em rgba(51,102,204, 0.7), 1.75em 1.75em 0 0em #3b77c0, 0em 2.5em 0 0em rgba(51,102,204, 0.2), -1.8em 1.8em 0 0em rgba(51,102,204, 0.2), -2.6em 0em 0 0em rgba(51,102,204, 0.2), -1.8em -1.8em 0 0em rgba(51,102,204, 0.2); 987 | } 988 | 50% { 989 | box-shadow: 0em -2.6em 0em 0em rgba(51,102,204, 0.2), 1.8em -1.8em 0 0em rgba(51,102,204, 0.2), 2.5em 0em 0 0em rgba(51,102,204, 0.5), 1.75em 1.75em 0 0em rgba(51,102,204, 0.7), 0em 2.5em 0 0em #3b77c0, -1.8em 1.8em 0 0em rgba(51,102,204, 0.2), -2.6em 0em 0 0em rgba(51,102,204, 0.2), -1.8em -1.8em 0 0em rgba(51,102,204, 0.2); 990 | } 991 | 62.5% { 992 | box-shadow: 0em -2.6em 0em 0em rgba(51,102,204, 0.2), 1.8em -1.8em 0 0em rgba(51,102,204, 0.2), 2.5em 0em 0 0em rgba(51,102,204, 0.2), 1.75em 1.75em 0 0em rgba(51,102,204, 0.5), 0em 2.5em 0 0em rgba(51,102,204, 0.7), -1.8em 1.8em 0 0em #3b77c0, -2.6em 0em 0 0em rgba(51,102,204, 0.2), -1.8em -1.8em 0 0em rgba(51,102,204, 0.2); 993 | } 994 | 75% { 995 | box-shadow: 0em -2.6em 0em 0em rgba(51,102,204, 0.2), 1.8em -1.8em 0 0em rgba(51,102,204, 0.2), 2.5em 0em 0 0em rgba(51,102,204, 0.2), 1.75em 1.75em 0 0em rgba(51,102,204, 0.2), 0em 2.5em 0 0em rgba(51,102,204, 0.5), -1.8em 1.8em 0 0em rgba(51,102,204, 0.7), -2.6em 0em 0 0em #3b77c0, -1.8em -1.8em 0 0em rgba(51,102,204, 0.2); 996 | } 997 | 87.5% { 998 | box-shadow: 0em -2.6em 0em 0em rgba(51,102,204, 0.2), 1.8em -1.8em 0 0em rgba(51,102,204, 0.2), 2.5em 0em 0 0em rgba(51,102,204, 0.2), 1.75em 1.75em 0 0em rgba(51,102,204, 0.2), 0em 2.5em 0 0em rgba(51,102,204, 0.2), -1.8em 1.8em 0 0em rgba(51,102,204, 0.5), -2.6em 0em 0 0em rgba(51,102,204, 0.7), -1.8em -1.8em 0 0em #3b77c0; 999 | } 1000 | } 1001 | 1002 | 1003 | /* 1004 | * Форма для отправки комментария 1005 | */ 1006 | 1007 | .social__footer { 1008 | border-top: 1px solid #cccccc; 1009 | } 1010 | 1011 | .big-picture__cancel { 1012 | position: absolute; 1013 | top: 0; 1014 | left: 100%; 1015 | 1016 | margin-left: 10px; 1017 | } 1018 | 1019 | .social__footer { 1020 | position: relative; 1021 | 1022 | display: -webkit-box; 1023 | display: -ms-flexbox; 1024 | display: flex; 1025 | 1026 | padding: 15px; 1027 | } 1028 | 1029 | .social__footer-commentor { 1030 | display: block; 1031 | 1032 | margin-right: 15px; 1033 | } 1034 | 1035 | .social__footer-text { 1036 | width: 100%; 1037 | 1038 | border: none; 1039 | } 1040 | 1041 | .social__footer-btn { 1042 | position: absolute; 1043 | right: 15px; 1044 | top: 50%; 1045 | 1046 | width: 34px; 1047 | height: 34px; 1048 | 1049 | font-size: 0; 1050 | 1051 | background-image: url("../img/sprite.png"); 1052 | background-repeat: no-repeat; 1053 | background-position: -5px -106px; 1054 | 1055 | opacity: 0.6; 1056 | 1057 | -webkit-transform: translateY(-50%); 1058 | -ms-transform: translateY(-50%); 1059 | transform: translateY(-50%); 1060 | 1061 | border: none; 1062 | } 1063 | 1064 | .social__footer-btn:hover, 1065 | .social__footer-btn:focus { 1066 | opacity: 1; 1067 | } 1068 | 1069 | .social__footer-btn:active, 1070 | .social__footer-btn--active { 1071 | opacity: 1; 1072 | background-position: -5px -149px; 1073 | } 1074 | 1075 | 1076 | /* Подвал сайта 1077 | ========================================================================== */ 1078 | 1079 | .page-footer { 1080 | margin-top: 30px; 1081 | padding-top: 30px; 1082 | border-top: solid 1px #cccccc; 1083 | } 1084 | 1085 | .page-footer__wrapper { 1086 | display: -webkit-box; 1087 | display: -ms-flexbox; 1088 | display: flex; 1089 | 1090 | -webkit-box-align: start; 1091 | -ms-flex-align: start; 1092 | align-items: flex-start; 1093 | 1094 | -webkit-box-pack: justify; 1095 | -ms-flex-pack: justify; 1096 | justify-content: space-between; 1097 | } 1098 | 1099 | 1100 | /* 1101 | * Копирайт Академии 1102 | */ 1103 | 1104 | .copyright { 1105 | font-size: 12px; 1106 | } 1107 | 1108 | .copyright__link--image:hover, 1109 | .copyright__link--image:focus { 1110 | opacity: 0.6; 1111 | } 1112 | 1113 | .copyright__link--text { 1114 | color: #ffffff; 1115 | } 1116 | 1117 | .copyright__link--text:hover, 1118 | .copyright__link--text:focus { 1119 | opacity: 0.6; 1120 | } 1121 | 1122 | .page-footer__contacts { 1123 | background-color: inherit; 1124 | } 1125 | 1126 | 1127 | /* 1128 | * Ссылки на социальные сети 1129 | */ 1130 | 1131 | .contacts { 1132 | margin: 0; 1133 | padding: 0; 1134 | list-style: none; 1135 | 1136 | display: -webkit-box; 1137 | display: -ms-flexbox; 1138 | display: flex; 1139 | } 1140 | 1141 | .contacts__link { 1142 | display: block; 1143 | 1144 | width: 29px; 1145 | height: 29px; 1146 | margin-left: 10px; 1147 | 1148 | font-size: 0; 1149 | 1150 | background-image: url("../img/sprite.png"); 1151 | background-repeat: no-repeat; 1152 | 1153 | opacity: 0.6; 1154 | cursor: pointer; 1155 | } 1156 | 1157 | .contacts__link:hover, 1158 | .contacts__link:focus { 1159 | opacity: 1; 1160 | } 1161 | 1162 | .contacts__link--twitter { 1163 | background-position: -5px -270px; 1164 | } 1165 | 1166 | .contacts__link--vk { 1167 | background-position: -5px -309px; 1168 | } 1169 | 1170 | 1171 | /* Экраны с сообщениями для пользователя 1172 | ========================================================================== */ 1173 | 1174 | .img-upload__message { 1175 | position: absolute; 1176 | z-index: 2; 1177 | left: 50%; 1178 | top: 0; 1179 | 1180 | padding-top: 250px; 1181 | 1182 | width: 600px; 1183 | height: 100%; 1184 | 1185 | font-family: "Open Sans", "Arial", sans-serif; 1186 | text-align: center; 1187 | font-weight: 700; 1188 | font-size: 20px; 1189 | color: #ffe753; 1190 | 1191 | background-color: #3c3614; 1192 | transform: translateX(-50%); 1193 | } 1194 | 1195 | 1196 | /** 1197 | * Экраны ошибки и успеха загрузки изображения 1198 | */ 1199 | 1200 | .error, 1201 | .success { 1202 | position: fixed; 1203 | display: flex; 1204 | justify-content: center; 1205 | align-items: flex-start; 1206 | 1207 | left: 0; 1208 | top: 0; 1209 | right: 0; 1210 | bottom: 0; 1211 | z-index: 1; 1212 | 1213 | background-color: rgba(0, 0, 0, 0.8); 1214 | } 1215 | 1216 | .error__inner, 1217 | .success__inner { 1218 | position: relative; 1219 | width: 600px; 1220 | padding: 50px 20px; 1221 | padding-top: 150px; 1222 | margin-top: 200px; 1223 | 1224 | display: flex; 1225 | flex-direction: column; 1226 | justify-content: center; 1227 | align-items: center; 1228 | 1229 | font-family: "Open Sans", "Arial", sans-serif; 1230 | text-align: center; 1231 | font-weight: 700; 1232 | font-size: 20px; 1233 | color: #ffe753; 1234 | 1235 | background-color: #3c3614; 1236 | border-radius: 10px; 1237 | } 1238 | 1239 | .success__inner { 1240 | padding: 100px 20px; 1241 | padding-bottom: 75px; 1242 | } 1243 | 1244 | .error__title { 1245 | position: relative; 1246 | 1247 | margin: 0; 1248 | margin-bottom: 50px; 1249 | 1250 | font-size: 32px; 1251 | } 1252 | 1253 | .success__title { 1254 | margin: 0; 1255 | margin-bottom: 50px; 1256 | 1257 | font-size: 32px; 1258 | } 1259 | 1260 | .error__title::before { 1261 | content: ""; 1262 | position: absolute; 1263 | 1264 | width: 59px; 1265 | height: 52px; 1266 | 1267 | top: -100px; 1268 | left: 50%; 1269 | 1270 | background-image: url("../img/icon-warning.svg"); 1271 | 1272 | -webkit-transform: translateX(-50%); 1273 | -ms-transform: translateX(-50%); 1274 | transform: translateX(-50%); 1275 | } 1276 | 1277 | .error__button, 1278 | .success__button { 1279 | padding: 5px 10px; 1280 | 1281 | color: #ffffff; 1282 | text-transform: uppercase; 1283 | 1284 | background: transparent; 1285 | border: 2px solid #ffffff; 1286 | border-radius: 5px; 1287 | } 1288 | 1289 | .error__button:hover, 1290 | .error__button:focus, 1291 | .success__button:hover, 1292 | .success__button:focus { 1293 | opacity: 0.8; 1294 | } 1295 | 1296 | .error__button:active, 1297 | .success__button:active { 1298 | opacity: 0.6; 1299 | } 1300 | 1301 | 1302 | /** 1303 | * Экран загрузки изображения 1304 | */ 1305 | 1306 | .img-upload__message--loading::before { 1307 | content: ""; 1308 | position: absolute; 1309 | bottom: 40%; 1310 | left: 50%; 1311 | 1312 | width: 300px; 1313 | height: 10px; 1314 | 1315 | background-color: #8a7d2d; 1316 | 1317 | -webkit-transform: translateX(-50%); 1318 | -ms-transform: translateX(-50%); 1319 | transform: translateX(-50%); 1320 | } 1321 | 1322 | .img-upload__message--loading::after { 1323 | content: ""; 1324 | position: absolute; 1325 | bottom: 40%; 1326 | left: 50%; 1327 | 1328 | width: 200px; 1329 | height: 10px; 1330 | 1331 | background-color: #ffe753; 1332 | 1333 | -webkit-transform: translateX(-50%); 1334 | -ms-transform: translateX(-50%); 1335 | transform: translateX(-50%); 1336 | } 1337 | 1338 | 1339 | /* Адаптивности чуток 1340 | ========================================================================== */ 1341 | 1342 | @media (min-width: 1024px) { 1343 | .img-upload__wrapper { 1344 | padding-bottom: 0; 1345 | } 1346 | } 1347 | 1348 | @media (min-width: 1024px) and (max-width: 1359px) { 1349 | .container { 1350 | width: 980px; 1351 | } 1352 | 1353 | .pictures { 1354 | -ms-grid-columns: (1fr)[5]; 1355 | grid-template-columns: repeat(5, 1fr); 1356 | } 1357 | 1358 | .img-upload { 1359 | -ms-grid-column: 2; 1360 | -ms-grid-column-span: 3; 1361 | grid-column: 2 / span 3; 1362 | 1363 | -ms-grid-row: 1; 1364 | -ms-grid-row-span: 3; 1365 | grid-row: 1 / span 3; 1366 | } 1367 | } 1368 | 1369 | @media (min-width: 0) and (max-width: 1023px) { 1370 | .container { 1371 | width: 600px; 1372 | } 1373 | 1374 | .pictures { 1375 | -ms-grid-columns: (1fr)[3]; 1376 | grid-template-columns: repeat(3, 1fr); 1377 | } 1378 | 1379 | .img-upload { 1380 | -ms-grid-column: 1; 1381 | -ms-grid-column-span: 3; 1382 | grid-column: 1 / span 3; 1383 | 1384 | -ms-grid-row: 1; 1385 | -ms-grid-row-span: 3; 1386 | grid-row: 1 / span 3; 1387 | } 1388 | } 1389 | 1390 | .visually-hidden { 1391 | position: absolute; 1392 | 1393 | width: 1px; 1394 | height: 1px; 1395 | margin: -1px; 1396 | border: 0; 1397 | padding: 0; 1398 | 1399 | white-space: nowrap; 1400 | 1401 | -webkit-clip-path: inset(100%); 1402 | clip-path: inset(100%); 1403 | 1404 | clip: rect(0 0 0 0); 1405 | overflow: hidden; 1406 | } 1407 | 1408 | .hidden { 1409 | display: none; 1410 | } 1411 | --------------------------------------------------------------------------------