diff --git a/js/main.js b/js/main.js index b196cd6..3722731 100644 --- a/js/main.js +++ b/js/main.js @@ -6,6 +6,7 @@ import { initForm } from './ui/form/index.js'; import { showAlert } from './ui/messages.js'; import { initFilters } from './ui/form/filter.js'; import { debounce } from './utils/common.js'; +import { initUploadImage } from './ui/form/upload-image.js'; const dom = { container: document.querySelector('.pictures'), @@ -13,6 +14,7 @@ const dom = { filters: document.querySelector('.img-filters'), }; +initUploadImage(); initBigPicture(); initCommentsLoader(); initForm(); diff --git a/js/mock/comments.js b/js/mock/comments.js deleted file mode 100644 index 54ca397..0000000 --- a/js/mock/comments.js +++ /dev/null @@ -1,30 +0,0 @@ -import { MESSAGES, NAMES } from './const/data.js'; -import { CONFIG } from './const/config.js'; -import { getRandomInteger, getRandomItem } from '../utils/random.js'; -import { createIdGenerator, } from '../utils/id.js'; - -const commentIdGenerator = createIdGenerator(); - -const getRandomAvatar = () => - `img/avatar-${getRandomInteger(CONFIG.avatar.min, CONFIG.avatar.max)}.svg`; - -const createMessage = () => - Array.from( - { length: getRandomInteger(CONFIG.messages.min, CONFIG.messages.max) }, - () => getRandomItem(MESSAGES) - ).join(' '); - -const createComment = () => ({ - id: commentIdGenerator(), - avatar: getRandomAvatar(), - message: createMessage(), - name: getRandomItem(NAMES), -}); - -const getComments = () => - Array.from( - { length: getRandomInteger(CONFIG.comments.min, CONFIG.comments.max) }, - createComment - ); - -export { getComments }; diff --git a/js/mock/const/config.js b/js/mock/const/config.js deleted file mode 100644 index 81116cc..0000000 --- a/js/mock/const/config.js +++ /dev/null @@ -1,8 +0,0 @@ -const CONFIG = { - likes: { min: 15, max: 200 }, - comments: { min: 0, max: 30 }, - avatar: { min: 1, max: 6 }, - messages: { min: 1, max: 2 }, -}; - -export { CONFIG }; diff --git a/js/mock/const/data.js b/js/mock/const/data.js deleted file mode 100644 index 893910f..0000000 --- a/js/mock/const/data.js +++ /dev/null @@ -1,26 +0,0 @@ -const MESSAGES = [ - 'Всё отлично!', - 'В целом всё неплохо. Но не всё.', - 'Когда вы делаете фотографию, хорошо бы убирать палец из кадра. В конце-концов это просто непрофессионально.', - 'Моя бабушка случайно чихнула с фотоаппаратом в руках и у неё получилась фотография лучше.', - 'Я поскользнулся на банановой кожуре и уронил фотоаппарат на кота и у меня получилась фотография лучше.', - 'Лица у людей на фотке перекошены, как-будто их избивают. Как можно было поймать такой неудачный момент?!', -]; -const DESCRIPTIONS = [ - 'Летний чил на югах. #тай #отдых #лето #чил #travel #travelgram #summergram #chill', - 'Тестим новую камеру! #camera #test #new #newcameratest #pic #photo #instaphoto', - 'Затусили с друзьями на море #laptevsea #north #northeastpassage', - 'Как же круто тут кормят #food #foodgram #instafood #delicious #yummy', - 'Отдыхаем... #chill #relax #group #photo', - 'Цените каждое мгновенье. Цените тех, кто рядом с вами и отгоняйте все сомненья. Не обижайте всех словами......', - 'Вот это тачка! #wow #car #carwow #drive', - '#fun #party #cool #young', - 'Господи, это такая милота, я сейчас умру от нежности, у меня закшалил мимимиметр', - 'Хорошо, когда в жизни есть #друзья, которые вместе со мной могут зайти в #барнарубинштейна и бахнуть #пивка', - 'Норм', -]; -const NAMES = ['Николай', 'Аким', 'Ким', 'Харитон', 'Тимур', 'Степан']; -const PICTURES_COUNT = 25; -const COMMENTS_PER_PORTION = 5; - -export { MESSAGES, DESCRIPTIONS, NAMES, PICTURES_COUNT, COMMENTS_PER_PORTION }; diff --git a/js/mock/pictures.js b/js/mock/pictures.js deleted file mode 100644 index bb259d7..0000000 --- a/js/mock/pictures.js +++ /dev/null @@ -1,27 +0,0 @@ -import { DESCRIPTIONS, PICTURES_COUNT } from './const/data.js'; -import { CONFIG } from './const/config.js'; -import { getRandomInteger, getRandomItem } from '../utils/random.js'; -import { createIdGenerator } from '../utils/id.js'; -import { getComments } from './comments.js'; - -const pictureIdGenerator = createIdGenerator(); - -const createPicture = () => { - const id = pictureIdGenerator(); - - return { - id, - url: `photos/${id}.jpg`, - description: getRandomItem(DESCRIPTIONS), - likes: getRandomInteger(CONFIG.likes.min, CONFIG.likes.max), - comments: getComments(), - }; -}; - -const getPictures = () => - Array.from( - { length: PICTURES_COUNT }, - () => createPicture() - ); - -export { getPictures }; diff --git a/js/ui/big-picture/comments.js b/js/ui/big-picture/comments.js index 2bb6ff6..be69f0e 100644 --- a/js/ui/big-picture/comments.js +++ b/js/ui/big-picture/comments.js @@ -1,4 +1,4 @@ -import { COMMENTS_PER_PORTION } from '../../mock/const/data'; +const COMMENTS_PER_PORTION = 5; const commentsList = document.querySelector('.social__comments'); const commentsLoader = document.querySelector('.comments-loader'); diff --git a/js/ui/form/effect.js b/js/ui/form/effect.js index 95cca4b..0afab94 100644 --- a/js/ui/form/effect.js +++ b/js/ui/form/effect.js @@ -15,6 +15,8 @@ const EFFECTS_MAP = Object.fromEntries( let chosenEffect = DEFAULT_EFFECT; +let currentEffectClass = null; + const imageElement = document.querySelector('.img-upload__preview img'); const effectsElement = document.querySelector('.effects'); const sliderElement = document.querySelector('.effect-level__slider'); @@ -31,15 +33,23 @@ const hideSlider = () => { sliderContainerElement.classList.add('hidden'); }; -const removeEffectClasses = () => { - EFFECTS.forEach((effect) => { - imageElement.classList.remove(`effects__preview--${effect.name}`); - }); +const removeCurrentEffectClass = () => { + if (currentEffectClass) { + imageElement.classList.remove(currentEffectClass); + currentEffectClass = null; + } }; const applyEffectClass = () => { - removeEffectClasses(); - imageElement.classList.add(`effects__preview--${chosenEffect.name}`); + removeCurrentEffectClass(); + + if (isDefault()) { + return; + } + + const newClass = `effects__preview--${chosenEffect.name}`; + imageElement.classList.add(newClass); + currentEffectClass = newClass; }; const updateSlider = () => { @@ -83,7 +93,8 @@ const onSliderUpdate = () => { const resetEffects = () => { chosenEffect = DEFAULT_EFFECT; - removeEffectClasses(); + removeCurrentEffectClass(); + imageElement.style.filter = 'none'; effectLevelElement.value = ''; diff --git a/js/ui/form/index.js b/js/ui/form/index.js index 31633b1..8eea929 100644 --- a/js/ui/form/index.js +++ b/js/ui/form/index.js @@ -12,7 +12,6 @@ const initForm = () => { resetValidation(); }); - initScale(); initEffects(); initSubmit({ diff --git a/js/ui/form/modal.js b/js/ui/form/modal.js index 255abbd..3a6799c 100644 --- a/js/ui/form/modal.js +++ b/js/ui/form/modal.js @@ -44,8 +44,11 @@ const isTextFieldFocused = () => document.activeElement === hashtagField || document.activeElement === commentField; +const isMessageShown = () => + Boolean(document.querySelector('.error, .success')); + function onDocumentKeydown(evt) { - if (isEscapeKey(evt) && !isTextFieldFocused()) { + if (isEscapeKey(evt) && !isTextFieldFocused() && !isMessageShown()) { evt.preventDefault(); hideModal(); } diff --git a/js/ui/form/upload-image.js b/js/ui/form/upload-image.js new file mode 100644 index 0000000..0baeb51 --- /dev/null +++ b/js/ui/form/upload-image.js @@ -0,0 +1,42 @@ +const FILE_TYPES = ['jpg', 'jpeg', 'png']; + +const form = document.querySelector('.img-upload__form'); +const fileField = form.querySelector('.img-upload__input'); +const photoPreview = form.querySelector('.img-upload__preview img'); +const effectsPreviews = form.querySelectorAll('.effects__preview'); + +let currentUrl; + +const initUploadImage = () => { + fileField.addEventListener('change', () => { + const file = fileField.files[0]; + if (!file) { + return; + } + + const fileName = file.name.toLowerCase(); + + const isValid = + FILE_TYPES.some((ext) => fileName.endsWith(ext)) && + file.type.startsWith('image/'); + + if (!isValid) { + return; + } + + if (currentUrl) { + URL.revokeObjectURL(currentUrl); + currentUrl = null; + } + + currentUrl = URL.createObjectURL(file); + + photoPreview.src = currentUrl; + + effectsPreviews.forEach((preview) => { + preview.style.backgroundImage = `url(${currentUrl})`; + }); + }); +}; + +export { initUploadImage }; diff --git a/js/utils/random.js b/js/utils/random.js index 98d9b3c..2ee0e05 100644 --- a/js/utils/random.js +++ b/js/utils/random.js @@ -1,16 +1,4 @@ const getRandomInteger = (min, max) => { - if (min === undefined || max === undefined) { - throw new TypeError('Both min and max must be provided'); - } - - if (typeof min !== 'number' || typeof max !== 'number') { - throw new TypeError('Min and max must be numbers'); - } - - if (!Number.isFinite(min) || !Number.isFinite(max)) { - throw new TypeError('Min and max must be finite numbers'); - } - const lower = Math.ceil(Math.min(min, max)); const upper = Math.floor(Math.max(min, max)); @@ -21,12 +9,6 @@ const getRandomInteger = (min, max) => { return Math.floor(Math.random() * (upper - lower + 1)) + lower; }; -const getRandomItem = (items) => { - if (!Array.isArray(items) || items.length === 0) { - throw new TypeError('Items must be a non-empty array'); - } - - return items[getRandomInteger(0, items.length - 1)]; -}; +const getRandomItem = (items) => items[getRandomInteger(0, items.length - 1)]; export { getRandomInteger, getRandomItem };