650 lines
27 KiB
JavaScript
650 lines
27 KiB
JavaScript
/**
|
|
* Conrev.js
|
|
*
|
|
* 리뷰 슬라이더 및 리뷰 보기 보상 기능을 관리하는 모듈
|
|
* 사용자가 일정 시간 동안 리뷰를 보면 마일리지를 적립해주는 기능 구현
|
|
*/
|
|
const Conrev = (function() {
|
|
// 모듈 내부 변수 선언
|
|
let timer; // 리뷰 시간 카운트다운 타이머
|
|
let remainingTime; // 리뷰를 봐야 하는 남은 시간(초)
|
|
let currentEventId; // 현재 선택된 리뷰의 고유 ID
|
|
let startTime; // 리뷰 보기 시작 시간
|
|
let reviewCheckInterval; // 리뷰 팝업이 계속 열려있는지 확인하는 타이머
|
|
let currentSlideIndex = 0; // 현재 보고 있는 슬라이드 인덱스
|
|
let reviewData = []; // 서버에서 받아온 리뷰 데이터 저장 배열
|
|
let lastViewedSlideIndex = 0; // 마지막으로 본 슬라이드 인덱스 (중복 마일리지 적립 방지용)
|
|
let reviewLayer; // 리뷰 팝업 레이어 요소
|
|
let autoSlideTimer; // 자동 슬라이드 타이머
|
|
let mileageAwarded = false; // 마일리지 적립 완료 플래그 추가
|
|
|
|
/**
|
|
* 토스트 메시지 표시 함수
|
|
* @param {string} message - 표시할 메시지
|
|
*/
|
|
function showToast(message) {
|
|
// 기존에 토스트 요소가 있으면 제거
|
|
$('.toastPop').remove();
|
|
|
|
// 토스트 요소 생성 및 추가 (z-index 1001로 설정하여 모달 위에 표시)
|
|
const $toast = $('<div class="toastPop" style="z-index: 1001; position: fixed;">' + message + '</div>');
|
|
$('body').append($toast);
|
|
|
|
// 토스트 표시 및 자동 제거
|
|
setTimeout(function() {
|
|
$toast.addClass('show');
|
|
|
|
setTimeout(function() {
|
|
$toast.removeClass('show');
|
|
|
|
// 애니메이션 완료 후 요소 제거
|
|
setTimeout(function() {
|
|
$toast.remove();
|
|
}, 300);
|
|
}, 2000);
|
|
}, 100);
|
|
}
|
|
|
|
/**
|
|
* 마일리지 보상 정보 가져오기
|
|
* 현재 슬라이드의 리뷰에 맞는 보상 정보를 가져옴
|
|
* @param {string|number} bnfInnb - 리뷰 혜택 고유 번호 (선택사항)
|
|
*/
|
|
function fetchRewardInfo(bnfInnb) {
|
|
// 현재 슬라이드의 bnfInnb 사용하거나 파라미터로 받은 값 사용
|
|
const targetBnfInnb = bnfInnb || (reviewData.length > currentSlideIndex ? reviewData[currentSlideIndex].bnfInnb : '');
|
|
|
|
if (targetBnfInnb) {
|
|
$.ajax({
|
|
url: '/sopmal/bnft/getRewardInfo',
|
|
method: 'POST',
|
|
contentType: 'application/json;charset=UTF-8',
|
|
data: JSON.stringify({ bnfInnb: targetBnfInnb }),
|
|
success: function(response) {
|
|
if (response.success && response.mlgRwdAmt) {
|
|
// JSP의 금액 부분을 동적으로 업데이트
|
|
$('.topupPoint b').text(response.mlgRwdAmt + '원');
|
|
console.log('마일리지 보상 금액:', response.mlgRwdAmt);
|
|
} else {
|
|
console.error('마일리지 보상 정보를 가져오는데 실패했습니다.');
|
|
}
|
|
},
|
|
error: function(jqXHR, textStatus, errorThrown) {
|
|
console.error('마일리지 보상 정보 요청 중 오류 발생:', textStatus, errorThrown);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 슬라이더 초기화 함수
|
|
* 페이지 로드 시 호출되어 리뷰 슬라이더의 모든 기능을 설정
|
|
*/
|
|
function initializeSlider() {
|
|
// hidden input에서 JSON 형태의 리뷰 데이터 가져오기
|
|
try {
|
|
let rawData = $('#reviewDataInput').val();
|
|
// JSON 파싱을 위해 백슬래시를 이중 백슬래시로 변환 (이스케이프 처리)
|
|
rawData = rawData.replace(/\\(?!["])/g, "\\\\");
|
|
reviewData = JSON.parse(rawData) || [];
|
|
} catch (e) {
|
|
console.error('리뷰 데이터 파싱 실패:', e);
|
|
reviewData = [];
|
|
}
|
|
|
|
// 슬라이더 요소 참조 가져오기
|
|
const $slider = $('.reviewSlider ul'); // 슬라이더 컨테이너
|
|
const $slides = $('.reviewSlider li'); // 각 슬라이드 항목
|
|
const slideCount = $slides.length; // 전체 슬라이드 개수
|
|
let currentSlide = 0; // 현재 슬라이드 인덱스
|
|
|
|
// 페이지네이션 도트 동적 생성 (슬라이드 개수만큼 도트 생성)
|
|
const $pagination = $('.pagination');
|
|
for (let i = 0; i < slideCount; i++) {
|
|
$pagination.append('<div class="dot" data-slide="' + i + '"></div>');
|
|
}
|
|
// 첫 번째 도트를 활성화 상태로 설정
|
|
$pagination.find('.dot').first().addClass('active');
|
|
|
|
/**
|
|
* 슬라이드 이동 함수
|
|
* 현재 슬라이드 인덱스에 따라 슬라이더 위치와 페이지네이션 상태 업데이트
|
|
*/
|
|
function moveSlide() {
|
|
// 슬라이더 위치 조정 (CSS margin-left 사용하여 수평 이동)
|
|
$slider.css('margin-left', -currentSlide * 100 + '%');
|
|
// 현재 슬라이드에 해당하는 도트만 활성화
|
|
$pagination.find('.dot').removeClass('active').eq(currentSlide).addClass('active');
|
|
// 글로벌 변수 업데이트
|
|
currentSlideIndex = currentSlide;
|
|
// 리뷰 버튼 업데이트 (현재 슬라이드에 맞는 리뷰 데이터 연결)
|
|
updateReviewButton();
|
|
// 현재 슬라이드에 맞는 마일리지 보상 정보 업데이트
|
|
fetchRewardInfo();
|
|
}
|
|
|
|
// 슬라이드 이미지 로드 함수 호출 - JSP에서 CDN으로 이미 설정되므로 주석 처리
|
|
// loadImages();
|
|
|
|
/**
|
|
* 페이지네이션 도트 클릭 이벤트 핸들러
|
|
* 사용자가 특정 도트를 클릭하면 해당 슬라이드로 이동
|
|
*/
|
|
$pagination.on('click', '.dot', function() {
|
|
const newSlideIndex = $(this).data('slide');
|
|
currentSlide = newSlideIndex;
|
|
moveSlide();
|
|
// 도트 클릭으로 슬라이드 변경시에는 마일리지 적립하지 않음 (리뷰 보기를 통해서만 적립)
|
|
});
|
|
|
|
/**
|
|
* 마우스/터치 드래그 이벤트 처리
|
|
* 슬라이더를 드래그하여 다음/이전 슬라이드로 이동할 수 있게 함
|
|
*/
|
|
let isDragging = false; // 드래그 중인지 상태 플래그
|
|
let startPos = 0; // 드래그 시작 위치
|
|
let endPos = 0; // 드래그 종료 위치
|
|
|
|
// 드래그 시작 이벤트 (마우스다운 또는 터치스타트)
|
|
// $slider 대신 더 넓은 영역인 .revieBox에 이벤트 적용
|
|
$('.revieBox').on('mousedown touchstart', function(e) {
|
|
isDragging = true;
|
|
// 마우스 또는 터치 이벤트의 시작 위치 저장
|
|
startPos = e.pageX || e.originalEvent.touches[0].pageX;
|
|
});
|
|
|
|
// 드래그 종료 이벤트 (마우스업 또는 터치엔드)
|
|
$(document).on('mouseup touchend', function(e) {
|
|
if (isDragging) {
|
|
// 마우스 또는 터치 이벤트의 종료 위치 저장
|
|
endPos = e.pageX || e.originalEvent.changedTouches[0].pageX;
|
|
const diff = endPos - startPos;
|
|
|
|
// 드래그 거리가 충분히 클 경우에만 슬라이드 전환 (50px 이상)
|
|
if (Math.abs(diff) > 50) {
|
|
// 오른쪽으로 드래그: 이전 슬라이드, 왼쪽으로 드래그: 다음 슬라이드
|
|
currentSlide += (diff > 0) ? -1 : 1;
|
|
|
|
// 슬라이드 인덱스 범위 제한 (순환 슬라이더)
|
|
if (currentSlide < 0) {
|
|
currentSlide = slideCount - 1; // 첫 슬라이드에서 이전으로 가면 마지막으로
|
|
} else if (currentSlide >= slideCount) {
|
|
currentSlide = 0; // 마지막 슬라이드에서 다음으로 가면 처음으로
|
|
}
|
|
moveSlide();
|
|
}
|
|
isDragging = false;
|
|
}
|
|
});
|
|
|
|
// 이미지 드래그 방지 (브라우저 기본 동작 차단)
|
|
$('.reviewSlider img').on('dragstart', function(event) {
|
|
event.preventDefault();
|
|
});
|
|
|
|
// 슬라이드 클릭 시 링크 이동 처리 - 드래그와 구분하여 처리
|
|
$slides.on('click', function(e) {
|
|
// 더보기 버튼이나 그 하위 요소를 클릭한 경우 제외
|
|
if ($(e.target).closest('.reviewText-toggle').length > 0) {
|
|
return;
|
|
}
|
|
|
|
// 드래그 중이 아닐 때만 링크 이동
|
|
if (!isDragging) {
|
|
const link = $(this).data('link');
|
|
if (link) {
|
|
window.location.href = link;
|
|
}
|
|
}
|
|
});
|
|
|
|
// 초기 리뷰 버튼 설정
|
|
updateReviewButton();
|
|
|
|
// 첫 번째 슬라이드의 마일리지 보상 정보 가져오기 (페이지 로딩시)
|
|
fetchRewardInfo();
|
|
|
|
/**
|
|
* 자동 슬라이드 시작 함수
|
|
* 일정 시간마다 자동으로 다음 슬라이드로 이동
|
|
*/
|
|
function startAutoSlide() {
|
|
autoSlideTimer = setInterval(function() {
|
|
// 다음 슬라이드로 순환 이동 (마지막 슬라이드에서는 처음으로)
|
|
currentSlide = (currentSlide + 1) % slideCount;
|
|
moveSlide();
|
|
}, 5000); // 5초마다 슬라이드 자동 전환
|
|
}
|
|
|
|
/**
|
|
* 자동 슬라이드 정지 함수
|
|
*/
|
|
function stopAutoSlide() {
|
|
clearInterval(autoSlideTimer);
|
|
}
|
|
|
|
// 슬라이더에 마우스 오버/아웃 시 자동 슬라이드 제어
|
|
// .reviewSlider 대신 .revieBox에 적용
|
|
$('.revieBox').hover(
|
|
function() { stopAutoSlide(); }, // 마우스 오버 시 자동 슬라이드 정지
|
|
function() { startAutoSlide(); } // 마우스 아웃 시 자동 슬라이드 재시작
|
|
);
|
|
|
|
// 초기 자동 슬라이드 시작
|
|
startAutoSlide();
|
|
|
|
// 더보기 기능 초기화
|
|
initializeReadMore();
|
|
}
|
|
|
|
/**
|
|
* 리뷰 모달 생성 함수
|
|
* 리뷰 버튼 클릭 시 호출되어 모달 창 요소를 생성
|
|
*/
|
|
function createReviewModal() {
|
|
// 기존 모달이 있다면 제거
|
|
$('#review-modal').remove();
|
|
|
|
// 모달 HTML 구조 생성
|
|
const modalHTML = `
|
|
<div class="reviewModal" id="review-modal" style="display:none;">
|
|
<div class="reviewModal__overlay" style="position:fixed; top:0; left:0; right:0; bottom:0; background-color:rgba(0,0,0,0.6); z-index:999;">
|
|
<div class="reviewModal__container" style="position:absolute; top:50%; left:50%; transform:translate(-50%, -50%); width:90%; max-width:800px; max-height:80vh; background-color:#fff; border-radius:5px; overflow:hidden;">
|
|
<div class="reviewModal__header" style="display:flex; justify-content:space-between; align-items:center; padding:15px; border-bottom:1px solid #e0e0e0;">
|
|
<h2 class="reviewModal__title" style="margin:0; font-size:18px; font-weight:bold;">리뷰 보기</h2>
|
|
<button class="reviewModal__close" style="background:transparent; border:0; width:24px; height:24px; position:relative; cursor:pointer;">
|
|
<span style="position:absolute; width:2px; height:16px; background-color:#666; top:4px; left:11px; transform:rotate(45deg);"></span>
|
|
<span style="position:absolute; width:2px; height:16px; background-color:#666; top:4px; left:11px; transform:rotate(-45deg);"></span>
|
|
</button>
|
|
</div>
|
|
<div class="reviewModal__body" style="max-height:calc(80vh - 130px); overflow:hidden;">
|
|
<div class="reviewModal__content">
|
|
<div class="review-iframe-container" style="width:100%; height:calc(80vh - 170px); min-height:300px; overflow:hidden;">
|
|
<iframe id="reviewIframe" src="about:blank" frameborder="0" style="width:100%; height:100%; border:none;"></iframe>
|
|
</div>
|
|
<div class="review-timer" style="padding:10px 15px; text-align:center; background-color:#f8f8f8; border-top:1px solid #e0e0e0; font-weight:bold;">
|
|
남은 시간: <span id="popupCountdown">0</span>초
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="reviewModal__btnContainer" style="padding:15px; border-top:1px solid #e0e0e0; text-align:right;">
|
|
<button id="review-modal-close-btn" style="padding:8px 16px; border-radius:4px; cursor:pointer; border:none; font-weight:bold; background-color:#f1f1f1; color:#333;">닫기</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// 모달을 페이지에 추가
|
|
$('body').append(modalHTML);
|
|
|
|
// 모달 닫기 버튼에 이벤트 핸들러 직접 연결
|
|
$('#review-modal-close-btn, .reviewModal__close').on('click', function() {
|
|
closeReviewModal();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 리뷰 버튼 업데이트 함수
|
|
* 현재 슬라이드에 맞는 리뷰 정보로 리뷰 버튼의 이벤트 핸들러 업데이트
|
|
*/
|
|
function updateReviewButton() {
|
|
if (reviewData.length > currentSlideIndex) {
|
|
const currentReview = reviewData[currentSlideIndex];
|
|
|
|
// 기존 이벤트 핸들러 제거 후 새로운 핸들러 등록
|
|
$('#reviewButton').off('click').on('click', function() {
|
|
fetchReviewTime(currentReview.bnfInnb);
|
|
});
|
|
|
|
// 현재 슬라이드의 더보기 상태 리셋
|
|
const $currentSlide = $('.reviewSlider li').eq(currentSlideIndex);
|
|
const $reviewText = $currentSlide.find('.reviewText');
|
|
const $toggleBtn = $currentSlide.find('.reviewText-toggle');
|
|
|
|
if ($toggleBtn.length > 0) {
|
|
$reviewText.removeClass('expanded').addClass('collapsed');
|
|
$toggleBtn.text('더보기');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 리뷰 시간 정보 가져오기
|
|
* 서버에 리뷰 보기 필요 시간을 요청
|
|
* @param {string|number} bnfInnb - 리뷰 혜택 고유 번호
|
|
*/
|
|
function fetchReviewTime(bnfInnb) {
|
|
$.ajax({
|
|
url: '/sopmal/bnft/getReviewTime',
|
|
method: 'POST',
|
|
contentType: 'application/json;charset=UTF-8',
|
|
data: JSON.stringify({ bnfInnb: bnfInnb }),
|
|
success: function(response) {
|
|
// 서버에서 정상적으로 시간 정보를 받아왔을 경우
|
|
if (response.success && response.tmeSecnd) {
|
|
// 모달 생성 먼저
|
|
createReviewModal();
|
|
|
|
// 그 다음 리뷰 보기 시작 (ID, 링크URL, 필요 시간(초))
|
|
start(bnfInnb, reviewData[currentSlideIndex].linkUrl, response.tmeSecnd);
|
|
} else {
|
|
console.error('리뷰 시간 정보를 가져오는데 실패했습니다.');
|
|
}
|
|
},
|
|
error: function(jqXHR, textStatus, errorThrown) {
|
|
console.error('리뷰 시간 정보 요청 중 오류 발생:', textStatus, errorThrown);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 리뷰 보기 시작 함수
|
|
* 리뷰 모달을 열고 타이머 시작
|
|
* @param {string|number} bnfInnb - 리뷰 혜택 고유 번호
|
|
* @param {string} linkUrl - 리뷰 페이지 URL
|
|
* @param {number} seconds - 리뷰를 봐야 하는 시간(초)
|
|
*/
|
|
function start(bnfInnb, linkUrl, seconds) {
|
|
// 현재 eventId를 명확히 설정하여 나중에 마일리지 적립시 올바른 ID 사용
|
|
currentEventId = bnfInnb;
|
|
remainingTime = seconds;
|
|
mileageAwarded = false;
|
|
|
|
startTime = new Date().getTime();
|
|
const endTime = startTime + (seconds * 1000);
|
|
|
|
$('#reviewIframe').attr('src', linkUrl);
|
|
$('#review-modal').fadeIn(300);
|
|
|
|
startTimer();
|
|
|
|
reviewCheckInterval = setInterval(function() {
|
|
if ($('#review-modal').is(':hidden')) {
|
|
clearInterval(reviewCheckInterval);
|
|
stopTimer();
|
|
|
|
// 토스트 메시지 제거하고 서버 응답에서만 표시
|
|
if (!mileageAwarded && remainingTime <= 0) {
|
|
awardMileage();
|
|
} else if (!mileageAwarded) {
|
|
cancelMileageAward();
|
|
}
|
|
}
|
|
}, 1000);
|
|
}
|
|
|
|
/**
|
|
* 리뷰 모달 닫기 함수
|
|
*/
|
|
function closeReviewModal() {
|
|
console.log('리뷰 모달 닫기 함수 호출');
|
|
|
|
$('#review-modal').fadeOut(300, function() {
|
|
$('#reviewIframe').attr('src', 'about:blank');
|
|
});
|
|
|
|
stopTimer();
|
|
|
|
// 토스트 메시지 제거하고 서버 응답에서만 표시
|
|
if (!mileageAwarded && remainingTime <= 0) {
|
|
awardMileage();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 타이머 시작 함수
|
|
* 리뷰 보기 시간 카운트다운 시작
|
|
*/
|
|
function startTimer() {
|
|
updateTimerDisplay(); // 초기 타이머 표시 업데이트
|
|
|
|
// 기존 타이머 있으면 정지
|
|
clearInterval(timer);
|
|
|
|
// 1초마다 카운트다운 실행
|
|
timer = setInterval(function() {
|
|
remainingTime--;
|
|
updateTimerDisplay();
|
|
|
|
// 시간이 모두 경과했을 경우
|
|
if (remainingTime <= 0) {
|
|
clearInterval(timer);
|
|
completeReviewViewing(); // 리뷰 보기 완료 처리
|
|
}
|
|
}, 1000);
|
|
}
|
|
|
|
/**
|
|
* 리뷰 보기 완료 처리 함수
|
|
*/
|
|
function completeReviewViewing() {
|
|
clearInterval(timer);
|
|
|
|
// 마일리지가 아직 적립되지 않았다면 적립 (토스트 메시지 제거)
|
|
if (!mileageAwarded && remainingTime <= 0) {
|
|
awardMileage(); // 서버 응답에서만 토스트 표시하도록 함
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 마일리지 적립 함수
|
|
*/
|
|
function awardMileage() {
|
|
// 이미 적립되었다면 중복 호출 방지
|
|
if (mileageAwarded) {
|
|
console.log('마일리지 이미 적립됨 - 중복 호출 방지');
|
|
return;
|
|
}
|
|
|
|
// 적립 시작 시 플래그 설정
|
|
mileageAwarded = true;
|
|
|
|
// currentEventId (리뷰 보기 시작시 설정된 ID) 사용하여 정확한 마일리지 적립
|
|
$.ajax({
|
|
url: '/sopmal/bnft/awardConrevMileage',
|
|
method: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify({ bnfInnb: currentEventId }),
|
|
success: function(response) {
|
|
let result;
|
|
try {
|
|
result = typeof response === 'string' ? JSON.parse(response) : response;
|
|
} catch (e) {
|
|
console.error('서버 응답 파싱 오류:', e);
|
|
mileageAwarded = false;
|
|
showToast('서버 응답 처리 중 오류가 발생했습니다.');
|
|
return;
|
|
}
|
|
|
|
// 서버 응답에 따른 정확한 메시지 표시
|
|
if (result.result === true) {
|
|
const successMessage = result.message || '리뷰 보기 완료! 마일리지가 적립되었습니다!';
|
|
showToast(successMessage);
|
|
} else {
|
|
// 실패 시에도 사용자에게 알림 (이미 적립된 경우 등)
|
|
const errorMessage = result.message || '마일리지 적립에 실패했습니다.';
|
|
showToast(errorMessage);
|
|
console.log('마일리지 적립 실패 - 오류 코드:', result.errorCode);
|
|
}
|
|
},
|
|
error: function(jqXHR, textStatus, errorThrown) {
|
|
console.error('마일리지 적립 요청 중 오류 발생:', textStatus, errorThrown);
|
|
mileageAwarded = false;
|
|
showToast('마일리지 적립 요청 중 오류가 발생했습니다.');
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 마일리지 적립 취소 함수
|
|
* 사용자가 리뷰 보기를 중단한 경우
|
|
*/
|
|
function cancelMileageAward() {
|
|
console.log('마일리지 적립이 취소되었습니다.');
|
|
// 필요한 경우 서버에 마일리지 적립 취소 요청을 보낼 수 있습니다.
|
|
}
|
|
|
|
/**
|
|
* 타이머 정지 함수
|
|
* 모든 타이머 및 인터벌 중지
|
|
*/
|
|
function stopTimer() {
|
|
if (timer) {
|
|
clearInterval(timer);
|
|
}
|
|
if (reviewCheckInterval) {
|
|
clearInterval(reviewCheckInterval);
|
|
}
|
|
if (autoSlideTimer) {
|
|
clearInterval(autoSlideTimer);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 이미지 로딩 함수
|
|
* 슬라이더의 모든 이미지를 로드
|
|
*/
|
|
function loadImages() {
|
|
// JSP에서 이미 CDN URL로 이미지 src가 설정되어 있으므로
|
|
// 추가적인 이미지 로딩 처리가 불필요함
|
|
console.log('이미지는 JSP에서 CDN URL로 이미 로드됨');
|
|
|
|
// 필요한 경우에만 이미지 로드 실패 처리
|
|
$('.reviewSlider .thum img').each(function(index) {
|
|
const $img = $(this);
|
|
|
|
// 이미지 로드 실패 시에만 처리
|
|
$img.on('error', function() {
|
|
console.error(`이미지 로드 실패: ${this.src}`);
|
|
this.src = '/mobile/images/image_placeholder.png'; // 모바일용 플레이스홀더로 변경
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 앱에서 호출하는 콜백 함수
|
|
* 앱에서 리뷰 보기 완료 시 호출됨
|
|
* @param {string} url - 완료된 리뷰 URL
|
|
*/
|
|
function complete_open_blank_url(url) {
|
|
console.log('Review completed for URL:', url);
|
|
awardMileage(); // 마일리지 적립
|
|
}
|
|
|
|
/**
|
|
* 타이머 표시 업데이트 함수
|
|
* 남은 시간을 화면에 표시
|
|
*/
|
|
function updateTimerDisplay() {
|
|
// 카운트다운 타이머와 팝업 카운트다운에 남은 시간 표시
|
|
$('#countdown, #popupCountdown').text(remainingTime);
|
|
}
|
|
|
|
/**
|
|
* 더보기 기능 초기화
|
|
*/
|
|
function initializeReadMore() {
|
|
$('.reviewSlider li').each(function() {
|
|
const $reviewText = $(this).find('.reviewText');
|
|
const $slide = $(this);
|
|
|
|
// 원본 텍스트 저장
|
|
const originalText = $reviewText.html();
|
|
$reviewText.data('original-text', originalText);
|
|
|
|
// 높이 체크하여 더보기 버튼 필요 여부 판단
|
|
checkTextHeight($reviewText, $slide);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 텍스트 높이 체크 및 더보기 버튼 생성
|
|
*/
|
|
function checkTextHeight($reviewText, $slide) {
|
|
// 임시로 전체 텍스트 표시하여 높이 측정
|
|
$reviewText.removeClass('collapsed expanded');
|
|
const fullHeight = $reviewText.height();
|
|
// 3줄 높이로 변경: 3줄 * line-height(1.6) * font-size(16px) = 약 76.8px
|
|
const maxHeight = 3 * 1.6 * 16;
|
|
|
|
if (fullHeight > maxHeight) {
|
|
// 긴 텍스트인 경우 더보기 버튼 추가
|
|
$reviewText.addClass('collapsed');
|
|
|
|
// 더보기 버튼이 없으면 생성
|
|
if ($slide.find('.reviewText-toggle').length === 0) {
|
|
const $toggleBtn = $('<button class="reviewText-toggle">더보기</button>');
|
|
$reviewText.after($toggleBtn);
|
|
|
|
// 클릭 이벤트 바인딩 - 강화된 이벤트 방지
|
|
$toggleBtn.on('click touchend', function(event) {
|
|
// 모든 이벤트 전파 방지
|
|
event.stopPropagation();
|
|
event.stopImmediatePropagation();
|
|
event.preventDefault();
|
|
|
|
toggleReadMore($reviewText, $toggleBtn);
|
|
|
|
// 추가적인 이벤트 방지를 위한 return false
|
|
return false;
|
|
});
|
|
}
|
|
} else {
|
|
// 짧은 텍스트인 경우 더보기 버튼 제거
|
|
$slide.find('.reviewText-toggle').remove();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 더보기/접기 토글
|
|
*/
|
|
function toggleReadMore($reviewText, $toggleBtn) {
|
|
if ($reviewText.hasClass('collapsed')) {
|
|
// 펼치기
|
|
$reviewText.removeClass('collapsed').addClass('expanded');
|
|
$toggleBtn.text('접기');
|
|
} else {
|
|
// 접기
|
|
$reviewText.removeClass('expanded').addClass('collapsed');
|
|
$toggleBtn.text('더보기');
|
|
|
|
// 접을 때 해당 위치로 스크롤
|
|
$reviewText[0].scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'start'
|
|
});
|
|
}
|
|
}
|
|
|
|
// 모듈의 공개 인터페이스
|
|
return {
|
|
init: initializeSlider, // 슬라이더 초기화
|
|
start: start, // 리뷰 보기 시작
|
|
stop: stopTimer, // 타이머 정지
|
|
loadImages: loadImages, // 이미지 로드
|
|
showToast: showToast, // 토스트 메시지 표시 함수 추가
|
|
fetchRewardInfo: fetchRewardInfo // 마일리지 보상 정보 가져오기 함수 추가
|
|
};
|
|
})();
|
|
|
|
/**
|
|
* DOM이 로드된 후 슬라이더 초기화
|
|
* 페이지 로드 완료 시 Conrev 모듈 초기화
|
|
*/
|
|
$(document).ready(function() {
|
|
Conrev.init();
|
|
|
|
// 페이지 로드 시 기존 alert 메시지가 있다면 토스트로 변환
|
|
if (typeof alertMessage !== 'undefined' && alertMessage) {
|
|
Conrev.showToast(alertMessage);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* 창이 닫힐 때 타이머 정지
|
|
* 페이지 이탈 시 리소스 정리
|
|
*/
|
|
$(window).on('beforeunload', function() {
|
|
Conrev.stop();
|
|
}); |