1002 lines
46 KiB
JavaScript
1002 lines
46 KiB
JavaScript
/* ====================================================================
|
||
ОБЕРТКА ДЛЯ ОЖИДАНИЯ JQUERY
|
||
==================================================================== */
|
||
(function waitForJQuery() {
|
||
if (typeof jQuery === 'undefined') {
|
||
setTimeout(waitForJQuery, 10);
|
||
} else {
|
||
(function($) {
|
||
|
||
if (typeof aveabspath === 'undefined') {
|
||
window.aveabspath = '/';
|
||
}
|
||
|
||
function initCommentTimers() {
|
||
$('.timer-count:not(.timer-running)').each(function() {
|
||
var $timer = $(this);
|
||
var timeLeft = parseInt($timer.attr('data-left'));
|
||
|
||
// Если время не определено или уже вышло — помечаем как запущенный и пропускаем
|
||
if (isNaN(timeLeft) || timeLeft <= 0) {
|
||
$timer.addClass('timer-running');
|
||
return;
|
||
}
|
||
|
||
$timer.addClass('timer-running');
|
||
var cid = $timer.attr('id').replace('timer_', '');
|
||
|
||
var countdown = setInterval(function() {
|
||
timeLeft--;
|
||
|
||
if (timeLeft <= 0) {
|
||
clearInterval(countdown);
|
||
|
||
// --- Проверка режима редактирования ---
|
||
var isEditing = $('#comment_wrapper_' + cid).find('.edit-form-container').length > 0;
|
||
|
||
if (!isEditing) {
|
||
// Находим кнопки управления
|
||
var $controls = $('#controls_' + cid);
|
||
if ($controls.length) {
|
||
$controls.fadeOut(300, function() { $(this).remove(); });
|
||
}
|
||
|
||
// Обновляем статус визуально
|
||
$('#timer_container_' + cid).html('<span class="text-danger small"><i class="bi bi-clock-history"></i>' + COMMENT_JS_TIMER_OFF + '</span>');
|
||
} else {
|
||
// Если открыта форма редактирования, просто меняем текст, не удаляя кнопки
|
||
$('#timer_container_' + cid).html('<span class="text-warning small fw-bold"><i class="bi bi-exclamation-triangle"></i>' + COMMENT_JS_TIMER_OFF_A + '</span>');
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Форматируем вывод 00:00
|
||
var minutes = Math.floor(timeLeft / 60);
|
||
var seconds = timeLeft % 60;
|
||
$timer.text(minutes + ':' + (seconds < 10 ? '0' : '') + seconds);
|
||
$timer.attr('data-left', timeLeft);
|
||
|
||
}, 1000);
|
||
|
||
$timer.data('interval-id', countdown);
|
||
});
|
||
}
|
||
|
||
/* Limit Plugin */
|
||
$.fn.extend({
|
||
limit: function(limit, element) {
|
||
return this.each(function() {
|
||
var self = $(this);
|
||
function substring() {
|
||
var val = self.val();
|
||
if (val.length > limit) self.val(val.substring(0, limit));
|
||
if (typeof element !== 'undefined') {
|
||
var remaining = limit - self.val().length;
|
||
$(element).html(remaining < 0 ? '0' : remaining);
|
||
}
|
||
}
|
||
self.on('focus keyup paste', substring);
|
||
substring();
|
||
});
|
||
}
|
||
});
|
||
|
||
function getCaptha() {
|
||
var now = new Date();
|
||
$('#captcha img').attr('src', aveabspath + 'inc/captcha.php?cd=' + now.getTime());
|
||
}
|
||
|
||
// ВАЛИДАЦИЯ ПОЛЕЙ
|
||
|
||
// Функция для проверки файла (расширение и размер)
|
||
function checkFileConsistency(file, $errorDisplay) {
|
||
if (!file) return true;
|
||
|
||
// Получаем настройки из шаблона
|
||
var allowedExts = (typeof ALLOWED_EXTENSIONS !== 'undefined') ? ALLOWED_EXTENSIONS.toLowerCase().split(',') : ['jpg', 'jpeg', 'png', 'gif, webp'];
|
||
var maxSizeKb = (typeof MAX_FILE_SIZE_KB !== 'undefined') ? parseInt(MAX_FILE_SIZE_KB) : 2048;
|
||
|
||
var fileName = file.name.toLowerCase();
|
||
var fileExt = fileName.split('.').pop();
|
||
var fileSizeKb = file.size / 1024;
|
||
|
||
// Проверка расширения
|
||
if ($.inArray(fileExt, allowedExts) === -1) {
|
||
$errorDisplay.text(COMMENT_JS_FILE_TYPE + ' .' + fileExt + ' ' + COMMENT_JS_FILE_TYPE_A).removeClass('d-none');
|
||
return false;
|
||
}
|
||
|
||
// Проверка размера
|
||
if (fileSizeKb > maxSizeKb) {
|
||
$errorDisplay.text(COMMENT_JS_FILE_SIZE + ' (' + Math.round(fileSizeKb) + COMMENT_JS_FILE_SIZE_KB + '). ' + COMMENT_JS_FILE_SIZE_MAX + ' ' + maxSizeKb + COMMENT_JS_FILE_SIZE_KB).removeClass('d-none');
|
||
return false;
|
||
}
|
||
|
||
$errorDisplay.addClass('d-none').text('');
|
||
return true;
|
||
}
|
||
|
||
function validate(form) {
|
||
var isValid = true;
|
||
|
||
// Вспомогательная функция для подсветки
|
||
function setInvalid($el, show, msg) {
|
||
if (show) {
|
||
$el.addClass('is-invalid');
|
||
if ($el.next('.invalid-feedback').length === 0) {
|
||
$el.after('<div class="invalid-feedback">' + msg + '</div>');
|
||
}
|
||
isValid = false;
|
||
} else {
|
||
$el.removeClass('is-invalid');
|
||
$el.next('.invalid-feedback').remove();
|
||
}
|
||
}
|
||
|
||
// Проверка Имени
|
||
setInvalid($(form.comment_author_name), !form.comment_author_name.value.trim(), COMMENT_JS_CHECK_NAME);
|
||
|
||
// Проверка Email
|
||
setInvalid($(form.comment_author_email), !form.comment_author_email.value.trim(), COMMENT_JS_CHECK_EMAIL);
|
||
|
||
// Проверка Доп. поля №1 (если оно обязательное)
|
||
if (typeof REQ_F1 !== 'undefined' && REQ_F1 == '1') {
|
||
var f1 = $('#in_author_website');
|
||
setInvalid(f1, !f1.val().trim(), COMMENT_JS_FILL + " " + NAME_F1);
|
||
}
|
||
|
||
// Проверка Доп. поля №2 (если оно обязательное)
|
||
if (typeof REQ_F2 !== 'undefined' && REQ_F2 == '1') {
|
||
var f2 = $('#in_author_city');
|
||
setInvalid(f2, !f2.val().trim(), COMMENT_JS_FILL + " " + NAME_F2);
|
||
}
|
||
|
||
// Проверка Текста
|
||
setInvalid($(form.comment_text), !form.comment_text.value.trim(), COMMENT_JS_ADD_COMMENT_TEXT);
|
||
|
||
// Проверка файлов в основной форме (Мультизагрузка)
|
||
var $fileInput = $(form).find('#comment_image');
|
||
if ($fileInput.length && $fileInput[0].files.length > 0) {
|
||
var files = $fileInput[0].files;
|
||
for (var i = 0; i < files.length; i++) {
|
||
if (!checkFileConsistency(files[i], $('#file_error'))) {
|
||
$fileInput.addClass('is-invalid');
|
||
isValid = false;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!isValid) {
|
||
// Скроллим к первой ошибке
|
||
$('html, body').animate({
|
||
scrollTop: $('.is-invalid:first').offset().top - 130
|
||
}, 300);
|
||
}
|
||
|
||
return isValid;
|
||
}
|
||
|
||
/* --- ДЕЙСТВИЯ (DELETE, LOCK, OPEN/CLOSE, VOTE) --- */
|
||
function cAction(obj, action) {
|
||
var $btn = $(obj);
|
||
var cid = $btn.data('id');
|
||
|
||
if (!cid && action !== 'open' && action !== 'close') return;
|
||
|
||
$.get(aveabspath + 'index.php', {
|
||
module: 'comment',
|
||
action: action,
|
||
docid: typeof DOC_ID !== 'undefined' ? DOC_ID : '',
|
||
Id: cid ? cid : ''
|
||
}, function() {
|
||
if (action === 'close') {
|
||
$btn.attr('id', 'mod_comment_open')
|
||
.removeClass('btn-outline-danger').addClass('btn-outline-success')
|
||
.html('<i class="bi bi-lock-fill me-1"></i> ' + COMMENT_SITE_OPEN);
|
||
$('#mod_comment_new').fadeOut(300);
|
||
}
|
||
else if (action === 'open') {
|
||
$btn.attr('id', 'mod_comment_close')
|
||
.removeClass('btn-outline-success').addClass('btn-outline-danger')
|
||
.html('<i class="bi bi-unlock-fill me-1"></i> ' + COMMENT_SITE_CLOSE);
|
||
$('#mod_comment_new').fadeIn(300);
|
||
}
|
||
if (action === 'delete') {
|
||
var $commentBlock = $btn.closest('.mod_comment_comment');
|
||
$commentBlock.fadeOut(300, function() { $(this).remove(); });
|
||
}
|
||
// показать / скрыть комментарий
|
||
|
||
|
||
// показать / скрыть комментарий
|
||
if (action === 'unlock' || action === 'lock') {
|
||
var $icon = $btn.find('i');
|
||
var $card = $btn.closest('.mod_comment_comment');
|
||
|
||
if (action === 'lock') {
|
||
// --- Статус 0: СКРЫТО ---
|
||
$icon.attr('class', 'bi bi-eye-slash');
|
||
$btn.removeClass('text-success text-muted').addClass('text-danger').attr('title', COMMENT_ICON_SHOW);
|
||
|
||
$card.addClass('opacity-75 border-warning');
|
||
|
||
// Ищем плашку. Если её нет — создаем один раз.
|
||
var $alert = $card.find('.alert-warning');
|
||
if ($alert.length === 0) {
|
||
var alertHtml = '<div class="alert alert-warning py-1 px-2 mb-2 small d-flex align-items-center border-0 shadow-sm" style="border-left: 4px solid #ffc107 !important; display:none;">' +
|
||
'<i class="bi bi-clock-history me-2 text-dark"></i>' +
|
||
'<span class="text-dark">' + COMMENT_WAITING_MODERATION + '</span>' +
|
||
'</div>';
|
||
$card.find('.flex-grow-1').prepend(alertHtml);
|
||
$alert = $card.find('.alert-warning');
|
||
}
|
||
$alert.stop(true, true).fadeIn(300);
|
||
} else {
|
||
// --- Статус 1: ВИДИМО ---
|
||
$icon.attr('class', 'bi bi-eye');
|
||
$btn.removeClass('text-danger text-muted').addClass('text-success').attr('title', COMMENT_ICON_HIDE);
|
||
|
||
$card.removeClass('opacity-75 border-warning');
|
||
|
||
// Надежное скрытие: используем callback, чтобы после анимации точно убрать элемент
|
||
$card.find('.alert-warning').stop(true, true).fadeOut(300, function() {
|
||
$(this).remove(); // Удаляем плашку из кода совсем, чтобы при следующем "скрыть" она создалась чисто
|
||
});
|
||
}
|
||
}
|
||
|
||
});
|
||
}
|
||
|
||
/* --- ИНИЦИАЛИЗАЦИЯ --- */
|
||
$(document).ready(function() {
|
||
initCommentTimers();
|
||
var $doc = $(document);
|
||
|
||
// Убираем красную рамку, когда пользователь начинает исправлять поле
|
||
$doc.on('input change', '.form-control', function() {
|
||
$(this).removeClass('is-invalid');
|
||
$(this).next('.invalid-feedback').fadeOut(200, function() { $(this).remove(); });
|
||
});
|
||
|
||
// Инициализация тултипов для всех элементов с data-bs-toggle="tooltip"
|
||
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
||
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||
return new bootstrap.Tooltip(tooltipTriggerEl)
|
||
})
|
||
|
||
// Счетчик: Количество оставшихся символов 1000 для формы создания комментария
|
||
// Используем переменную MAX_CHARS, которая приходит из настроек модуля
|
||
$('#in_message').limit(MAX_CHARS, '#charsLeft_new');
|
||
|
||
$doc.on('change', '#comment_image', function() {
|
||
var input = this;
|
||
var $errorBox = $('#file_error');
|
||
var $previewWrapper = $('#image_preview_wrapper');
|
||
|
||
$previewWrapper.empty().addClass('d-none');
|
||
$errorBox.addClass('d-none');
|
||
$(input).removeClass('is-invalid');
|
||
|
||
if (input.files && input.files.length > 0) {
|
||
var files = Array.from(input.files);
|
||
$previewWrapper.removeClass('d-none').addClass('d-flex flex-wrap gap-2');
|
||
|
||
files.forEach(function(file) {
|
||
if (checkFileConsistency(file, $errorBox)) {
|
||
var reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
var imgHtml = `
|
||
<div class="position-relative preview-item">
|
||
<img src="${e.target.result}" class="img-thumbnail" style="max-height: 100px; min-width: 100px; object-fit: cover;">
|
||
<button type="button" class="btn btn-danger btn-sm position-absolute top-0 end-0 remove-new-img"
|
||
style="padding: 0 5px; margin: 2px;" title="${COMMENT_MOD_DEL_DEL}">
|
||
<i class="bi bi-x-lg"></i>
|
||
</button>
|
||
</div>`;
|
||
$previewWrapper.append(imgHtml);
|
||
}
|
||
reader.readAsDataURL(file);
|
||
} else {
|
||
$(input).val('').addClass('is-invalid');
|
||
$previewWrapper.addClass('d-none');
|
||
}
|
||
});
|
||
}
|
||
});
|
||
|
||
// обработчик, доработанный под мультизагрузку
|
||
$doc.on('click', '#remove_image_btn', function() {
|
||
$('#comment_image').val(''); // Очищаем выбор файлов
|
||
$('#image_preview_wrapper').addClass('d-none').empty(); // Скрываем и полностью очищаем блок превью
|
||
});
|
||
|
||
// удаление картинок по одной (крестиком на самой картинке)
|
||
$doc.on('click', '.remove-new-img', function() {
|
||
$(this).closest('.preview-item').remove(); // Удаляем только этот контейнер с картинкой
|
||
|
||
// Если после удаления картинок внутри не осталось — скрываем обертку и чистим инпут
|
||
if ($('#image_preview_wrapper').children().length === 0) {
|
||
$('#image_preview_wrapper').addClass('d-none');
|
||
$('#comment_image').val('');
|
||
}
|
||
});
|
||
|
||
// --- ВЫБОР РЕЙТИНГА В ФОРМЕ ---
|
||
$doc.on('mouseenter', '.star-choice', function() {
|
||
var val = $(this).data('value');
|
||
$(this).parent().find('.star-choice').each(function() {
|
||
if ($(this).data('value') <= val) {
|
||
$(this).removeClass('bi-star').addClass('bi-star-fill');
|
||
} else {
|
||
$(this).removeClass('bi-star-fill').addClass('bi-star');
|
||
}
|
||
});
|
||
});
|
||
|
||
$doc.on('mouseleave', '.rating-edit-block, #rating_wrapper', function() {
|
||
var $block = $(this);
|
||
var currentRating;
|
||
|
||
if ($block.attr('id') === 'rating_wrapper') {
|
||
currentRating = parseInt($('#comment_user_rating').val()) || 0;
|
||
} else {
|
||
var cid = $block.find('input[type="hidden"]').attr('id').replace('rating_input_', '');
|
||
currentRating = parseInt($('#rating_input_' + cid).val()) || 0;
|
||
}
|
||
|
||
$block.find('.star-choice').each(function() {
|
||
if ($(this).data('value') <= currentRating) {
|
||
$(this).removeClass('bi-star').addClass('bi-star-fill');
|
||
} else {
|
||
$(this).removeClass('bi-star-fill').addClass('bi-star');
|
||
}
|
||
});
|
||
});
|
||
|
||
$doc.on('click', '.star-choice', function() {
|
||
var val = $(this).data('value');
|
||
var ratingInput = $(this).closest('.rating-edit-block').find('input[type="hidden"]');
|
||
if (ratingInput.length) {
|
||
ratingInput.val(val);
|
||
} else {
|
||
$('#comment_user_rating').val(val);
|
||
}
|
||
$(this).prevAll().addBack().removeClass('bi-star').addClass('bi-star-fill');
|
||
$(this).nextAll().removeClass('bi-star-fill').addClass('bi-star');
|
||
});
|
||
|
||
$doc.on('click', '#buttonReset', function() {
|
||
$('#comment_user_rating').val(0);
|
||
$('#user_rating_stars .star-choice').removeClass('bi-star-fill').addClass('bi-star');
|
||
$('#rating_wrapper').show(); // Показываем блок рейтинга обратно
|
||
$('#image_preview_wrapper').addClass('d-none');
|
||
$('#charsLeft_new').text(MAX_CHARS);
|
||
$('.form-control').removeClass('is-invalid');
|
||
$('.invalid-feedback').remove();
|
||
$('#parent_id').val(''); // Очищаем ID родителя, чтобы комментарий снова стал корневым
|
||
});
|
||
|
||
$doc.on('click', '#reset_stars', function(e) {
|
||
e.preventDefault();
|
||
$('#comment_user_rating').val(0);
|
||
$('#user_rating_stars .star-choice').removeClass('bi-star-fill').addClass('bi-star');
|
||
});
|
||
|
||
$doc.on('click', '.reset-edit-stars', function(e) {
|
||
e.preventDefault();
|
||
var cid = $(this).data('id');
|
||
$('#rating_input_' + cid).val(0);
|
||
$('#user_rating_stars_' + cid + ' .star-choice').removeClass('bi-star-fill').addClass('bi-star');
|
||
});
|
||
|
||
// --- ГОЛОСОВАНИЕ (РЕЙТИНГ УЖЕ ОПУБЛИКОВАННЫХ) ---
|
||
$doc.on('mouseenter', '.star-item', function() {
|
||
var $parent = $(this).parent();
|
||
if ($parent.hasClass('comment-like')) {
|
||
$(this).css('transform', 'scale(1.2)');
|
||
}
|
||
else {
|
||
$(this).prevAll().addBack().removeClass('bi-star').addClass('bi-star-fill');
|
||
$(this).nextAll().removeClass('bi-star-fill').addClass('bi-star');
|
||
}
|
||
});
|
||
|
||
$doc.on('mouseleave', '.star-item', function() {
|
||
var $parent = $(this).parent();
|
||
if ($parent.hasClass('comment-like')) { $(this).css('transform', 'scale(1)'); }
|
||
});
|
||
|
||
$doc.on('click', '.star-item', function() {
|
||
var $container = $(this).closest('.comment-rating-container');
|
||
var commentId = $container.data('id');
|
||
var voteValue = $(this).data('value');
|
||
|
||
$.ajax({
|
||
url: 'index.php?module=comment&action=vote&ajax=1',
|
||
type: 'POST',
|
||
data: { comment_id: commentId, vote: voteValue, ajax: 1 },
|
||
success: function(response) {
|
||
var res = response.toString().trim();
|
||
if (res.indexOf('success') !== -1) {
|
||
$container.html('<span class="text-success small fw-bold">' + COMMENT_JS_THX + '</span>');
|
||
setTimeout(function(){ location.reload(); }, 1000);
|
||
} else if (res.indexOf('already_voted') !== -1) {
|
||
alert(COMMENT_JS_VOTE_A);
|
||
} else if (res.indexOf('own_comment') !== -1) {
|
||
alert(COMMENT_JS_VOTE_B);
|
||
} else if (res.indexOf('forbidden_anon') !== -1) {
|
||
alert('COMMENT_JS_VOTE_C');
|
||
} else {
|
||
alert(COMMENT_JS_VOTE_ERR);
|
||
}
|
||
},
|
||
error: function(xhr) {alert(COMMENT_JS_VOTE_ERR_A + ' ' + xhr.status);}
|
||
});
|
||
});
|
||
|
||
$doc.on('click', '#mod_comment_close', function(e) { e.preventDefault(); cAction(this, 'close'); });
|
||
$doc.on('click', '#mod_comment_open', function(e) { e.preventDefault(); cAction(this, 'open'); });
|
||
|
||
// КНОПКА ОТВЕТА
|
||
$doc.off('click', '.mod_comment_answer').on('click', '.mod_comment_answer', function(e) {
|
||
e.preventDefault();
|
||
var cid = $(this).data('id');
|
||
var $form = $('#mod_comment_new');
|
||
$form.insertAfter('#comment_' + cid).show();
|
||
$('#parent_id').val(cid);
|
||
// --- СКРЫТИЕ РЕЙТИНГА ДЛЯ ОТВЕТОВ ---
|
||
if (typeof SHOW_USER_RATING_REPLIES !== 'undefined' && SHOW_USER_RATING_REPLIES == '0') {
|
||
$('#rating_wrapper').hide(); // Скрываем весь блок рейтинга
|
||
$('#comment_user_rating').val(0); // Сбрасываем значение
|
||
$('#user_rating_stars .star-choice').removeClass('bi-star-fill').addClass('bi-star');
|
||
} else {
|
||
$('#rating_wrapper').show();
|
||
}
|
||
// --- КОНЕЦ ---
|
||
$('html, body').animate({ scrollTop: $form.offset().top - 150 }, 500);
|
||
$('#in_message').focus();
|
||
});
|
||
|
||
// Удаление
|
||
$doc.off('click', '.mod_comment_delete').on('click', '.mod_comment_delete', function(e) {
|
||
e.preventDefault();
|
||
if (confirm(COMMENT_JS_DELDEL_CONFIRM)) cAction(this, 'delete');
|
||
});
|
||
|
||
|
||
// Универсальный обработчик для переключателя видимости
|
||
// Оставляем все селекторы для надежности
|
||
$doc.off('click', '.mod_comment_toggle, .mod_comment_lock, .mod_comment_unlock')
|
||
.on('click', '.mod_comment_toggle, .mod_comment_lock, .mod_comment_unlock', function(e) {
|
||
e.preventDefault();
|
||
var $btn = $(this);
|
||
var $icon = $btn.find('i');
|
||
|
||
// Теперь проверяем перечеркнутый глаз (скрыто) или закрытый замок (на случай если остался в кэше)
|
||
// Если глаз перечеркнут ИЛИ замок закрыт — значит в базе 0, шлем 'unlock'
|
||
var isHidden = $icon.hasClass('bi-eye-slash') || $icon.hasClass('bi-lock-fill');
|
||
var actionToSend = isHidden ? 'unlock' : 'lock';
|
||
|
||
cAction(this, actionToSend);
|
||
});
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
// НАЧАЛО $doc.off('click', '.mod_comment_edit') РЕДАКТИРОВАНИЕ КОММЕНТАРИЯ
|
||
|
||
$doc.off('click', '.mod_comment_edit').on('click', '.mod_comment_edit', function(e) {
|
||
e.preventDefault();
|
||
var cid = $(this).data('id');
|
||
var $wrapper = $('#comment_wrapper_' + cid);
|
||
|
||
if ($wrapper.length === 0) {
|
||
console.error("Обертка #comment_wrapper_" + cid + " не найдена!");
|
||
return;
|
||
}
|
||
|
||
var $textBlock = $wrapper.find('.mod_comment_text').first();
|
||
|
||
// Пытаемся найти блок с картинками
|
||
var $attachedImagesBlock = $wrapper.find('.mod_comment_attached_image');
|
||
if ($attachedImagesBlock.length === 0) {
|
||
$attachedImagesBlock = $wrapper.find('.comment-files, .attached-images');
|
||
}
|
||
|
||
if ($wrapper.find('.edit-form-container').length > 0) return;
|
||
|
||
var isMyOwn = $wrapper.attr('data-is-own') == '1';
|
||
var currentRating = parseInt($wrapper.attr('data-user-rating')) || 0;
|
||
|
||
$('.edit-form-container').remove();
|
||
$('.mod_comment_text').show();
|
||
$('.mod_comment_attached_image, .comment-files').show();
|
||
|
||
var cleanText = $textBlock.html().replace(/<br\s*\/?>/mg, "\n").trim();
|
||
|
||
// --- СБОР ТЕКУЩИХ ВЛОЖЕНИЙ ---
|
||
var existingImagesHtml = '';
|
||
var $imageContainer = $('#image_container_' + cid);
|
||
|
||
// Ищем именно "карточки" из шаблона (класс .comment-image-item)
|
||
var $foundItems = $imageContainer.find('.comment-image-item');
|
||
|
||
if ($foundItems.length > 0) {
|
||
existingImagesHtml = '<div class="d-flex flex-wrap gap-2 mb-3 p-2 bg-white border rounded border-dashed">';
|
||
|
||
$foundItems.each(function(index) {
|
||
var $item = $(this);
|
||
var $link = $item.find('a');
|
||
var fileUrl = $link.attr('href');
|
||
if (!fileUrl) return;
|
||
|
||
var fileName = fileUrl.split('/').pop();
|
||
var fileExt = fileName.split('.').pop().toLowerCase();
|
||
var isImg = ['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(fileExt);
|
||
|
||
// Используем стиль превью
|
||
var previewContent = isImg
|
||
? `<img src="${fileUrl}" class="img-thumbnail" style="width: 80px; height: 80px; object-fit: cover;">`
|
||
: `<div class="d-flex align-items-center justify-content-center bg-light text-dark rounded border small fw-bold"
|
||
style="width: 80px; height: 80px; text-transform: uppercase;">${fileExt}</div>`;
|
||
|
||
existingImagesHtml += `
|
||
<div id="existing_wrapper_${cid}_${index}" class="position-relative">
|
||
${previewContent}
|
||
<button type="button" class="btn btn-danger btn-sm position-absolute top-0 end-0 remove-existing-img"
|
||
data-id="${cid}" data-img-path="${fileName}" data-target="existing_wrapper_${cid}_${index}"
|
||
style="padding: 0 4px; margin: 1px; line-height: 1; z-index: 10;">
|
||
<i class="bi bi-x-lg" style="font-size: 0.7rem;"></i>
|
||
</button>
|
||
</div>`;
|
||
});
|
||
existingImagesHtml += '</div>';
|
||
}
|
||
|
||
// --- РЕЙТИНГ ---
|
||
var starsEditBlock = '';
|
||
var parentIdVal = parseInt($wrapper.attr('data-parent')) || 0;
|
||
var isReply = parentIdVal > 0;
|
||
var ratingForbiddenForReply = (isReply && typeof SHOW_USER_RATING_REPLIES !== 'undefined' && SHOW_USER_RATING_REPLIES == '0');
|
||
|
||
if (typeof SHOW_USER_RATING !== 'undefined' && SHOW_USER_RATING == '1' && isMyOwn && !ratingForbiddenForReply) {
|
||
if (typeof UGROUP !== 'undefined' && (UGROUP != '2' || RATING_ANON_SET == '1')) {
|
||
var starsHtml = '';
|
||
for(var i=1; i<=5; i++) {
|
||
var starClass = (i <= currentRating) ? 'bi-star-fill' : 'bi-star';
|
||
starsHtml += `<i class="star-choice ${starClass}" data-value="${i}" style="cursor: pointer; color: #ffc107; font-size: 1.2rem; margin-right: 2px;"></i>`;
|
||
}
|
||
starsEditBlock = `
|
||
<div class="mb-2 rating-edit-block">
|
||
<label class="small text-muted d-block mb-1">${COMMENT_JS_RATING_ST_EDIT}</label>
|
||
<div class="d-flex align-items-center">
|
||
<div id="user_rating_stars_${cid}">${starsHtml}</div>
|
||
<a href="javascript:void(0);" class="reset-edit-stars ms-3 text-decoration-none small text-muted" data-id="${cid}">
|
||
<i class="bi bi-x-circle"></i> ${COMMENT_JS_RATING_ST_EDIT_A}
|
||
</a>
|
||
</div>
|
||
<input type="hidden" name="comment_user_rating" id="rating_input_${cid}" value="${currentRating}" />
|
||
</div>`;
|
||
}
|
||
}
|
||
|
||
// --- СБОРКА ФОРМЫ ---
|
||
var editHtml = `
|
||
<div class="edit-form-container border rounded p-3 bg-light mt-2 mb-2" data-cid="${cid}">
|
||
<textarea rows="5" id="ta_${cid}" class="form-control mb-3">${cleanText}</textarea>
|
||
${starsEditBlock}
|
||
|
||
<p class="small text-muted mb-1">${COMMENT_JS_LOADED_FILES}</p>
|
||
${existingImagesHtml}
|
||
|
||
<div class="mb-2">
|
||
<label class="small text-muted mb-1">${COMMENT_JS_ADD_NEW_FILES}</label>
|
||
<div id="file_error_${cid}" class="js-file-error text-danger small fw-bold mb-1" style="display:none;"></div>
|
||
<input type="file" name="comment_image[]" id="new_file_${cid}" class="form-control form-control-sm" multiple>
|
||
</div>
|
||
<input type="hidden" name="delete_files" id="delete_files_${cid}" value="">
|
||
|
||
<div class="d-flex gap-2 mt-3 align-items-center">
|
||
<button type="button" class="btn btn-sm btn-primary saveButton" data-id="${cid}">${COMMENT_JS_EDIT_BUT_SAVE}</button>
|
||
<button type="button" class="btn btn-sm btn-secondary cancelButton">${COMMENT_JS_EDIT_BUT_CANCEL}</button>
|
||
<small class="ms-auto text-muted">${COMMENT_JS_EDIT_BUT_LEFT} <span id="charsLeft_${cid}"></span></small>
|
||
</div>
|
||
</div>`;
|
||
|
||
// Прячем текст комментария
|
||
$textBlock.hide();
|
||
|
||
// Прячем ВЕСЬ контейнер с файлами
|
||
$('#image_container_' + cid).attr('style', 'display: none !important;');
|
||
|
||
// Для надежности скрываем всё остальное
|
||
$wrapper.find('.mod_comment_attached_images, .comment-files').hide();
|
||
|
||
$textBlock.after(editHtml);
|
||
|
||
// --- НАКОПИТЕЛЬНОЕ ПРЕВЬЮ НОВЫХ ФАЙЛОВ ---
|
||
var pendingFiles = []; // Массив для хранения всех выбранных файлов
|
||
|
||
$wrapper.find('.edit-form-container').data('pendingFiles', pendingFiles);
|
||
|
||
$('#new_file_' + cid).on('change', function() {
|
||
var files = this.files;
|
||
var $input = $(this);
|
||
var $currentEditForm = $input.closest('.edit-form-container');
|
||
var $errorDisplay = $currentEditForm.find('.js-file-error');
|
||
var maxLimit = parseInt(typeof MAX_FILES_COUNT !== 'undefined' ? MAX_FILES_COUNT : 5);
|
||
|
||
if (files) {
|
||
$errorDisplay.text('').hide();
|
||
|
||
$.each(files, function(i, file) {
|
||
var alreadyInPost = $currentEditForm.find('[id^="existing_wrapper_"]').length;
|
||
var inPending = pendingFiles.filter(f => f !== null).length;
|
||
|
||
if ((alreadyInPost + inPending) >= maxLimit) {
|
||
$errorDisplay.text(' ' + COMMENT_JS_LIMIT_AB + ' ' + maxLimit + ' ' + COMMENT_JS_LIMIT_ABC).show();
|
||
return false;
|
||
}
|
||
|
||
// ПРОВЕРКА ЧЕРЕЗ ФУНКЦИЮ (она берет ALLOWED_EXTENSIONS)
|
||
if (!checkFileConsistency(file, $errorDisplay)) return true;
|
||
|
||
pendingFiles.push(file);
|
||
var currentIndex = pendingFiles.length - 1;
|
||
var fileExt = file.name.split('.').pop().toLowerCase();
|
||
var isImage = ['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(fileExt);
|
||
|
||
var reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
var $previewContainer = $('#new_files_preview_' + cid);
|
||
if ($previewContainer.length === 0) {
|
||
$input.after(`<div id="new_files_preview_${cid}" class="d-flex flex-wrap gap-2 mt-2"></div>`);
|
||
$previewContainer = $('#new_files_preview_' + cid);
|
||
}
|
||
|
||
var content = isImage
|
||
? `<img src="${e.target.result}" class="img-thumbnail" style="width: 80px; height: 80px; object-fit: cover; border: 2px solid #0d6efd;">`
|
||
: `<div class="d-flex align-items-center justify-content-center bg-light text-dark rounded border shadow-sm"
|
||
style="width: 80px; height: 80px; font-weight: bold; text-transform: uppercase; font-size: 12px; border: 2px solid #0d6efd !important;">${fileExt}</div>`;
|
||
|
||
$previewContainer.append(`
|
||
<div class="position-relative new-file-item" data-index="${currentIndex}">
|
||
${content}
|
||
<button type="button" class="btn btn-danger btn-sm position-absolute top-0 end-0 remove-new-file" style="padding: 0 5px; margin: 2px; line-height: 1;">
|
||
<i class="bi bi-x-lg" style="font-size: 0.7rem;"></i>
|
||
</button>
|
||
</div>`);
|
||
};
|
||
|
||
if (isImage) reader.readAsDataURL(file);
|
||
else reader.onload({target: {result: ''}});
|
||
});
|
||
}
|
||
});
|
||
|
||
// Обработчик удаления НОВОГО файла из превью (до сохранения)
|
||
$doc.off('click', '.remove-new-file').on('click', '.remove-new-file', function() {
|
||
var $item = $(this).closest('.new-file-item');
|
||
var index = $item.data('index');
|
||
|
||
pendingFiles[index] = null; // Помечаем как удаленный
|
||
$item.remove();
|
||
|
||
// Ищем через ближайший контейнер формы, чтобы точно попасть в нужный блок текста
|
||
$(this).closest('.edit-form-container').find('.js-file-error').hide().text('');
|
||
});
|
||
// --- КОНЕЦ ВСТАВКИ ---
|
||
|
||
// Добавил поддержку лимита символов
|
||
if ($.fn.limit) {
|
||
$('#ta_' + cid).limit(MAX_CHARS, '#charsLeft_' + cid);
|
||
}
|
||
$('#ta_' + cid).focus();
|
||
});
|
||
|
||
|
||
// КОНЕЦ $doc.off('click', '.mod_comment_edit')
|
||
|
||
|
||
$doc.on('click', '.remove-existing-img', function() {
|
||
var cid = $(this).data('id');
|
||
var imgPath = $(this).data('img-path');
|
||
var targetId = $(this).data('target');
|
||
|
||
// 1. Очищаем текст ошибки
|
||
$('#file_error_' + cid).addClass('d-none').text('');
|
||
|
||
var $deleteInput = $('#delete_files_' + cid);
|
||
var currentDeleteList = $deleteInput.val() ? $deleteInput.val().split(',') : [];
|
||
|
||
if (currentDeleteList.indexOf(imgPath) === -1) {
|
||
currentDeleteList.push(imgPath);
|
||
$deleteInput.val(currentDeleteList.join(','));
|
||
}
|
||
|
||
// 2. Скрываем и удвляем элемент, чтобы счетчик .length его больше не видел
|
||
$('#' + targetId).fadeOut(300, function() {
|
||
$(this).remove();
|
||
});
|
||
});
|
||
|
||
// Обработка выбора НОВОГО файла при редактировании
|
||
$doc.on('change', 'input[id^="new_file_"]', function() {
|
||
var cid = $(this).attr('id').replace('new_file_', '');
|
||
var input = this;
|
||
|
||
// 1. Сначала находим блок
|
||
var $errorBox = $('#file_error_' + cid);
|
||
var $previewWrapper = $('#edit_preview_wrapper_' + cid);
|
||
|
||
if (input.files && input.files.length > 0) {
|
||
$previewWrapper.find('.preview-item-new').remove();
|
||
$previewWrapper.removeClass('d-none');
|
||
|
||
Array.from(input.files).forEach(function(file) {
|
||
if (checkFileConsistency(file, $errorBox)) {
|
||
// Если файл прошел проверку - только тогда можно спрятать ошибку
|
||
$errorBox.addClass('d-none').text('');
|
||
|
||
var reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
var item = `
|
||
<div class="position-relative preview-item-new">
|
||
<img src="${e.target.result}" class="img-thumbnail" style="max-height: 100px;">
|
||
</div>`;
|
||
$previewWrapper.append(item);
|
||
}
|
||
reader.readAsDataURL(file);
|
||
}
|
||
});
|
||
}
|
||
});
|
||
|
||
// Удаление НОВОГО выбранного файла в режиме редактирования
|
||
$doc.on('click', '.remove-edit-new-img', function() {
|
||
var cid = $(this).data('id');
|
||
$('#new_file_' + cid).val('');
|
||
$('#edit_preview_wrapper_' + cid).addClass('d-none');
|
||
});
|
||
|
||
// Обработка кнопки "Отмена" при редактировании
|
||
$doc.on('click', '.cancelButton', function() {
|
||
var $container = $(this).closest('.edit-form-container');
|
||
var $wrapper = $container.closest('[id^="comment_wrapper_"]');
|
||
var cid = $container.data('cid'); // Берем ID для обращения к контейнеру файлов
|
||
|
||
// 1. Возвращаем текст
|
||
$wrapper.find('.mod_comment_text').show();
|
||
|
||
// 2. Возвращаем оригинальный блок с картинками
|
||
// СНАЧАЛА ЧИСТИМ ИНЛАЙН СТИЛЬ
|
||
$('#image_container_' + cid).attr('style', '');
|
||
|
||
// Затем стандартный показ
|
||
$wrapper.find('.mod_comment_attached_image, .mod_comment_attached_images, .comment-files, .attached-images').show();
|
||
|
||
// 3. Если мы скрывали изображения напрямую через $foundImgs.hide(), показываем их
|
||
$wrapper.find('img').show();
|
||
// И ссылки
|
||
$wrapper.find('a').show();
|
||
|
||
// 4. Удаляем форму редактирования
|
||
$container.remove();
|
||
});
|
||
|
||
$doc.on('click', '.saveButton', function() {
|
||
var $btn = $(this);
|
||
var cid = $btn.data('id');
|
||
var $container = $btn.closest('.edit-form-container');
|
||
|
||
// Достаем накопительный массив файлов
|
||
var pendingFiles = $container.data('pendingFiles') || [];
|
||
|
||
var fd = new FormData();
|
||
fd.append('module', 'comment');
|
||
fd.append('action', 'edit');
|
||
fd.append('Id', cid);
|
||
fd.append('text', $('#ta_' + cid).val());
|
||
|
||
var rInput = $('#rating_input_' + cid);
|
||
if (rInput.length) fd.append('user_rating', rInput.val());
|
||
|
||
fd.append('delete_files', $('#delete_files_' + cid).val());
|
||
|
||
// --- берем файлы из массива ---
|
||
pendingFiles.forEach(function(file) {
|
||
if (file !== null) { // Проверяем, что файл не был удален крестиком
|
||
fd.append('comment_image[]', file);
|
||
}
|
||
});
|
||
// ------------------------------------
|
||
|
||
$.ajax({
|
||
url: aveabspath + 'index.php?ajax=1',
|
||
type: 'POST',
|
||
data: fd,
|
||
processData: false,
|
||
contentType: false,
|
||
xhr: function() {
|
||
var xhr = new window.XMLHttpRequest();
|
||
xhr.upload.addEventListener("progress", function(evt) {
|
||
if (evt.lengthComputable) {
|
||
var pct = Math.round((evt.loaded / evt.total) * 100);
|
||
$('#edit_progress_container_' + cid).removeClass('d-none');
|
||
$('#edit_progress_bar_' + cid).css('width', pct + '%');
|
||
}
|
||
}, false);
|
||
return xhr;
|
||
},
|
||
beforeSend: function() { $btn.prop('disabled', true).text('...'); },
|
||
success: function() { location.reload(); }
|
||
});
|
||
});
|
||
|
||
|
||
|
||
// Глобальный массив для файлов НОВОГО комментария
|
||
var newCommentPendingFiles = [];
|
||
|
||
// Обработка выбора файлов в основной форме
|
||
// .off('change') чтобы не было дублей
|
||
$doc.off('change', '#comment_image').on('change', '#comment_image', function() {
|
||
var files = this.files;
|
||
var $previewWrapper = $('#image_preview_wrapper');
|
||
var $errorDisplay = $('#file_error');
|
||
|
||
// 1. Лимит из настроек
|
||
var maxLimit = parseInt(typeof MAX_FILES_COUNT !== 'undefined' ? MAX_FILES_COUNT : 5);
|
||
|
||
if (files) {
|
||
$errorDisplay.addClass('d-none').text(''); // Сбрасываем старые ошибки
|
||
|
||
$.each(files, function(i, file) {
|
||
// 2. Считаем, сколько сейчас РЕАЛЬНО файлов в очереди (не null)
|
||
var currentInQueue = newCommentPendingFiles.filter(function(f) { return f !== null; }).length;
|
||
|
||
// 3. ПРОВЕРКА ЛИМИТА: если уже достигли максимума
|
||
if (currentInQueue >= maxLimit) {
|
||
$errorDisplay.removeClass('d-none').text(COMMENT_JS_LIMIT_ABCD + maxLimit + ' ' + COMMENT_JS_LIMIT_ABCDE);
|
||
return false; // break - полностью выходим из цикла $.each
|
||
}
|
||
|
||
// 4. Валидация (размер, расширение)
|
||
if (typeof checkFileConsistency === 'function') {
|
||
if (!checkFileConsistency(file, $errorDisplay)) {
|
||
return true; // continue - этот файл плохой, идем к следующему
|
||
}
|
||
}
|
||
|
||
// Если прошли все проверки — добавляем в очередь
|
||
newCommentPendingFiles.push(file);
|
||
var currentIndex = newCommentPendingFiles.length - 1;
|
||
|
||
var reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
if ($previewWrapper.find(`[data-index="${currentIndex}"]`).length > 0) return;
|
||
|
||
// Определяем, что вставить: картинку или заглушку с текстом расширения
|
||
var fileExt = file.name.split('.').pop().toLowerCase();
|
||
var isImage = ['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(fileExt);
|
||
|
||
// Формируем контент: либо твой оригинальный img, либо блок с расширением
|
||
var content = isImage
|
||
? `<img src="${e.target.result}" class="img-thumbnail" style="width: 80px; height: 80px; object-fit: cover; border: 2px solid #198754;">`
|
||
: `<div class="d-flex align-items-center justify-content-center bg-primary text-white rounded" style="width: 80px; height: 80px; font-weight: bold; text-transform: uppercase; border: 2px solid #0d6efd;">${fileExt}</div>`;
|
||
|
||
$previewWrapper.removeClass('d-none');
|
||
$previewWrapper.append(`
|
||
<div class="position-relative new-comment-file-item" data-index="${currentIndex}">
|
||
${content}
|
||
<button type="button" class="btn btn-danger btn-sm position-absolute top-0 end-0 remove-new-comment-img"
|
||
style="padding: 0 5px; margin: 2px; line-height: 1;">
|
||
<i class="bi bi-x-lg" style="font-size: 0.7rem;"></i>
|
||
</button>
|
||
</div>`);
|
||
};
|
||
reader.readAsDataURL(file);
|
||
});
|
||
}
|
||
|
||
// Очищаем инпут в любом случае, чтобы можно было выбрать те же файлы снова
|
||
$(this).val('');
|
||
});
|
||
|
||
// Удаление выбранного файла из очереди в новой форме
|
||
$doc.on('click', '.remove-new-comment-img', function() {
|
||
var $item = $(this).closest('.new-comment-file-item');
|
||
var index = $item.data('index');
|
||
newCommentPendingFiles[index] = null; // Помечаем удаленным
|
||
$('#file_error').addClass('d-none').text('');
|
||
$item.fadeOut(200, function() {
|
||
$(this).remove();
|
||
if ($('#image_preview_wrapper').children().length === 0) {
|
||
$('#image_preview_wrapper').addClass('d-none');
|
||
}
|
||
});
|
||
});
|
||
|
||
// Очистка при нажатии кнопки "Сбросить" (reset)
|
||
$doc.on('click', '#buttonReset', function() {
|
||
newCommentPendingFiles = [];
|
||
$('#image_preview_wrapper').empty().addClass('d-none');
|
||
// убрать текст ошибки
|
||
$('#file_error').addClass('d-none').text('');
|
||
});
|
||
|
||
// Отправка новой формы (или ответа)
|
||
$doc.on('submit', '#mod_comment_new form', function(e) {
|
||
e.preventDefault();
|
||
if (!validate(this)) return false;
|
||
|
||
var $form = $(this);
|
||
var $btn = $form.find('[type="submit"]');
|
||
var originalBtnText = $btn.text();
|
||
var formData = new FormData(this);
|
||
|
||
// 1. Удаляем стандартные значения, чтобы они не дублировались из инпута
|
||
formData.delete('comment_image');
|
||
formData.delete('comment_image[]');
|
||
|
||
// 2. Добавляем файлы из нашего накопленного массива newCommentPendingFiles
|
||
if (newCommentPendingFiles && newCommentPendingFiles.length > 0) {
|
||
newCommentPendingFiles.forEach(function(file) {
|
||
if (file !== null) {
|
||
formData.append('comment_image[]', file);
|
||
}
|
||
});
|
||
}
|
||
|
||
$.ajax({
|
||
url: aveabspath + 'index.php?ajax=1',
|
||
type: 'POST',
|
||
data: formData,
|
||
processData: false,
|
||
contentType: false,
|
||
xhr: function() {
|
||
var xhr = new window.XMLHttpRequest();
|
||
xhr.upload.addEventListener("progress", function(evt) {
|
||
if (evt.lengthComputable) {
|
||
var pct = Math.round((evt.loaded / evt.total) * 100);
|
||
$('#upload_progress_container').removeClass('d-none');
|
||
$('#upload_progress_bar').css('width', pct + '%');
|
||
$('#upload_status_text').text(COMMENT_JS_LOADS_F + ' ' + pct + '%');
|
||
}
|
||
}, false);
|
||
return xhr;
|
||
},
|
||
beforeSend: function() { $btn.prop('disabled', true).text(COMMENT_JS_LOADS_R); },
|
||
success: function(data) {
|
||
if (data.includes('wrong_securecode')) {
|
||
alert(COMMENT_JS_SEC_CODE_WRONG);
|
||
getCaptha();
|
||
$btn.prop('disabled', false).text(originalBtnText);
|
||
$('#upload_progress_container').addClass('d-none');
|
||
} else {
|
||
// Очищаем массив перед перезагрузкой
|
||
newCommentPendingFiles = [];
|
||
$form[0].reset(); // Чистим текст и инпуты
|
||
$form.find('input[name="parent_id"]').val('0'); // Сбрасываем "режим ответа"
|
||
location.reload();
|
||
}
|
||
},
|
||
error: function() {
|
||
alert(COMMENT_JS_ERR_SRV);
|
||
$btn.prop('disabled', false).text(originalBtnText);
|
||
$('#upload_progress_container').addClass('d-none');
|
||
}
|
||
});
|
||
});
|
||
|
||
$doc.on('click', '#captcha img, #reload_captcha', function(e) { e.preventDefault(); getCaptha(); });
|
||
});
|
||
|
||
})(jQuery);
|
||
}
|
||
})(); |