Рад приветствовать тебя в «Очерках жизни» — месте, где истории обретают форму, а память становится книгой.
Этот конструктор создан для тех, кто хочет запечатлеть важное: семейные хроники, портреты героев, воспоминания о путешествиях или благодарность близким.
Ты можешь:
добавлять фотографии и управлять ими (поворачивать, отражать, масштабировать);
редактировать текст — от заголовков до длинных заметок;
сохранять проект и экспортировать страницы в PDF;
отмечать любимые шаблоны звёздочкой;
включить фоновую музыку для вдохновения.
Всё просто: выбери шаблон, добавь фото и текст, меняй порядок страниц, смотри предпросмотр и сохраняй результат.
С уважением, мастерская дизайна Evstafiadi
Выберите стиль книги
Классический
Скетч
Название книги
// ============================================================
// ========== ПАНЕЛЬ ТЕКСТА (БУКВА А) ==========
// ============================================================
(function() {
const panelText = document.getElementById('panelText');
const panelTextArrow = document.getElementById('panelTextArrow');
const fontSelect = document.getElementById('fontSelect');
const selectionInfo = document.getElementById('selectionInfo');
const fontSizeSlider = document.getElementById('fontSizeSlider');
const fontSizeValue = document.getElementById('fontSizeValue');
const letterSpacingSlider = document.getElementById('letterSpacingSlider');
const letterSpacingValue = document.getElementById('letterSpacingValue');
const boldBtn = document.getElementById('boldBtn');
const italicBtn = document.getElementById('italicBtn');
const normalBtn = document.getElementById('normalBtn');
const alignLeftBtn = document.getElementById('alignLeftBtn');
const alignCenterBtn = document.getElementById('alignCenterBtn');
const alignRightBtn = document.getElementById('alignRightBtn');
const alignJustifyBtn = document.getElementById('alignJustifyBtn');
const caseNormalBtn = document.getElementById('caseNormalBtn');
const caseUpperBtn = document.getElementById('caseUpperBtn');
const caseLowerBtn = document.getElementById('caseLowerBtn');
const textColorSwatch = document.getElementById('textColorSwatch');
const textColorPicker = document.getElementById('textColorPicker');
const textColorHex = document.getElementById('textColorHex');
const bgColorSwatch = document.getElementById('bgColorSwatch');
const bgColorPicker = document.getElementById('bgColorPicker');
const bgColorHex = document.getElementById('bgColorHex');
const resetTextStylesBtn = document.getElementById('resetTextStylesBtn');
const groups = {
size: document.getElementById('groupSize'),
letterSpacing: document.getElementById('groupLetterSpacing'),
font: document.getElementById('groupFont'),
style: document.getElementById('groupStyle'),
align: document.getElementById('groupAlign'),
case: document.getElementById('groupCase'),
textColor: document.getElementById('groupTextColor'),
bgColor: document.getElementById('groupBgColor')
};
let hideTimeoutText = null;
let savedRange = null;
function isValidHex(hex) { return /^#[0-9A-Fa-f]{6}$/.test(hex); }
function rgbToHex(r, g, b) { return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase(); }
function getCurrentEditableElement() {
const sel = window.getSelection();
if (!sel.isCollapsed && sel.rangeCount > 0) {
let node = sel.getRangeAt(0).startContainer;
if (node.nodeType === 3) node = node.parentElement;
while (node && node !== document.body && !node.hasAttribute && !node.getAttribute) {
node = node.parentElement;
}
if (node && node.closest && node.closest('.page-layout, .template-page, [contenteditable="true"]')) {
return node;
}
}
return null;
}
function saveSelection() {
const sel = window.getSelection();
if (!sel.isCollapsed && sel.rangeCount > 0) {
savedRange = sel.getRangeAt(0).cloneRange();
return true;
}
savedRange = null;
return false;
}
function restoreSelection() {
if (savedRange) {
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(savedRange);
}
}
function getStylesFromSelection() {
if (!savedRange) return null;
restoreSelection();
let node = savedRange.startContainer;
if (node.nodeType === 3) node = node.parentElement;
const f = window.getComputedStyle(node).fontFamily;
const font = f ? f.split(',')[0].replace(/['"]/g, '').trim() : '';
const w = window.getComputedStyle(node).fontWeight;
const bold = w === '700' || w === 'bold' || parseInt(w) >= 600;
const italic = window.getComputedStyle(node).fontStyle === 'italic';
const a = window.getComputedStyle(node).textAlign;
let align = 'left';
if (a === 'center') align = 'center';
else if (a === 'right') align = 'right';
else if (a === 'justify') align = 'justify';
const transform = window.getComputedStyle(node).textTransform;
const fontSize = parseInt(window.getComputedStyle(node).fontSize);
const ls = window.getComputedStyle(node).letterSpacing;
const letterSpacing = ls === 'normal' ? 0 : parseFloat(ls);
const color = window.getComputedStyle(node).color;
let textColor = '#000000';
const colorMatch = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
if (colorMatch) textColor = rgbToHex(parseInt(colorMatch[1]), parseInt(colorMatch[2]), parseInt(colorMatch[3]));
const bgColorRaw = window.getComputedStyle(node).backgroundColor;
let bgColor = 'transparent';
if (bgColorRaw !== 'rgba(0, 0, 0, 0)' && bgColorRaw !== 'transparent') {
const bgMatch = bgColorRaw.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
if (bgMatch) bgColor = rgbToHex(parseInt(bgMatch[1]), parseInt(bgMatch[2]), parseInt(bgMatch[3]));
}
return { font, bold, italic, align, transform, fontSize, letterSpacing, textColor, bgColor };
}
function updateUI() {
const styles = getStylesFromSelection();
if (!styles) {
selectionInfo.innerHTML = 'Шрифт: —';
fontSelect.value = '';
fontSizeSlider.value = 14; fontSizeValue.textContent = '14px';
letterSpacingSlider.value = 0; letterSpacingValue.textContent = '0px';
boldBtn.classList.remove('active'); italicBtn.classList.remove('active'); normalBtn.classList.remove('active');
alignLeftBtn.classList.remove('active'); alignCenterBtn.classList.remove('active'); alignRightBtn.classList.remove('active'); alignJustifyBtn.classList.remove('active');
caseNormalBtn.classList.remove('active'); caseUpperBtn.classList.remove('active'); caseLowerBtn.classList.remove('active');
textColorSwatch.style.background = '#000000'; textColorHex.value = '#000000';
bgColorSwatch.style.background = 'transparent'; bgColorHex.value = 'transparent';
return;
}
selectionInfo.innerHTML = `Шрифт: ${styles.font}`;
let fontValue = '';
if (styles.font === 'Georgia') fontValue = 'Georgia, serif';
else if (styles.font === 'Montserrat') fontValue = 'Montserrat, sans-serif';
else if (styles.font === 'Bebas Neue') fontValue = 'Bebas Neue, cursive';
else if (styles.font === 'Inter') fontValue = 'Inter, sans-serif';
fontSelect.value = fontValue;
fontSizeSlider.value = styles.fontSize; fontSizeValue.textContent = styles.fontSize + 'px';
letterSpacingSlider.value = styles.letterSpacing; letterSpacingValue.textContent = styles.letterSpacing + 'px';
boldBtn.classList.remove('active'); italicBtn.classList.remove('active'); normalBtn.classList.remove('active');
if (styles.bold) boldBtn.classList.add('active');
if (styles.italic) italicBtn.classList.add('active');
if (!styles.bold && !styles.italic) normalBtn.classList.add('active');
alignLeftBtn.classList.remove('active'); alignCenterBtn.classList.remove('active'); alignRightBtn.classList.remove('active'); alignJustifyBtn.classList.remove('active');
if (styles.align === 'left') alignLeftBtn.classList.add('active');
else if (styles.align === 'center') alignCenterBtn.classList.add('active');
else if (styles.align === 'right') alignRightBtn.classList.add('active');
else if (styles.align === 'justify') alignJustifyBtn.classList.add('active');
caseNormalBtn.classList.remove('active'); caseUpperBtn.classList.remove('active'); caseLowerBtn.classList.remove('active');
if (styles.transform === 'none') caseNormalBtn.classList.add('active');
else if (styles.transform === 'uppercase') caseUpperBtn.classList.add('active');
else if (styles.transform === 'lowercase') caseLowerBtn.classList.add('active');
textColorSwatch.style.background = styles.textColor; textColorHex.value = styles.textColor;
if (styles.bgColor === 'transparent') {
bgColorSwatch.style.background = 'rgba(255,255,255,0.2)';
bgColorHex.value = 'transparent';
} else {
bgColorSwatch.style.background = styles.bgColor;
bgColorHex.value = styles.bgColor;
}
}
function applyToSelection(styleName, value, preserveOtherStyles = true) {
if (!savedRange) return;
restoreSelection();
const sel = window.getSelection();
const range = sel.getRangeAt(0);
const text = range.toString();
if (!text) return;
range.deleteContents();
const span = document.createElement('span');
span.style[styleName] = value;
if (preserveOtherStyles) {
const styles = getStylesFromSelection();
if (styles) {
if (styleName !== 'fontFamily') span.style.fontFamily = fontSelect.value || styles.font + ', serif';
if (styleName !== 'fontSize') span.style.fontSize = styles.fontSize + 'px';
if (styleName !== 'letterSpacing') span.style.letterSpacing = styles.letterSpacing + 'px';
if (styleName !== 'fontWeight') span.style.fontWeight = styles.bold ? 'bold' : 'normal';
if (styleName !== 'fontStyle') span.style.fontStyle = styles.italic ? 'italic' : 'normal';
if (styleName !== 'textAlign') span.style.textAlign = styles.align;
if (styleName !== 'textTransform') span.style.textTransform = styles.transform;
if (styleName !== 'color') span.style.color = styles.textColor;
if (styleName !== 'backgroundColor') span.style.backgroundColor = styles.bgColor === 'transparent' ? 'transparent' : styles.bgColor;
}
}
span.textContent = text;
range.insertNode(span);
sel.removeAllRanges();
const newRange = document.createRange();
newRange.selectNodeContents(span);
sel.addRange(newRange);
savedRange = newRange.cloneRange();
updateUI();
}
function applyAlignmentToSelection(align) {
if (!savedRange) return;
restoreSelection();
const sel = window.getSelection();
const range = sel.getRangeAt(0);
const text = range.toString();
if (!text) return;
range.deleteContents();
const div = document.createElement('div');
div.style.textAlign = align;
div.style.display = 'block';
const styles = getStylesFromSelection();
if (styles) {
div.style.fontFamily = fontSelect.value || styles.font + ', serif';
div.style.fontSize = styles.fontSize + 'px';
div.style.letterSpacing = styles.letterSpacing + 'px';
div.style.fontWeight = styles.bold ? 'bold' : 'normal';
div.style.fontStyle = styles.italic ? 'italic' : 'normal';
div.style.textTransform = styles.transform;
div.style.color = styles.textColor;
div.style.backgroundColor = styles.bgColor === 'transparent' ? 'transparent' : styles.bgColor;
}
div.textContent = text;
range.insertNode(div);
sel.removeAllRanges();
const newRange = document.createRange();
newRange.selectNodeContents(div);
sel.addRange(newRange);
savedRange = newRange.cloneRange();
updateUI();
}
function applyTextColorToSelection(color) { applyToSelection('color', color); }
function applyBgColorToSelection(color) { applyToSelection('backgroundColor', color); }
function toggleBold() {
const styles = getStylesFromSelection();
if (styles && styles.bold) applyToSelection('fontWeight', 'normal');
else applyToSelection('fontWeight', 'bold');
}
function toggleItalic() {
const styles = getStylesFromSelection();
if (styles && styles.italic) applyToSelection('fontStyle', 'normal');
else applyToSelection('fontStyle', 'italic');
}
function applyRegular() { applyToSelection('fontWeight', 'normal'); applyToSelection('fontStyle', 'normal'); }
function applyCase(caseType) {
if (caseType === 'normal') applyToSelection('textTransform', 'none');
else if (caseType === 'upper') applyToSelection('textTransform', 'uppercase');
else if (caseType === 'lower') applyToSelection('textTransform', 'lowercase');
}
function showPanelText() { if (hideTimeoutText) clearTimeout(hideTimeoutText); panelText.classList.add('visible'); }
function hidePanelText() { panelText.classList.remove('visible'); }
function scheduleHideText() { if (hideTimeoutText) clearTimeout(hideTimeoutText); hideTimeoutText = setTimeout(() => hidePanelText(), 10000); }
panelTextArrow.addEventListener('click', (e) => {
e.stopPropagation();
if (window.hidePanelPhoto) window.hidePanelPhoto();
if (panelText.classList.contains('visible')) { hidePanelText(); if (hideTimeoutText) clearTimeout(hideTimeoutText); }
else { showPanelText(); scheduleHideText(); }
});
for (const [key, el] of Object.entries(groups)) {
if (el && el.querySelector('h3')) {
el.querySelector('h3').addEventListener('click', (e) => {
e.stopPropagation();
for (const [k, group] of Object.entries(groups)) {
if (group) {
if (k === key) group.classList.add('active-group');
else group.classList.remove('active-group');
}
}
});
}
}
document.addEventListener('selectionchange', () => {
const editable = getCurrentEditableElement();
if (editable) {
setTimeout(() => { saveSelection(); updateUI(); showPanelText(); scheduleHideText(); }, 10);
}
});
if (fontSelect) fontSelect.addEventListener('change', () => applyToSelection('fontFamily', fontSelect.value));
if (fontSizeSlider) fontSizeSlider.addEventListener('input', () => { fontSizeValue.textContent = fontSizeSlider.value + 'px'; applyToSelection('fontSize', fontSizeSlider.value + 'px'); scheduleHideText(); });
if (letterSpacingSlider) letterSpacingSlider.addEventListener('input', () => { letterSpacingValue.textContent = letterSpacingSlider.value + 'px'; applyToSelection('letterSpacing', letterSpacingSlider.value + 'px'); scheduleHideText(); });
if (boldBtn) boldBtn.addEventListener('click', toggleBold);
if (italicBtn) italicBtn.addEventListener('click', toggleItalic);
if (normalBtn) normalBtn.addEventListener('click', applyRegular);
if (alignLeftBtn) alignLeftBtn.addEventListener('click', () => applyAlignmentToSelection('left'));
if (alignCenterBtn) alignCenterBtn.addEventListener('click', () => applyAlignmentToSelection('center'));
if (alignRightBtn) alignRightBtn.addEventListener('click', () => applyAlignmentToSelection('right'));
if (alignJustifyBtn) alignJustifyBtn.addEventListener('click', () => applyAlignmentToSelection('justify'));
if (caseNormalBtn) caseNormalBtn.addEventListener('click', () => applyCase('normal'));
if (caseUpperBtn) caseUpperBtn.addEventListener('click', () => applyCase('upper'));
if (caseLowerBtn) caseLowerBtn.addEventListener('click', () => applyCase('lower'));
if (textColorSwatch) textColorSwatch.addEventListener('mousedown', (e) => { e.preventDefault(); saveSelection(); textColorPicker.click(); });
if (textColorPicker) textColorPicker.addEventListener('input', (e) => { textColorHex.value = e.target.value.toUpperCase(); applyTextColorToSelection(e.target.value); scheduleHideText(); });
if (textColorHex) textColorHex.addEventListener('input', (e) => { let val = e.target.value; if (!val.startsWith('#')) val = '#' + val; if (isValidHex(val)) { textColorHex.value = val.toUpperCase(); applyTextColorToSelection(val.toUpperCase()); } });
if (bgColorSwatch) bgColorSwatch.addEventListener('mousedown', (e) => { e.preventDefault(); saveSelection(); bgColorPicker.click(); });
if (bgColorPicker) bgColorPicker.addEventListener('input', (e) => { bgColorHex.value = e.target.value.toUpperCase(); applyBgColorToSelection(e.target.value); scheduleHideText(); });
if (bgColorHex) bgColorHex.addEventListener('input', (e) => {
let val = e.target.value;
if (val === 'transparent') applyBgColorToSelection('transparent');
else { if (!val.startsWith('#')) val = '#' + val; if (isValidHex(val)) { bgColorHex.value = val.toUpperCase(); applyBgColorToSelection(val.toUpperCase()); } }
});
if (resetTextStylesBtn) {
resetTextStylesBtn.addEventListener('click', () => {
if (savedRange) {
const text = savedRange.toString();
if (text) {
restoreSelection();
const sel = window.getSelection();
const range = sel.getRangeAt(0);
range.deleteContents();
const textNode = document.createTextNode(text);
range.insertNode(textNode);
sel.removeAllRanges();
const newRange = document.createRange();
newRange.selectNodeContents(textNode);
sel.addRange(newRange);
savedRange = newRange.cloneRange();
updateUI();
}
}
scheduleHideText();
});
}
hidePanelText();
})();
// ============================================================
// ========== ПАНЕЛЬ ФОТО (БУКВА И) ==========
// ============================================================
(function() {
const panelPhoto = document.getElementById('panelPhoto');
const panelPhotoArrow = document.getElementById('panelPhotoArrow');
let currentImage = null;
let currentTransform = { scale: 1, rotate: 0, scaleX: 1, scaleY: 1, translateX: 0, translateY: 0 };
let hideTimeoutPhoto = null;
const scaleSlider = document.getElementById('editScaleSlider');
const scaleValue = document.getElementById('editScaleValue');
const rotateLeftBtn = document.getElementById('editRotateLeft');
const rotateRightBtn = document.getElementById('editRotateRight');
const rotateAngle = document.getElementById('editRotateAngle');
const mirrorHorBtn = document.getElementById('editMirrorHor');
const mirrorVerBtn = document.getElementById('editMirrorVer');
const moveUpBtn = document.getElementById('editMoveUp');
const moveDownBtn = document.getElementById('editMoveDown');
const moveLeftBtn = document.getElementById('editMoveLeft');
const moveRightBtn = document.getElementById('editMoveRight');
const posXSpan = document.getElementById('editPosX');
const posYSpan = document.getElementById('editPosY');
const replaceBtn = document.getElementById('editReplacePhotoBtn');
const resetPhotoBtn = document.getElementById('editResetPhotoBtn');
function applyImageTransform() {
if (!currentImage) return;
const transform = `translate(${currentTransform.translateX}px, ${currentTransform.translateY}px) scale(${currentTransform.scale * currentTransform.scaleX}, ${currentTransform.scale * currentTransform.scaleY}) rotate(${currentTransform.rotate}deg)`;
currentImage.style.transform = transform;
if (scaleValue) scaleValue.textContent = Math.round(currentTransform.scale * 100) + '%';
if (rotateAngle) rotateAngle.value = currentTransform.rotate;
if (posXSpan) posXSpan.textContent = currentTransform.translateX;
if (posYSpan) posYSpan.textContent = currentTransform.translateY;
}
function updateActiveStates() {
if (mirrorHorBtn) mirrorHorBtn.classList.remove('active');
if (mirrorVerBtn) mirrorVerBtn.classList.remove('active');
if (currentTransform.scaleX === -1 && mirrorHorBtn) mirrorHorBtn.classList.add('active');
if (currentTransform.scaleY === -1 && mirrorVerBtn) mirrorVerBtn.classList.add('active');
}
function setActiveImage(img) {
currentImage = img;
currentTransform = { scale: 1, rotate: 0, scaleX: 1, scaleY: 1, translateX: 0, translateY: 0 };
if (scaleSlider) scaleSlider.value = 1;
applyImageTransform();
updateActiveStates();
showPanelPhoto();
resetHideTimerPhoto();
}
function showPanelPhoto() { if (hideTimeoutPhoto) clearTimeout(hideTimeoutPhoto); if (panelPhoto) panelPhoto.classList.add('visible'); }
function hidePanelPhoto() { if (panelPhoto) panelPhoto.classList.remove('visible'); }
function scheduleHidePhoto() { if (hideTimeoutPhoto) clearTimeout(hideTimeoutPhoto); hideTimeoutPhoto = setTimeout(() => hidePanelPhoto(), 10000); }
function resetHideTimerPhoto() { if (hideTimeoutPhoto) clearTimeout(hideTimeoutPhoto); scheduleHidePhoto(); }
window.hidePanelPhoto = hidePanelPhoto;
if (panelPhotoArrow) {
panelPhotoArrow.addEventListener('click', (e) => {
e.stopPropagation();
if (window.hidePanelText) window.hidePanelText();
if (panelPhoto.classList.contains('visible')) { hidePanelPhoto(); if (hideTimeoutPhoto) clearTimeout(hideTimeoutPhoto); }
else { showPanelPhoto(); scheduleHidePhoto(); }
});
}
// ГЛОБАЛЬНЫЙ ОБРАБОТЧИК ДЛЯ ВСЕХ ФОТО В КОНСТРУКТОРЕ
document.addEventListener('click', (e) => {
let target = e.target;
if (target.tagName === 'IMG' && target.closest('.page-layout, .template-page')) {
e.stopPropagation();
setActiveImage(target);
}
});
if (scaleSlider) scaleSlider.addEventListener('input', () => { currentTransform.scale = parseFloat(scaleSlider.value); applyImageTransform(); resetHideTimerPhoto(); });
if (rotateLeftBtn) rotateLeftBtn.addEventListener('click', () => { currentTransform.rotate -= 90; applyImageTransform(); resetHideTimerPhoto(); });
if (rotateRightBtn) rotateRightBtn.addEventListener('click', () => { currentTransform.rotate += 90; applyImageTransform(); resetHideTimerPhoto(); });
if (rotateAngle) rotateAngle.addEventListener('input', () => { currentTransform.rotate = parseInt(rotateAngle.value) || 0; applyImageTransform(); resetHideTimerPhoto(); });
if (mirrorHorBtn) mirrorHorBtn.addEventListener('click', () => { currentTransform.scaleX = currentTransform.scaleX === 1 ? -1 : 1; applyImageTransform(); updateActiveStates(); resetHideTimerPhoto(); });
if (mirrorVerBtn) mirrorVerBtn.addEventListener('click', () => { currentTransform.scaleY = currentTransform.scaleY === 1 ? -1 : 1; applyImageTransform(); updateActiveStates(); resetHideTimerPhoto(); });
if (moveUpBtn) moveUpBtn.addEventListener('click', () => { currentTransform.translateY -= 10; applyImageTransform(); resetHideTimerPhoto(); });
if (moveDownBtn) moveDownBtn.addEventListener('click', () => { currentTransform.translateY += 10; applyImageTransform(); resetHideTimerPhoto(); });
if (moveLeftBtn) moveLeftBtn.addEventListener('click', () => { currentTransform.translateX -= 10; applyImageTransform(); resetHideTimerPhoto(); });
if (moveRightBtn) moveRightBtn.addEventListener('click', () => { currentTransform.translateX += 10; applyImageTransform(); resetHideTimerPhoto(); });
if (replaceBtn) {
replaceBtn.addEventListener('click', () => {
if (!currentImage) return;
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*';
input.onchange = (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (ev) => {
currentImage.src = ev.target.result;
currentTransform = { scale: 1, rotate: 0, scaleX: 1, scaleY: 1, translateX: 0, translateY: 0 };
if (scaleSlider) scaleSlider.value = 1;
applyImageTransform();
updateActiveStates();
};
reader.readAsDataURL(file);
}
};
input.click();
resetHideTimerPhoto();
});
}
if (resetPhotoBtn) {
resetPhotoBtn.addEventListener('click', () => {
currentTransform = { scale: 1, rotate: 0, scaleX: 1, scaleY: 1, translateX: 0, translateY: 0 };
if (scaleSlider) scaleSlider.value = 1;
applyImageTransform();
updateActiveStates();
resetHideTimerPhoto();
});
}
hidePanelPhoto();
})();
// Добавляем функции закрытия панелей в глобальный доступ
window.hidePanelText = function() {
const panelText = document.getElementById('panelText');
if (panelText) panelText.classList.remove('visible');
};
window.hidePanelPhoto = function() {
const panelPhoto = document.getElementById('panelPhoto');
if (panelPhoto) panelPhoto.classList.remove('visible');
};
// Закрытие панелей при клике вне
document.addEventListener('click', (e) => {
const panelText = document.getElementById('panelText');
const panelTextArrow = document.getElementById('panelTextArrow');
const panelPhoto = document.getElementById('panelPhoto');
const panelPhotoArrow = document.getElementById('panelPhotoArrow');
if (panelText && !panelText.contains(e.target) && e.target !== panelTextArrow && !panelTextArrow.contains(e.target)) {
if (window.hidePanelText) window.hidePanelText();
}
if (panelPhoto && !panelPhoto.contains(e.target) && e.target !== panelPhotoArrow && !panelPhotoArrow.contains(e.target) && e.target.tagName !== 'IMG') {
if (window.hidePanelPhoto) window.hidePanelPhoto();
}
});