1<!-- [í´ë½ë ì¤ììì¤] ìì½ 1ë¨ê³ 'ë ì§/í°ì¼/ìë' ì í --> 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16<html lang="ko"> 17<head> 18 <meta charset="utf-8"> 19 20 <meta content="width=device-width, initial-scale=1.0" name="viewport"> 21 22 <title>í´ë½ë ì¤ììì¤ </title> 23 <meta name="description" content="í´ì´ë í리미ì ì¤í & ìí°íí¬ í´ë½ë ì¤ììì¤"> 24 <meta name="keywords" content="í´ë½ëì¤ììì¤, í´ë½ë ì¤ììì¤, ë¶ì°ìí°íí¬, ë¶ì°ì¤í, í´ì´ëìí°íí¬, í´ì´ëì¤í, ìí°í´ë½"> 25 26 <!-- Open Graph --> 27 <meta property="og:title" content="í´ë½ëì¤ììì¤" /> 28 <meta property="og:description" content="í´ì´ë í리미ì ì¤í & ìí°íí¬ í´ë½ë ì¤ììì¤" /> 29 <meta property="og:image" content="https://www.clubdoasis.com/img/orimage2.jpg" /> 30 <meta property="og:url" content="https://www.clubdoasis.com/" /> 31 <meta property="og:site_name" content="í´ë½ëì¤ììì¤" /> 32 33 <!-- Twitter Card --> 34 <meta name="twitter:card" content="summary_large_image" /> 35 <meta name="twitter:title" content="í´ë½ëì¤ììì¤" /> 36 <meta name="twitter:description" content="í´ì´ë í리미ì ì¤í & ìí°íí¬ í´ë½ë ì¤ììì¤" /> 37 <meta name="twitter:image" content="https://www.clubdoasis.com/img/orimage2.jpg" /> 38 <meta name="twitter:url" content="https://www.clubdoasis.com/" /> 39 40 <!-- Favicons --> 41 <link rel="shortcut icon" href="/img/favicon.ico"> 42 <link href="/img/apple-touch-icon.png" rel="apple-touch-icon"> 43 44 <!-- ì§ì 모ë°ì¼ ë¸ë¼ì°ì Safari 15~ / ì¼ì± ì¸í°ë· 6.2~ / í¬ë¡¬ 94~ --> 45 <meta name="theme-color" content="#ffffff"> 46 47 <!--iOS Safari ì ì©--> 48 <meta name="apple-mobile-web-app-status-bar-style" content="default"> 49 50 <link rel="stylesheet" href="/css/bootstrap-icons.css"> <!-- bootstrap-icons --> 51 <link rel="stylesheet" href="/css/bootstrap.min.css"> <!-- bootstrap-css --> 52 <link rel="stylesheet" href="/css/fonts.css?now=Mon Oct 05 04:40:42 KST 2026"> 53 <link rel="stylesheet" href="/css/base.css?now=Mon Oct 05 04:40:42 KST 2026"> 54 <link rel="stylesheet" href="/css/gnb.css?now=Mon Oct 05 04:40:42 KST 2026"> 55 <link rel="stylesheet" href="/css/sub.css?now=Mon Oct 05 04:40:42 KST 2026"> 56 57 <link rel="stylesheet" href="/css/animate.css"><!--ìì§ì--> 58 <link rel="stylesheet" href="/css/owl.carousel.min.css"><!--ì¬ë¼ì´ë--> 59 <link rel="stylesheet" href="/css/member.css?now=Mon Oct 05 04:40:42 KST 2026"> 60 <link rel="stylesheet" href="/css/style.css?now=Mon Oct 05 04:40:42 KST 2026"> 61 <link rel="stylesheet" href="/css/common.css?now=Mon Oct 05 04:40:42 KST 2026"> 62 <link rel="stylesheet" href="/css/hBook.css?now=Mon Oct 05 04:40:42 KST 2026"> 63 64 <link rel="stylesheet" href="/css/aos.css"> 65 <!--ì¤í¬ë¡¤aos--> 66 <link rel="stylesheet" href="/css/swiper.css" /> 67 <!--swiper--> 68 <link rel="stylesheet" href="/vendor/jquery-ui.min.css"> 69 <link rel="stylesheet" href="/css/festival.css?now=Mon Oct 05 04:40:42 KST 2026"> 70 <link rel="stylesheet" href="/css/main.css?now=Mon Oct 05 04:40:42 KST 2026"> 71 <link rel="stylesheet" href="/css/main-popup.css?now=Mon Oct 05 04:40:42 KST 2026"> 72 <link rel="stylesheet" href="/css/secret.css?now=Mon Oct 05 04:40:42 KST 2026"> 73 74 <!--í°í¸ ìì´ì½--> 75 76 <link rel="preconnect" href="https://fonts.googleapis.com"> 77 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 78 <link 79 href="https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&display=swap" 80 rel="stylesheet"> 81 <link rel="preconnect" href="https://fonts.googleapis.com"> 82 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 83 <link 84 href="https://fonts.googleapis.com/css2?family=Gamja+Flower&display=swap" 85 rel="stylesheet"> 86
86<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
86 87 <!-- date picker CSS --> 88 <link rel="stylesheet" href="/css/flatpickr/flatpickr.css"> 89 <link rel="stylesheet" href="/css/flatpickr/confetti.css"> 90 91 <!-- Naver --> 92 <meta name="naver-site-verification" content="edd9e8eecb6c3c4fbd7ef0bb269872bf737d344a" /> 93
93<script>
vendor: 127 bytes, line 93
93window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', '
93G-QNL6YK7B8E
vendor: 4 bytes, line 93
93');
93</script>
93 94
94<script type="application/ld+json"> { "@context": "http://schema.org", "@type": "Person", "name": "CLUBD OASIS", "url": "https://clubdoasis.com/", "sameAs": [ "https://www.instagram.com/clubdoasis/", "https://www.youtube.com/@clubdoasis" ] } </script>
94 95 <!-- Google tag (gtag.js) -->
vendor: 65 bytes, lines 95-96
95 96 <script async src="https://www.googletagmanager.com/gtag/js?id=
96G-QNL6YK7B8E
vendor: 13 bytes, lines 96-97
96"></script> 97
97<!-- ë¤ì´ë² ìì¹ì´ëë°ì´ì íê·¸ --> 98 <meta name="naver-site-verification" content="3d5d33e0c622de8473586655e70b98e7a7a8dfd4" /> 99 <!-- kakao --> 100
100<script type="text/javascript" charset="UTF-8" src="//t1.daumcdn.net/kas/static/kp.js"></script>
100 101
101<script type="text/javascript"> kakaoPixel('7350261284392647385').pageView(); </script>
101 102 <!-- Meta Pixel Code --> 103
103<script>
vendor: 425 bytes, lines 103-112
103 104 !function(f,b,e,v,n,t,s) 105 {if(f.fbq)return;n=f.fbq=function(){n.callMethod? 106 n.callMethod.apply(n,arguments):n.queue.push(arguments)}; 107 if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0'; 108 n.queue=[];t=b.createElement(e);t.async=!0; 109 t.src=v;s=b.getElementsByTagName(e)[0]; 110 s.parentNode.insertBefore(t,s)}(window, document,'script', 111 'https://connect.facebook.net/en_US/fbevents.js'); 112 fbq('init', '
1121675250166477047
vendor: 33 bytes, lines 112-114
112'); 113 fbq('track', 'PageView'); 114
114</script>
114 115 <noscript><img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=1675250166477047&ev=PageView&noscript=1" /></noscript> 116 117 118 119 120 121</head> 122 123 124<body class="nonetop no-res-btn hbook-purchase-page"> 125 126<!-- ìíì¡°í ë¡ë© ì¤í¼ë --> 127<div id="v2-loading-overlay" role="status" aria-live="polite" aria-label="ìíì ì¡°í ì¤ì ëë¤."> 128 <div class="v2-loading-box"> 129 <div class="v2-loading-spinner" aria-hidden="true"></div> 130 <div class="v2-loading-text">ìíì ì¡°í ì¤ì ëë¤.</div> 131 </div> 132</div> 133 134<!-- ìë¨ ë©ë´ --> 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152<header id="fh5co-header-section" role="banner"> 153 154 155<section class="header-utility" aria-label="Quick navigation"> 156 <div class="top"> 157 <div class="top-left"> 158 <button type="button" class="txt_global" aria-haspopup="true" aria-expanded="false" aria-controls="header-language-menu"> 159 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-globe2" viewBox="0 0 16 16" aria-hidden="true"> 160 <path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8m7.5-6.923c-.67.204-1.335.82-1.887 1.855q-.215.403-.395.872c.705.157 1.472.257 2.282.287zM4.249 3.539q.214-.577.481-1.078a7 7 0 0 1 .597-.933A7 7 0 0 0 3.051 3.05q.544.277 1.198.49zM3.509 7.5c.036-1.07.188-2.087.436-3.008a9 9 0 0 1-1.565-.667A6.96 6.96 0 0 0 1.018 7.5zm1.4-2.741a12.3 12.3 0 0 0-.4 2.741H7.5V5.091c-.91-.03-1.783-.145-2.591-.332M8.5 5.09V7.5h2.99a12.3 12.3 0 0 0-.399-2.741c-.808.187-1.681.301-2.591.332zM4.51 8.5c.035.987.176 1.914.399 2.741A13.6 13.6 0 0 1 7.5 10.91V8.5zm3.99 0v2.409c.91.03 1.783.145 2.591.332.223-.827.364-1.754.4-2.741zm-3.282 3.696q.18.469.395.872c.552 1.035 1.218 1.65 1.887 1.855V11.91c-.81.03-1.577.13-2.282.287zm.11 2.276a7 7 0 0 1-.598-.933 9 9 0 0 1-.481-1.079 8.4 8.4 0 0 0-1.198.49 7 7 0 0 0 2.276 1.522zm-1.383-2.964A13.4 13.4 0 0 1 3.508 8.5h-2.49a6.96 6.96 0 0 0 1.362 3.675c.47-.258.995-.482 1.565-.667m6.728 2.964a7 7 0 0 0 2.275-1.521 8.4 8.4 0 0 0-1.197-.49 9 9 0 0 1-.481 1.078 7 7 0 0 1-.597.933M8.5 11.909v3.014c.67-.204 1.335-.82 1.887-1.855q.216-.403.395-.872A12.6 12.6 0 0 0 8.5 11.91zm3.555-.401c.57.185 1.095.409 1.565.667A6.96 6.96 0 0 0 14.982 8.5h-2.49a13.4 13.4 0 0 1-.437 3.008M14.982 7.5a6.96 6.96 0 0 0-1.362-3.675c-.47.258-.995.482-1.565.667.248.92.4 1.938.437 3.008zM11.27 2.461q.266.502.482 1.078a8.4 8.4 0 0 0 1.196-.49 7 7 0 0 0-2.275-1.52c.218.283.418.597.597.932m-.488 1.343a8 8 0 0 0-.395-.872C9.835 1.897 9.17 1.282 8.5 1.077V4.09c.81-.03 1.577-.13 2.282-.287z"/> 161 </svg> 162 íêµì´(KOR) 163 </button> 164 <ul class="ul_global text-center" id="header-language-menu"> 165 <li><a href="/?lang=ko">íêµì´</a></li> 166 <li><a href="/en?lang=en">English</a></li> 167 <li><a href="/jp?lang=ja">æ¥æ¬èª</a></li> 168 <li><a href="/cn?lang=zh_CN">ç®ä½ä¸æ</a></li> 169 170 </ul> 171 </div> 172 173 <div class="logo" role="link" tabindex="0" aria-label="CLUBD OASIS" data-home-url="/"></div> 174 175 <div class="top-right"> 176 <a class="header-ticket-link" href="/hBook/bookMainV2/19110001?lang=ko"> 177 <i class="bi bi-ticket-perforated-fill" aria-hidden="true"></i> 178 <span>TICKETS</span> 179 </a> 180 181 182 183 184 185 <a href="/join/loginView"><i class="bi bi-person-fill" aria-hidden="true"></i><span>LOGIN</span></a> 186 187 188 189 190 </div> 191 </div> 192</section>
193<script> 194 (function () { 195 var headerUtility = document.currentScript.previousElementSibling; 196 var logo = headerUtility.querySelector('.logo'); 197 var languageButton = headerUtility.querySelector('.txt_global'); 198 var languageMenu = headerUtility.querySelector('.ul_global'); 199 var header = headerUtility.closest('#fh5co-header-section'); 200 var goHome = function () { window.location.href = logo.getAttribute('data-home-url'); }; 201 var setLanguageMenuOpen = function (isOpen) { 202 languageMenu.style.display = isOpen ? 'block' : 'none'; 203 languageButton.setAttribute('aria-expanded', String(isOpen)); 204 if (header) { 205 header.classList.toggle('language-menu-open', isOpen); 206 } 207 }; 208 209 setLanguageMenuOpen(false); 210 languageButton.addEventListener('click', function () { 211 var isOpen = languageMenu.style.display !== 'none'; 212 setLanguageMenuOpen(!isOpen); 213 }); 214 document.addEventListener('click', function (event) { 215 if (!headerUtility.contains(event.target)) { 216 setLanguageMenuOpen(false); 217 } 218 }); 219 headerUtility.addEventListener('keydown', function (event) { 220 if (event.key === 'Escape') { 221 setLanguageMenuOpen(false); 222 languageButton.focus(); 223 } 224 }); 225 226 logo.addEventListener('click', goHome); 227 logo.addEventListener('keydown', function (event) { 228 if (event.key === 'Enter' || event.key === ' ') { 229 event.preventDefault(); 230 goHome(); 231 } 232 }); 233 }()); 234</script>
234 235 236 <!-- <div class="event-button-zone"> 237 <p class="mb-2">2024 HAEUNDAE WATER WARS</p> 238 <a href="/waterwars" class="mr-2 event-button">ì´ì©ê¶ ì매</a> 239 <a href="/highest" class="event-button">ííí° ì매</a> 240 </div> --> 241 242 243 <section> 244 245 <div class="border"> 246 <div class="container"> 247 <nav id="fh5co-menu-wrap" role="navigation"> 248 <div class="gnb"> 249 <ul> 250 <li><a href="/brand/brand">BRAND</a> 251 <ul class="depth2"> 252 <li><a href="/brand/brand">ë¸ëëìê°</a></li> 253 <li><a href="/brand/boyang">ë³´ìì¨ì²</a></li> 254 <li><a href="/brand/well-luv">K-OGAM(ì°ëì¤)</a></li> 255 <li><a href="/brand/pr">íë³´ìì</a></li> 256 </ul> 257 </li> 258 <li><a href="/enjoy/sand">SPA & WATERPARK</a> 259 <ul class="depth2"> 260 <li><a class="" href="/enjoy/sand">THE SAND</a></li> 261 <li><a class="" href="/enjoy/horizon">THE HORIZON</a></li> 262 <li><a class="" href="/enjoy/mirage">THE MIRAGE</a></li> 263 <li><a class="" href="/enjoy/palm">THE PALM</a></li> 264 <li><a class="" href="/enjoy/oasis">THE OASIS</a></li> 265 </ul> 266 </li> 267 <li><a href="/guide/guideV2">GUIDE</a> 268 <ul class="depth2"> 269 <li><a class="" href="/guide/guideV2">ì´ì©ìë´</a></li> 270 <li><a class="" href="/guide/charge">ì´ì©ìê¸</a></li> 271 <li><a class="" href="/guide/floor">층ë³ìë´</a></li> 272 <li><a class="" href="/guide/rental">ë íì©í </a></li> 273 <li><a class="" href="/guide/cbnEtc">ì¹´ë°ë/ë©´ì공방 </a></li> 274 <li><a class="" href="/guide/location">êµíµ/주차ìë´</a></li> 275 <li><a class="" href="/guide/faq">ì주묻ëì§ë¬¸</a></li> 276 </ul> 277 </li> 278 <li><a href="/facility/service">FACILITY</a> 279 <ul class="depth2"> 280 <li><a class="" href="/facility/service">í¸ììì¤</a></li> 281 282 283 284 <li><a class="" href="/facility/food">ìììì¤ </a></li> 285 <li><a class="" href="/facility/3dmap">3D ë§µ </a></li> 286 </ul> 287 </li> 288 <li><a href="/hBook/bookMainV2/19110001">TICKETS</a> 289 <ul class="depth2"> 290 <li><a href="/hBook/bookMainV2/19110001">í°ì¼ 구매í기</a></li> 291 <li><a href="/join/loginView?mode=ticket">í°ì¼ì¡°í</a></li> 292 <li><a href="/hBook/inquiryGroup">
292ë¨ì²´ìì½ë¬¸ì</a></li> 293 <li><a href="/hBook/inquiryPtnrsp">ëê´/ì í´ë¬¸ì</a></li> 294 <li onClick="openModal('code')" style="cursor: pointer; line-height: 40px; font-size: 15px;">ì í´íì</li> 295 </ul> 296 </li> 297 <li><a href="/customer/board?bbstype=1">NOTICE</a> 298 <ul class="depth2"> 299 <li><a class="" href="/customer/board?bbstype=1">ì´ë²¤í¸</a></li> 300 <li><a class="" href="/customer/board?bbstype=0">ê³µì§ì¬í</a></li> 301 <li><a class="" href="/customer/region">ì í´ì ì²´</a></li> 302 <li><a class="" href="/voc">ê³ ê°ìì리</a></li> 303 </ul> 304 </li> 305 </ul> 306 </div> 307 </nav> 308 </div> 309 </div> 310 <!-- Waves --> 311 <svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto"> 312 <defs> 313 <path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z"/> 314 </defs> 315 <g class="parallax"> 316 <use xlink:href="#gentle-wave" x="48" y="0" fill="rgba(0, 0, 0, 0.6" stroke-width="1" stroke="rgba(221, 221, 221, 0.6)"/> 317 </g> 318 </svg> 319 </section> 320 321 <div class="container gnb_container"> 322 <!-- START #fh5co-menu-wrap --> 323 <nav id="fh5co-menu-wrap" role="navigation"> 324 <!--모ë°ì¼ì©ìë§ ë³´ì´ë ë©ë´--> 325 <div class="mobile_gnb" style="display: none;"> 326 <div id="fh5co-primary-menu"> 327 <div class="login"> 328 <div class="mobile-account-actions"> 329 330 331 <a href="/join/loginView"><i class="bi bi-person-fill" aria-hidden="true"></i> LOGIN</a> 332 333 334 335 </div> 336 <a href="javascript:closeWin()" id="fh5co-mobile-menu-btn" class="mr-0"><svg xmlns="http://www.w3.org/2000/svg" width="21" height="21" fill="currentColor" class="bi bi-x-lg" viewBox="0 0 16 16"> 337 <path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"/> 338</svg></a> 339 </div> 340 <div class="ticket"> 341 <a href="/hBook/bookMainV2/19110001"><i class="bi bi-ticket-perforated-fill" aria-hidden="true"></i> í°ì¼êµ¬ë§¤í기</a> 342 </div> 343 <ul class="mobile sf-menu"> 344 <li class="mobile_depth"> 345 <h3>BRAND<i class="fas fa-sort-down ml10"></i></h3> 346 <ul class="mobile_depth2"> 347 <li><a href="/brand/brand">ë¸ëëìê°</a></li> 348 <li><a href="/brand/boyang">ë³´ìì¨ì²</a></li> 349 <li><a href="/brand/well-luv">K-OGAM(ì°ëì¤)</a></li> 350 <li><a href="/brand/pr">íë³´ìì</a></li> 351 </ul> 352 </li> 353 <li class="mobile_depth"> 354 <h3>SPA & WATERPARK<i class="fas fa-sort-down ml10"></i></h3> 355 <ul class="mobile_depth2"> 356 <li><a class="" href="/enjoy/sand">THE SAND</a></li> 357 <li><a class="" href="/enjoy/horizon">THE HORIZON</a></li> 358 <li><a class="" href="/enjoy/mirage">THE MIRAGE</a></li> 359 <li><a class="" href="/enjoy/palm">THE PALM</a></li> 360 <li><a class="" href="/enjoy/oasis">THE OASIS</a></li> 361 </ul> 362 </li> 363 364 <li class="mobile_depth"> 365 <h3>GUIDE<i class="fas fa-sort-down ml10"></i></h3> 366 <ul class="mobile_depth2"> 367 <li><a class="" href="/guide/guideV2">ì´ì©ìë´</a></li> 368 <li><a class="" href="/guide/charge">ì´ì©ìê¸</a></li> 369 <li><a class="" href="/guide/floor">층ë³ìë´</a></li> 370 <li><a class="" href="/guide/rental">ë íì©í </a></li> 371 <li><a class="" href="/guide/cbnEtc">ì¹´ë°ë/ë©´ì공방 </a></li> 372 <li><a class="" href="/guide/location">êµíµ/주차ìë´</a></li> 373 <li><a class="" href="/guide/faq">ì주묻ëì§ë¬¸</a></li> 374 </ul> 375 </li> 376 377 <li class="mobile_depth"> 378 <h3>FACILITY<i class="fas fa-sort-down ml10"></i></h3> 379 <ul class="mobile_depth2"> 380 <li><a class="" href="/facility/service">
380í¸ììì¤</a></li> 381 <!-- <li style="background: url(/img/event/cats/bg-event.jpg);"><a class="orange" href="/facility/catssoup"><b>í´ë½ë ì¤ììì¤ x ê³ ìì´ì ì¤í</b></a> --> 382 383 384 <li><a class="" href="/facility/food">ìììì¤ </a></li> 385 <li><a class="" href="/facility/3dmap">3D ë§µ </a></li> 386 </ul> 387 </li> 388 <li class="mobile_depth"> 389 <h3>TICKETS<i class="fas fa-sort-down ml10"></i></h3> 390 <ul class="mobile_depth2"> 391 <li><a href="/hBook/bookMainV2/19110001">í°ì¼êµ¬ë§¤í기</a></li> 392 <li><a href="/join/loginView?mode=ticket">í°ì¼ì¡°í</a></li> 393 <li><a href="/hBook/inquiryGroup">ë¨ì²´ìì½ë¬¸ì</a></li> 394 <li><a href="/hBook/inquiryPtnrsp">ëê´/ì í´ë¬¸ì</a></li> 395 <li onClick="openModal('code')" style="cursor: pointer; line-height: 3em;">ì í´íì</li> 396 </ul> 397 </li> 398 <li class="mobile_depth"> 399 <h3>NOTICE <i class="fas fa-sort-down ml10"></i></h3> 400 <ul class="mobile_depth2"> 401 <li><a class="" href="/customer/board?bbstype=1">ì´ë²¤í¸</a></li> 402 <li><a class="" href="/customer/board?bbstype=0">ê³µì§ì¬í</a></li> 403 <li><a class="" href="/customer/region">ì í´ì ì²´</a></li> 404 <li><a class="" href="/voc">ê³ ê°ìì리</a></li> 405 </ul> 406 </li> 407 408 409 410 411 412 <!-- ë¤êµì´ì í --> 413 <li class="mobile_depth mobile-language-menu mb20"> 414 <h3> 415 <div class="mobile-language-current"><i class="fas fa-globe-asia fs14"></i> íêµì´(KOR)</div> <i class="fas fa-sort-down ml10"></i> 416 </h3> 417 <ul class="mobile_depth2"> 418 <li><a href="/en?lang=en">English</a></li> 419 <li><a href="/cn?lang=zh_CN">ç®ä½ä¸æ</a></li> 420 <li><a href="/tw?lang=zh_TW">ç¹é«ä¸æ</a></li> 421 <li><a href="/jp?lang=ja">æ¥æ¬èª</a></li> 422 </ul> 423 </li> 424 <!-- //ë¤êµì´ì í --> 425 </ul> 426 </div> 427 </div> 428 <!--//gnb--> 429 430 </nav> 431 </div> 432</header> 433 434<div class="modal fade" id="code" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="codeLabel" aria-hidden="true"> 435 <div class="modal-dialog modal-dialog-centered"> 436 <div class="modal-content"> 437 <div class="modal-header"> 438 <h5 class="modal-title" id="staticBackdropLabel">ì í´íì ì½ë ì ë ¥</h5> 439 </div> 440 <div class="modal-body"> 441 <input type="text" class="form-control" id="pCode" placeholder="ì í´íì ì½ë를 ì ë ¥í´ì£¼ì¸ì."> 442 </div> 443 <div class="modal-footer"> 444 <button type="button" class="btn btn-secondary" onClick="closeModal('code')">ì·¨ì</button> 445 <button type="button" class="btn btn-primary" onClick="fnNext()">ì ë ¥</button> 446 </div> 447 </div> 448 </div> 449</div> 450
451<script type="text/javascript"> 452 function checkLogout() { 453 Swal.fire({ 454 title: 'ë¡ê·¸ìì íìê² ìµëê¹?', 455 icon: 'question', 456 showCancelButton: true, 457 confirmButtonText: 'íì¸', 458 cancelButtonText: 'ìëì¤', 459 confirmButtonColor: '#ff710d', 460 cancelButtonColor: '#d9d9d9', 461 heightAuto: false 462 }).then(function(result) { 463 if (result.isConfirmed) location.href = "/succ-logout"; 464 }); 465 } 466</script>
vendor: 2 bytes, line 466
466
467<script> 468 469 let gubun = ""; // 1: ííí° í¨í¤ì§ ìì½ íì´ì§ë¡ ì´ëí기 ìí ê° 470 471 function goBook() { 472 if (gubun == "1") location.href='/hBook/pakageBook/1911001'; // ííí° í¨í¤ì§ ìì½íì´ì§ 473 // else location.href='/hBook/bookMain/19110001'; // ì¼ë° ìì½íì´ì§ 474 else location.href='/hBook/bookMainV2/19110001'; // ì¼ë° ìì½íì´ì§ 475 } 476 477 function openModal(modalName) { 478 $('#'+modalName).addClass('menu-active show bgDarkly'); 479 $('#'+modalName).css('display', 'block'); 480 } 481 482 function closeModal(modalName) { 483 $('#'+modalName).removeClass('menu-active show bgDarkly'); 484 $('#'+modalName).css('display', 'none'); 485 } 486 487 488 //ë¤ìë¨ê³ 489 function fnNext() { 490 491 let pCode = $('#pCode').val().trim(); 492 493 if (pCode == '') { 494 hBookSwalAlert('ì í´íì ì½ë를 ì ë ¥í´ì£¼ì¸ì.').then(function() { 495 $('#pCode').focus(); 496 }); 497 return false; 498 } 499 500 $.ajax({
501 url: "/hBook/validatePromotionCode/"+pCode, 502 type: "post", 503 dataType: 'json', 504 contentType:"application/json; charset=utf-8", 505 success:function(data){ 506 507 if (data.code == '0000') { 508 var vData = data.data; 509 location.href="/hBook/bookMainV2/19110001?pCode=" + pCode + "&startDt=" + vData.StartDt + "&endDt=" + vData.EndDt; 510 } else { 511 hBookSwalAlert(data.message, { icon: 'warning' }); 512 } 513 } 514 }); 515 } 516</script>
516 517 518 519 520 521 522<div class="hBook v2-book-main"> 523 <div class="container"> 524 <div class="row"> 525 <!-- ì¼ìª½: ìí ëª©ë¡ --> 526 <div class="col-lg-8 col-12"> 527 <nav class="v2-language-switcher" id="v2ProductLanguageSwitcher" aria-label="Language selection"> 528 <span class="v2-language-switcher__label" aria-hidden="true"> 529 <i class="bi bi-globe2"></i> 530 <span>LANGUAGE</span> 531 </span> 532 <div class="v2-language-switcher__buttons" role="group" aria-label="Language selection"> 533 <button type="button" 534 class="v2-language-button is-active" 535 data-booking-lang="ko" aria-pressed="true" 536 title="íêµì´"> 537 <span class="v2-language-code">KR</span><span class="v2-language-name">íêµì´</span> 538 </button> 539 <button type="button" 540 class="v2-language-button " 541 data-booking-lang="en" aria-pressed="false" 542 title="English"> 543 <span class="v2-language-code">EN</span><span class="v2-language-name">English</span> 544 </button> 545 <button type="button" 546 class="v2-language-button " 547 data-booking-lang="ja" aria-pressed="false" 548 title="æ¥æ¬èª"> 549 <span class="v2-language-code">JP</span><span class="v2-language-name">æ¥æ¬èª</span> 550 </button> 551 <button type="button" 552 class="v2-language-button " 553 data-booking-lang="zh_CN" aria-pressed="false" 554 title="ç®ä½ä¸æ"> 555 <span class="v2-language-code">CN</span><span class="v2-language-name">ç®ä½ä¸æ</span> 556 </button> 557 <button type="button" 558 class="v2-language-button " 559 data-booking-lang="zh_TW" aria-pressed="false" 560 title="ç¹é«ä¸æ"> 561 <span class="v2-language-code">TW</span><span class="v2-language-name">ç¹é«ä¸æ</span> 562 </button> 563 </div> 564 </nav> 565 566 <ul class="nav nav-tabs" id="v2TabNav"> 567 <li class="nav-item"><a class="nav-link active" data-tab="special" href="#">í¹ê°</a></li> 568 <li class="nav-item"><a class="nav-link" data-tab="ticket" href="#">ì ì¥í°ì¼</a></li> 569 <li class="nav-item"><a class="nav-link" data-tab="facility" href="#">ë¶ëìì¤</a></li> 570 <li class="nav-item"><a class="nav-link" data-tab="member" href="#">ìì¦/ì í´</a></li> 571 </ul> 572 573 <div class="tab-content"> 574 <div class="tab-pane" id="v2TabSpecial"> 575 <div id="v2ProductListSpecial"></div> 576 </div> 577 <div class="tab-pane" id="v2TabTicket"> 578 <div id="v2ProductListTicket"></div> 579 </div> 580 <div class="tab-pane" id="v2TabFacility"> 581 <div id="v2FacilityList"></div> 582 </div> 583 <div class="tab-pane" id="v2TabMember"> 584 <div id="v2ProductListMember"></div> 585 </div> 586 </div> 587 588 <!-- hidden DOM --> 589 <input type="hidden" name="bookDt" id="bookDt" /> 590 <select id="selectTicket1"></select> 591 <select id="selectTicket2"></select> 592 <select id="selectSpNo"></select> 593 <select id="selectSpTime"></select> 594 <div id="timeTable"></div> 595 <div id="hiddenCnt2"></div> 596 </div> 597 598 <!-- ì¤ë¥¸ìª½: ì í ë´ì + ì ìì¬í --> 599 <div class="col-lg-4 col-12" id="v2SidebarCol"> 600 <div class="sticky-sidebar"> 601 <div class="card" id="v2TicketPanel"> 602 <h4 class="card-title">ì¥ë°êµ¬ë</h4> 603 <div class="card-scroll pt-2"> 604 <div class="facility-hold-status" id="v2FacilityHoldStatus" role="timer" aria-live="off"> 605 <div class="facility-hold-status__label"> 606 <i class="bi bi-clock" aria-hidden="true"></i>
607 <span>ë¶ëìì¤ ììì ì </span> 608 </div> 609 <strong class="facility-hold-status__time" id="v2FacilityHoldCountdown">20:00</strong> 610 <small>ë¨ì ìê° ìì ê²°ì 를 ìë£í´ 주ì¸ì.</small> 611 </div> 612 <div class="option_area"> 613 <div class="text-center py-5" id="v2EmptyMsg">í°ì¼ì ì íí´ì£¼ì¸ì!</div> 614 <div class="selectedTicket1"></div> 615 <div class="selectedTicket2"></div> 616 </div> 617 <div class="row totalAmtInfo"> 618 <hr class="hr_2border"> 619 <div class="col-lg-6"><p class="title-single">í©ê³ê¸ì¡</p></div> 620 <div class="col-lg-6"> 621 <nav class="breadcrumb-box d-flex justify-content-lg-end"> 622 <h4 class="totalAmt"></h4> 623 </nav> 624 </div> 625 </div> 626 <button type="button" class="btn btn-orange btn-next" onclick="fnNext()">ê²°ì í기</button> 627 </div> 628 </div> 629 630 <div class="card" id="v2NoticeWrap"> 631 <h4 class="card-title">ì ìì¬í</h4> 632 <div class="card-scroll pt-2"> 633 <ul class="description" id="v2NoticeList"></ul> 634 </div> 635 </div> 636 637 <div id="hiddenCnt"></div> 638 </div> 639 </div> 640 </div> 641 </div> 642 <!-- 모ë°ì¼ íë¨ ê³ ì ë° --> 643 <div class="mobile-bar" id="v2MobileBar"> 644 <div class="mobile-cart-summary"> 645 <button type="button" class="mobile-cart-toggle" id="v2MobileCartToggle" 646 aria-expanded="true" aria-controls="v2MobileCartContent" aria-label="ì¥ë°êµ¬ë ì 기" 647 onclick="v2ToggleMobileCart()"> 648 <i class="bi bi-chevron-down mobile-cart-chevron" aria-hidden="true"></i> 649 </button> 650 <div class="mobile-cart-total-row"> 651 <span class="mobile-cart-total-label">í©ê³ê¸ì¡</span> 652 <strong class="mobile-total mobile-cart-toggle-total">0 ì</strong> 653 </div> 654 <div class="mobile-facility-hold-status" id="v2MobileFacilityHoldStatus" role="timer" aria-live="off"> 655 <span><i class="bi bi-clock" aria-hidden="true"></i> ë¶ëìì¤ ê²°ì ë¨ì ìê°</span> 656 <strong id="v2MobileFacilityHoldCountdown">20:00</strong> 657 </div> 658 </div> 659 <div class="mobile-bar-content" id="v2MobileCartContent"> 660 <button type="button" class="btn btn-orange mobile-cart-pay-btn" onclick="fnNext()">ê²°ì í기 â</button> 661 <button type="button" class="btn mobile-cart-detail-btn" onclick="v2ScrollToPanel()"> 662 ì í ë´ì íì¸í기 â 663 </button> 664 </div> 665 </div> 666</div> 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682<section class="section-footer" id="footer"> 683 <div class="footer-link"> 684 <nav class="container footer-nav" aria-label="íë¨ ë©ë´"> 685 <a href="/hBook/inquiryGroup">
685ë¨ì²´ìì½ë¬¸ì</a> 686 <a href="/hBook/inquiryPtnrsp">ëê´/ì í´ë¬¸ì</a> 687 <a href="/customer/region">ì í´ì ì²´</a> 688 <a href="/privacy/cookie">ì´ì©ì½ê´</a> 689 <a href="/privacy/privacy2">ììì ë³´ì²ë¦¬ê¸°ê¸° ì´ìê´ë¦¬ë°©ì¹¨</a> 690 <a href="/privacy/privacy" class="is-emphasis">ê°ì¸ì ë³´ì²ë¦¬ë°©ì¹¨</a> 691 </nav> 692 </div> 693 <div class="container footer-content"> 694 <div class="footer-contact"> 695 <a class="footer-phone" href="tel:1566-8007"> 696 <span>ëíì í</span> 697 <strong>1566-8007</strong> 698 </a> 699 <p class="footer-hours"> 700 <strong>ê³ ê°ì¼í° 기본 ì´ììê°</strong> 701 ì°ì¤ë¬´í´ 10:00~18:00 · ì ì¬ìê° 11:30~13:00<br> 702 ìì¤ ì´ììê°ì ë°ë¼ ë³ëë ì ììµëë¤. 703 </p> 704 </div> 705 <dl class="footer-company-info"> 706 <div class="footer-info-item"> 707 <dt>ì¬ì ì주ìì§</dt> 708 <dd>ë¶ì°ê´ìì í´ì´ë구 ë¬ë§ì´ê¸¸ 30, 4, 5층(ì¤ë, ììí°)</dd> 709 </br> 710 <dt>íµì í매ì ì ê³ ë²í¸</dt> 711 <dd>ì 2023-ë¶ì°í´ì´ë-0834í¸</dd> 712 </div> 713 714 <div class="footer-info-item"> 715 <dt>ìí¸</dt> 716 <dd>ì´ëí´ë½ëí´ì´ë 주ìíì¬</dd> 717 </br> 718 <dt>ì¬ì ìë±ë¡ë²í¸</dt> 719 <dd>133-86-02980</dd> 720 </div> 721 <div class="footer-info-item"> 722 <dt>ëíìëª </dt> 723 <dd>민ì¹ì°</dd> 724 </div> 725 </dl> 726 <p class="footer-copyright">Copyright â YIDOCLUBDHAEUNDAE Co., Ltd. All rights reserved.</p> 727 </div> 728</section> 729 730<!-- ìì½í기 --> 731 732<a href="/hBook/bookMainV2/19110001" class="reservation" id="ftBookBtn"> 733 <img src="/img/tickte.svg"> í°ì¼êµ¬ë§¤í기 734</a> 735 736<!-- ì¸ì¤íê·¸ë¨ 737<a href="https://www.instagram.com/clubdoasis/" target="_blank;" class="insta"></a> 738 739<div id="preloader"></div> --> 740 741<!-- ì°¾ìì ë ë¨ë ëª¨ë¬ --> 742<div class="modal fade" id="catFoundModalSuccess" tabindex="-1" role="dialog" aria-hidden="true"> 743 <div class="modal-dialog modal-dialog-centered" role="document"> 744 <div class="modal-content p-5"> 745 <div class="modal-body text-center"> 746 <h5 class="mb-2">ì¶íí©ëë¤!</h5> 747 <p id="catFoundMsg" class="mb-0">ê³ ìì´ë¥¼ ì°¾ì¼ì ¨ìµëë¤.</p> 748 <div class="mt-3"> 749 <button type="button" class="btn" onclick="location.href='/facility/catssoup#pills-2'">ë´ ì¤í¬í íì¸í기</button> 750 <button type="button" class="btn" data-dismiss="modal" aria-label="Close">ê³ì 찾기</button> 751 </div> 752 </div> 753 </div> 754 </div> 755</div> 756 757<div class="modal fade" id="catFoundModalDuplicate" tabindex="-1" role="dialog" aria-hidden="true"> 758 <div class="modal-dialog modal-dialog-centered" role="document"> 759 <div class="modal-content p-5"> 760 <div class="modal-body text-center"> 761 <h5 class="mb-2">ì´ë¯¸ ì°¸ì¬íì ì´ë²¤í¸ì ëë¤.</h5> 762 <div class="mt-3"> 763 <button type="button" class="btn" onclick="location.href='/facility/catssoup#pills-2'">ë´ ì¤í¬í íì¸í기</button> 764 <button type="button" class="btn" data-dismiss="modal" aria-label="Close">ê³ì 찾기</button> 765 </div> 766 </div> 767 </div> 768 </div> 769</div>
770<script> 771 function fnCatFount (catName) { 772 $.ajax({ 773 url: "/facility/foundCat", 774 type: "POST", 775 data : JSON.stringify({ catName: catName }), 776 contentType: "application/json; charset=UTF-8", 777 dataType: "json", // 778 success: function(data) { 779 if (data.resultCode == '0000') { 780 // ìë¡ ì°¾ì 781 $('#catFoundModalSuccess').modal('show'); 782 } else if (data.resultCode == '0001') { 783 // ì´ë¯¸ ì°¾ì 784 $('#catFoundModalDuplicate').modal('show'); 785 } else { 786 // ìë¬ ì²ë¦¬ 787 console.error("ìë¬ ë°ì:", data.message); 788 } 789 }, error: function (err) { 790 console.error("ìë¬:", err); 791 } 792 }); 793 } 794</script>
794 795 796 797 798 799 800 801 802 803 804 805 806
807<script src="/js/jquery.min.js"></script>
vendor: 1 bytes, line 807
807
808<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
vendor: 1 bytes, line 808
808
809<script src="/js/bootstrap.bundle.min.js"></script>
809<!--ë¶í¸ì¤í¸ë©-->
810<script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
810 811
812<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
812 813<!-- ë§ì§ë§ì¼ë¡ íì±íëë jQueryì QR íë¬ê·¸ì¸ì ì°ê²°í´ì¼ íë¤. -->
814<script src="/js/jquery.qrcode.min.js"></script>
814 815<!-- v4ì¼ ë Popper íì -->
816<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js"></script>
vendor: 1 bytes, line 816
816
817<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"></script>
817 818 819 820
821<script src="/js/jquery.easing.min.js"></script>
821<!--ìì§ì--> 822<!--
822<script src="/js/validate.js"></script>
822<!--í¼ìë¿--> 823
824<script src="/js/owl.carousel.js"></script>
824<!--ì¬ë¼ì´ë-->
825<script src="/js/aos.js"></script>
825<!--ì¤í¬ë¡¤-->
826<script src="/js/mobilegnb.js?now=Mon Oct 05 04:40:42 KST 2026"></script>
826<!--모ë°ì¼-->
827<script src="/js/swiper.js"></script>
827 828 829<!-- ë ì§ ê´ë ¨ ë¼ì´ë¸ë¬ë¦¬ -->
830<script src="/js/moment.min.js"></script>
830 831 832<!-- date picker -->
833<script src="/js/flatpickr/flatpickr.js"></script>
vendor: 1 bytes, line 833
833
834<script src="/js/flatpickr/flatpickr.min.js"></script>
vendor: 1 bytes, line 834
834
835<script src="/js/flatpickr/ko.js"></script>
vendor: 1 bytes, line 835
835
836<script src="/js/flatpickr/ja.js"></script>
vendor: 1 bytes, line 836
836
837<script src="/js/flatpickr/zh.js"></script>
837 838 839
840<script src="https://pay.billgate.net/paygate/plugin/gx_web_client.js" ></script>
840 841
842<script src="/js/main.js?now=Mon Oct 05 04:40:42 KST 2026"></script>
vendor: 1 bytes, line 842
842
843<script src="/js/tools.js?now=Mon Oct 05 04:40:42 KST 2026"></script>
843 844
845<script src="/js/pay.js?now=Mon Oct 05 04:40:42 KST 2026"></script>
845 846 847
848<script src="/datatables/datatables.min.js"></script>
vendor: 1 bytes, line 848
848
849<script type="text/javascript" src="https://unpkg.com/tabulator-tables/dist/js/tabulator.min.js"></script>
849 850
851<script> 852 /** 853 * ì매 íë©´ ê³µíµ ë¨ì¼ íì¸ ëª¨ë¬. 854 * 855 * 기본 alert()ì ë¬ë¦¬ ë¹ëê¸°ë¡ ë«íë¯ë¡, íì¸ í ì´ëì´ë í¬ì»¤ì¤ê° íìí 856 * í¸ì¶ë¶ë ë°í Promiseì then()ìì íì ëìì ìííë¤. 857 */ 858 window.hBookSwalAlert = function(message, options) { 859 options = options || {}; 860 var documentLanguage = (document.documentElement.lang || 'ko').toLowerCase(); 861 var labels; 862 863 if (documentLanguage.indexOf('zh-hant') === 0) { 864 labels = { title: 'æç¤º', confirm: '確èª' }; 865 } else if (documentLanguage.indexOf('zh') === 0) { 866 labels = { title: 'æç¤º', confirm: '确认' }; 867 } else if (documentLanguage.indexOf('ja') === 0) { 868 labels = { title: 'ãæ¡å ', confirm: '確èª' }; 869 } else if (documentLanguage.indexOf('en') === 0) { 870 labels = { title: 'Notice', confirm: 'OK' }; 871 } else { 872 labels = { title: 'ìë´', confirm: 'íì¸' }; 873 } 874 875 return Swal.fire({ 876 icon: options.icon || 'warning', 877 title: options.title || labels.title, 878 text: message == null ? '' : String(message), 879 confirmButtonText: options.confirmButtonText || labels.confirm, 880 confirmButtonColor: options.confirmButtonColor || '#ff710d', 881 allowOutsideClick: options.allowOutsideClick === true, 882 returnFocus: options.returnFocus !== false, 883 heightAuto: false 884 }); 885 }; 886</script>
886 887
888<script> 889 AOS.init(); 890 891 const rootPath = "https://www.clubdoasis.com"; 892 893 const boardSwiper = new Swiper(".boardSwiper", { 894 pagination: { 895 el: ".swiper-pagination", 896 clickable: true, 897 }, 898 navigation: { 899 nextEl: ".swiper-button-next", 900 prevEl: ".swiper-button-prev", 901 }, 902 }); 903 904 const eventSwiper = new Swiper(".eventSwiper", { 905 pagination: { 906 el: ".swiper-pagination", 907 clickable: true, 908 }, 909 }); 910 911 const mobileSwiper = new Swiper(".mobileSwiper", { 912 autoplay: { 913 delay: 2000, 914 }, 915 loop: true, 916 pagination: { 917 el: ".swiper-pagination", 918 clickable: true, 919 }, 920 }); 921 922 const mobileSwiper2 = new Swiper(".mobileSwiper2", { 923 pagination: { 924 el: ".swiper-pagination", 925 clickable: true, 926 }, 927 navigation: { 928 nextEl: ".swiper-button-next", 929 prevEl: ".swiper-button-prev", 930 }, 931 }); 932 933 const mySwiper = new Swiper(".mySwiper", { 934 spaceBetween: 10, 935 slidesPerView: 4, 936 freeMode: true, 937 watchSlidesProgress: true, 938 }); 939 940 const mySwiper2 = new Swiper(".mySwiper2", { 941 spaceBetween: 10, 942 autoplay: { 943 delay: 5000, 944 disableOnInteraction: false, 945 }, 946 navigation: { 947 nextEl: ".swiper-button-next", 948 prevEl: ".swiper-button-prev", 949 }, 950 thumbs: { 951 swiper: mySwiper, 952 }, 953 }); 954</script>
954 955
956<script> 957 document.addEventListener('DOMContentLoaded', function () { 958 setupSlides(); 959 }); 960 961 function setupSlides() { 962 const slides = document.querySelectorAll('.flex-slide'); 963 964 slides.forEach(slide => { 965 slide.addEventListener('mouseenter', function () { 966 slides.forEach(otherSlide => { 967 const otherFlex = otherSlide.querySelector('.flex'); 968 if (otherSlide !== slide) { 969 otherFlex.style.opacity = '0'; // ëë¨¸ì§ ì¬ë¼ì´ëì ë´ì©ì ì¨ê¹ 970 } 971 }); 972 }); 973 974 slide.addEventListener('mouseleave', function () { 975 slides.forEach(otherSlide => { 976 const otherFlex = otherSlide.querySelector('.flex'); 977 otherFlex.style.opacity = '1'; // ë¤ì 모ë ì¬ë¼ì´ëì ë´ì©ì ë³´ì´ê² í¨ 978 }); 979 }); 980 }); 981 } 982</script>
982 983
984<script> 985 $('#close').on("click", function() { 986 $('#layer_pop').hide(); 987 }); 988 989 function setCookie(name, value, expirehours) { 990 var todayDate = new Date(); 991 todayDate.setHours(todayDate.getHours() + expirehours); 992 document.cookie = name + "=" + escape(value) + "; path=/; expires=" 993 + todayDate.toGMTString() + ";" 994 } 995 function closeWin() { 996 if (document.getElementById("pop_today").checked) { 997 setCookie("ncookie", "done", 24); 998 } 999 document.getElementById('layer_pop').style.display = "none"; 1000 } 1001 1002 /*ë©ì¸ììë§ gnb 1280px*/ 1003 $('.gnb_container').addClass('main_container_1280') 1004 $(".main").addClass("on"); //sub header ì´ë¯¸ì§ scale ë³ê²½. í´ëì¤onì´ ì¶ê°ëë©´ ì´ë¯¸ì§ê° ì¶ì. 1005</script>
1005 1006 1007<!--GNB -->
1008<script> 1009 // íì´ì§ ë´ë¶ ìë¸ë©ë´ ë° ì½í ì¸ hover í¨ê³¼ 1010 $(document).ready(function () { 1011 1012 //ìë¸ë©ë´gnb 1013 $('.submenu_open > div > ul > li > div ul').hide(); //í°ë°í bgì gnbí목ì hide íë¤. 1014 $('.submenu_1').mouseenter(function () { 1015 $('.submenu_1 > div ul').stop().slideDown(450); 1016 }); 1017 $('.submenu_1').mouseleave(function () { 1018 $('.submenu_1 > div ul').stop().slideUp(450); 1019 }); 1020 $('.submenu_2').mouseenter(function () { 1021 $('.submenu_2 > div ul').stop().slideDown(450); 1022 }); 1023 $('.submenu_2').mouseleave(function () { 1024 $('.submenu_2 > div ul').stop().slideUp(450); 1025 }); 1026 1027 1028 /*news ë°ì¤ hover ì¡ì */ 1029 $(".main_box_news").each(function () { 1030 $(this).mouseenter(function () { 1031 $(this).addClass("on"); 1032 $(this).siblings().removeClass("on"); //ê°ê°ì ììììë oní´ëì¤ ìì 1033 }); 1034 $(this).mouseleave(function () { 1035 $(this).removeClass("on"); // ë§ì°ì¤ë¥¼ leaveíë©´ oní´ëì¤ ìì 1036 }); 1037 }); 1038 1039 }); 1040 1041 //í°ì¼ì¡°í 1042 $('.month_pick button').click(function(){ 1043 $(this).addClass('on'); 1044 $(this).siblings().removeClass('on'); 1045 }); 1046 1047</script>
1047 1048 1049
1050<script> 1051 1052 const bookingI18n = { 1053 confirm: 'íì¸', 1054 cancel: 'ì·¨ì', 1055 login: 'ë¡ê·¸ì¸', 1056 join: 'íìê°ì ', 1057 loading: 'ìíì ì¡°í ì¤ì ëë¤.', 1058 cartCollapse: 'ì¥ë°êµ¬ë ì 기', 1059 cartExpand: 'ì¥ë°êµ¬ë í¼ì¹ê¸°', 1060 loginRequiredTitle: 'ë¡ê·¸ì¸ í ìíì íì¸í ì ììµëë¤', 1061 loginRequiredDescription: 'ë¡ê·¸ì¸íê³ íìê°ë¥¼ íì¸í´ ë³´ì¸ì. íì ëì ë¤ìí íë¡ëª¨ì ì ì ê³µí©ëë¤.', 1062 partnerCodeTitle: 'ì í´í ì¸ ì½ë ì ë ¥', 1063 partnerCodePlaceholder: 'ì í´í ì¸ ì½ë를 ì ë ¥í´ ì£¼ì¸ì.', 1064 apply: 'ì ì©', 1065 partnerCodeChangeTitle: 'ì í´í ì¸ ì½ë ë³ê²½', 1066 partnerCodeChangeDescription: '기존 ì í ìíì´ ì´ê¸°íë©ëë¤.', 1067 continueText: 'ê³ì', 1068 partnerCodeChecking: 'ì í´í ì¸ ì½ë를 íì¸ ì¤ì ëë¤.', 1069 partnerCodeInvalid: 'ì í¨íì§ ìì ì½ëì ëë¤.', 1070 partnerCodeError: 'ì í´í ì¸ ì½ë íì¸ ì¤ ì¤ë¥ê° ë°ìíìµëë¤.', 1071 codeCheck: 'ì½ë íì¸', 1072 openSoon: 'ì¤íìì ', 1073 onSale: 'í매ì¤', 1074 saleEnded: 'íë§¤ì¢ ë£', 1075 emptySpecial: 'ì§í ì¤ì¸ í¹ê°ê° ììµëë¤.', 1076 groupReserved: 'ë ì§ì§ì í°ì¼', 1077 groupPeriod: 'ë ì§ë¯¸ì§ì í°ì¼', 1078 groupOther: '기í', 1079 emptyTicket: 'í매 ê°ë¥í í°ì¼ì´ ììµëë¤.', 1080 partnerProducts: 'ì í´ìí', 1081 partnerCodeChange: 'ì½ë ë³ê²½', 1082 partnerCodeEnter: 'ì í´ì½ë를 ì ë ¥í´ ì£¼ì¸ì', 1083 partnerCodeDescription: 'ì í´ìíì ë¡ê·¸ì¸ ìì´ ì í´ì½ëë§ ì ë ¥íë©´ íì¸íê³ êµ¬ë§¤í ì ììµëë¤.', 1084 partnerCodeButton: 'ì í´ì½ë ì ë ¥', 1085 partnerCodeApplied: 'ì ì©ë ì í´ì½ë', 1086 partnerLoadFailed: 'ì í´ìíì ë¶ë¬ì¤ì§ 못íìµëë¤', 1087 retrySupport: 'ì ì í ë¤ì ìëí´ ì£¼ì¸ì. 문ì ê° ê³ìëë©´ ê³ ê°ì¼í°ë¡ 문ìí´ ì£¼ì¸ì.', 1088 partnerEmpty: 'í´ë¹ëë ì í´ìíì´ ììµëë¤', 1089 partnerEmptyDescription: 'ì ë ¥í ì½ëë¡ íì¬ í매 ì¤ì¸ ìíì´ ììµëë¤. ì í´ì½ë를 ë¤ì íì¸í´ 주ì¸ì.', 1090 membershipProducts: 'ì°íìê¶\/ìì¦ê¶', 1091 membershipLoginTitle: 'ë¡ê·¸ì¸íê³ íìê°ë¥¼ íì¸íì¸ì', 1092 membershipLoginDescription: 'íì ëì ë¤ìí íë¡ëª¨ì ì ì ê³µí©ëë¤. ìí ì í ì ë¡ê·¸ì¸í ì ììµëë¤.', 1093 membershipEmpty: 'íì¬ ìíì ì¤ë¹ì¤ì ëë¤.', 1094 membershipEmptyDescription: 'ìë¡ì´ ì°íìê¶\/ìì¦ê¶ ìíì ì¤ë¹íê³ ììµëë¤.', 1095 productLoadFailed: 'ìí 목ë¡ì ë¶ë¬ì¤ì§ 못íìµëë¤.', 1096 salePrice: 'í매ê°', 1097 priceUnavailable: 'ê°ê²© ì¡°í ë¶ê°', 1098 dateUnavailable: 'ì íì¼ ìì½ ë¶ê°', 1099 detailFailed: 'ìì¸ ì 보를 ë¶ë¬ì¬ ì ììµëë¤.', 1100 more: 'ë보기 â¼', 1101 close: 'ì 기 â²', 1102 detailImage: 'ìì¸ì´ë¯¸ì§', 1103 dateSelect: 'ë ì§ ì í', 1104 datePlaceholder: 'ë ì§ë¥¼ ì ííì¸ì', 1105 seatSelect: 'ì리 ì í', 1106 seatPlaceholder: 'ì리를 ì íí´ì£¼ì¸ì', 1107 reservationDateSelect: 'ìì½ì¼ ì í', 1108 dateNoProduct: 'ì íí ë ì§ì ìì½ ê°ë¥í ìíì´ ììµëë¤.', 1109 productEmpty: 'ì í ê°ë¥í ìíì´ ììµëë¤.', 1110 dateGuide: 'ìì½ì¼ì ì ííë©´ ì´ì© ê°ë¥í ìíê³¼ ê°ê²©ì´ íìë©ëë¤.', 1111 todayUnavailable: 'ë¹ì¼ ìì½ ë¶ê°', 1112 priceFailed: 'ê°ê²© ì¡°í ì¤í¨', 1113 loadError: 'ìí ì¡°í ì¤ ì¤ë¥ê° ë°ìíìµëë¤.', 1114 seatEmpty: '구매 ê°ë¥í ìë¦¬ê° ììµëë¤.', 1115 error: 'ì¤ë¥ê° ë°ìíìµëë¤.', 1116 timeLoading: 'ìê°ë ì¡°í ì¤...', 1117 timeEmpty: 'ìì½ ê°ë¥í ìê°ì´ ììµëë¤.', 1118 timePlaceholder: 'ìê°ì ì íí´ì£¼ì¸ì', 1119 timeAvailable: 'ìì½ê°ë¥',
1120 timeOnsite: 'íì¥í매기ê°', 1121 timeClosed: 'ë§ê°', 1122 timeSelect: 'ìê° ì í', 1123 timeError: 'ìê° ì¡°í ì¤ ì¤ë¥ê° ë°ìíìµëë¤.', 1124 selectionRequired: 'ì í íì', 1125 dateFirst: 'ìì½ì¼ì 먼ì ì íí´ì£¼ì¸ì.', 1126 quantityFirst: 'ìëì 1 ì´ì ì íí´ì£¼ì¸ì.', 1127 holdExpiredTitle: 'ì ì ìê° ë§ë£', 1128 holdConfirmTitle: 'ë¶ëìì¤ì ììë¡ íë³´íì´ì', 1129 dateCheckTitle: 'ë ì§ íì¸', 1130 dateCheckDescription: 'ëì¼ ë ì§ì ìíë§ í¨ê» ìì½í ì ììµëë¤. 기존 ì í ë´ìì ë¹ì°ê³ ë¤ì ì íí´ ì£¼ì¸ì.', 1131 cartUnavailable: 'ë´ê¸° ë¶ê°', 1132 cartMixInvalid: 'ë ì§ì§ì ê¶ê³¼ ë ì§ë¯¸ì§ì \/ì´ë²¤í¸\/ìì¦ê¶ì í¨ê» ê²°ì í ì ììµëë¤. ë¶ëìì¤ì ë ì§ì§ì ê¶ê³¼ í¨ê» ê²°ì í ì ììµëë¤.', 1133 info: 'ì림', 1134 notice: 'ìë´', 1135 cartRestoreTitle: 'ì¥ë°êµ¬ë ë³µì ìë´', 1136 cartRestoreExpired: 'ì ì ìê°ì´ ë§ë£ëìê±°ë íì¸í ì ìë ë¶ëìì¤ì ì¥ë°êµ¬ëìì ì ì¸íìµëë¤.', 1137 cartRestoreUnavailable: 'ë¶ëìì¤ ì ì ìí를 íì¸íì§ ëª»í´ ë³µììì ì ì¸íìµëë¤. ì ì í ìë¡ê³ ì¹¨í´ ë¤ì íì¸í´ 주ì¸ì.', 1138 cartRestoreLanguageUnavailable: 'ì¸ì´ ë³ê²½ í íì¬ ìíì 보를 íì¸í ì ìë í목ì ì¥ë°êµ¬ëìì ì ì¸íìµëë¤.', 1139 facilityHoldFailed: 'ë¶ëìì¤ì ì ì íì§ ëª»íìµëë¤.', 1140 facilityHoldError: 'ë¶ëìì¤ ì ì ì¤ ì¤ë¥ê° ë°ìíìµëë¤. ë¤ì ìëí´ ì£¼ì¸ì.', 1141 facilityReleaseFailed: 'ë¶ëìì¤ ì ì ì í´ì íì§ ëª»íìµëë¤.', 1142 facilityReleaseError: 'ë¶ëìì¤ ì ì í´ì ì¤ ì¤ë¥ê° ë°ìíìµëë¤. ë¤ì ìëí´ ì£¼ì¸ì.', 1143 facilityHoldExpiredDescription: 'ë¶ëìì¤ ììì ì {0}ë¶ì´ ì§ë ì¥ë°êµ¬ëìì í´ì ëììµëë¤. ë¤ì ì íí´ ì£¼ì¸ì.', 1144 holdConfirmDescription: 'ì íí ë¶ëìì¤ì {0}ë¶ ëì ììì ì ë©ëë¤. ì¥ë°êµ¬ëì íìë ë¨ì ìê° ìì ê²°ì 를 ìë£í´ 주ì¸ì.', 1145 facilityTimeChangeError: 'ìê° ë³ê²½ ì¤ë¥', 1146 processing: 'ì²ë¦¬ ì¤ì ëë¤. ì ì í ë¤ì ìëí´ ì£¼ì¸ì.', 1147 reservationProcessing: 'ìì½ ì 보를 ì²ë¦¬ ì¤ì ëë¤.', 1148 reservationError: 'ìì½ ì²ë¦¬ ì¤ ì¤ë¥ê° ë°ìíìµëë¤.', 1149 paymentNone: 'ê²°ì í ê¸ì¡ì´ ììµëë¤. ìíì ì íí´ ì£¼ì¸ì.', 1150 ticketInvalid: 'ì´ì©ê¶ ì í ì ë³´ê° ì¬ë°ë¥´ì§ ììµëë¤.', 1151 facilityInvalid: 'ë¶ëìì¤ ì í ì ë³´ê° ì¬ë°ë¥´ì§ ììµëë¤.', 1152 facilityTimeInvalid: 'ë¶ëìì¤ ìì½ ìê° ì í ì 보를 íì¸í´ 주ì¸ì.', 1153 discountSuffix: 'í ì¸', 1154 operationNotice: 'ì´ì ìë´', 1155 dateUnavailableSelected: 'ì ííì ë ì§ë ì´ì©ì´ ë¶ê°í©ëë¤.' 1156 , noticeCapacity: 'ë°©ë¬¸ê³ ê°ì´ ë§ì ê²½ì° ì¾ì í ì´ì©ì ìí ì ì¥ì´ ì íë ì ììµëë¤.' 1157 , noticeDateRefund: 'ì¬ì©ì¼ì ë¹ì¼ ëë ë ì§ê° ì§ë í°ì¼ì ëí´ìë ì·¨ì ë° íë¶\/êµíì´ ë¶ê°í©ëë¤.' 1158 , noticeAdvance: 'ë ì§ì§ì ê¶ í°ì¼ ë° ë¶ëìì¤ ë¹ì¼ ìì½ì ë¶ê°í©ëë¤. ìµì 1ì¼ ì ìì½ ë¶íë립ëë¤.' 1159 , noticeUsedNoRefund: 'ì¬ì©í í°ì¼ ë° ë¶ëìì¤ì íë¶ì´ ë¶ê°ë¥í©ëë¤.' 1160 , noticeUnusedRefund: 'ì í¨ê¸°ê° 경과 ì 미ì¬ì©í°ì¼ì 100% íë¶ ê°ë¥í©ëë¤.' 1161 , noticeAutoRefund: 'ì í¨ê¸°ê° 경과 í ìì ì¼ ê¸°ì¤ 2-3ì¼ ë´ ë¯¸ì¬ì© í°ì¼ 100% ìë íë¶ ì²ë¦¬ ìì , ë¤ë§ ì¤ë¥ë¡ ì¸í´ ë³ë ì²ë¦¬ ëì§ ìë ê²½ì°ê° ìì ì ìì´ í¹ ì·¨ìê° ëì§ ìì ì ê³ ê°ì¼í°ë¡ ì°ë½ ë¶íë립ëë¤.' 1162 , noticeNoChange: 'í°ì¼ ìì í ê¶ì¢ ë° ìë ë³ê²½ì ë¶ê°íë©°, ì·¨ì í ì¬êµ¬ë§¤í´ì¼ í©ëë¤.' 1163 , noticeUsedTicketNoRefund: 'ì¬ì©í í°ì¼ì íë¶ì´ ë¶ê°ë¥í©ëë¤.' 1164 }; 1165 const bookingLanguage = 'ko'; 1166 const bookingLanguageParam = 'ko'; 1167 const v2SupportedLanguageParams = ['ko', 'en', 'ja', 'zh_CN', 'zh_TW']; 1168 const membershipProductsEnabled = true; 1169 const bookingCalendarLocale = bookingLanguage === 'ja' ? 'ja' 1170 : (bookingLanguage === 'zh' ? 'zh' : (bookingLanguage === 'en' ? 'en' : 'ko')); 1171 1172 /* ================================================================ 1173 ì ì ë³ì 1174 ================================================================ */ 1175 let prevBookDt = ""; 1176 let agentCode = "19110001"; 1177 let backPageYn = ""; 1178 let mbId = ""; 1179 let mbNo = ""; 1180 let pCode = ""; 1181 let pStartDt = ""; 1182 let pEndDt = ""; 1183 let invalidPCodeYn = "N"; 1184 1185 function v2AppendQueryParam(url, name, value) { 1186 if (!value) return url; 1187 return url + (url.indexOf('?') >= 0 ? '&' : '?') 1188 + encodeURIComponent(name) + '=' + encodeURIComponent(value); 1189 } 1190 1191 function v2AppendLanguage(url) { 1192 return v2AppendQueryParam(url, 'lang', bookingLanguageParam); 1193 } 1194 1195 function v2AppendPartnerCode(url) { 1196 return v2AppendQueryParam(v2AppendLanguage(url), 'pCode', pCode); 1197 } 1198 1199 function v2EscapeHtml(value) { 1200 return $('<div>').text(value == null ? '' : String(value)).html(); 1201 } 1202 1203 function v2BuildBookMainUrl(extraParams) { 1204 const extra = extraParams || {}; 1205 const params = []; 1206 const requestedLanguage = Object.prototype.hasOwnProperty.call(extra, 'language') 1207 ? v2NormalizeBookingLanguage(extra.language) : bookingLanguageParam; 1208 const code = Object.prototype.hasOwnProperty.call(extra, 'pCode') ? extra.pCode : pCode; 1209 const startDt = Object.prototype.hasOwnProperty.call(extra, 'startDt') ? extra.startDt : pStartDt; 1210 const endDt = Object.prototype.hasOwnProperty.call(extra, 'endDt') ? extra.endDt : pEndDt; 1211 1212 if (requestedLanguage) params.push('lang=' + encodeURIComponent(requestedLanguage)); 1213 if (code) params.push('pCode=' + encodeURIComponent(code)); 1214 if (startDt) params.push('startDt=' + encodeURIComponent(startDt)); 1215 if (endDt) params.push('endDt=' + encodeURIComponent(endDt));
1216 if (extra.serialNo) params.push('serialNo=' + encodeURIComponent(extra.serialNo)); 1217 1218 return '/hBook/bookMainV2/' + agentCode + (params.length ? '?' + params.join('&') : ''); 1219 } 1220 1221 function v2NormalizeBookingLanguage(language) { 1222 const normalized = String(language || '').trim(); 1223 return v2SupportedLanguageParams.indexOf(normalized) >= 0 ? normalized : ''; 1224 } 1225 1226 function v2GetLanguageFromLink(link) { 1227 if (!link) return ''; 1228 try { 1229 return v2NormalizeBookingLanguage(new URL(link.href, location.origin).searchParams.get('lang')); 1230 } catch (e) { 1231 const matched = String(link.getAttribute('href') || '').match(/[?&]lang=([^&#]+)/); 1232 return matched ? v2NormalizeBookingLanguage(decodeURIComponent(matched[1])) : ''; 1233 } 1234 } 1235 1236 function v2GetActiveTab() { 1237 return String($('#v2TabNav .nav-link.active').data('tab') || v2GetRequestedTab() || ''); 1238 } 1239 1240 function v2BuildBookingLanguageUrl(language) { 1241 const serialNo = v2GetQuerySerialNo() || v2CheckoutSerialNo || ''; 1242 const activeTab = v2GetActiveTab(); 1243 const targetUrl = v2BuildBookMainUrl({ language: language, serialNo: serialNo }); 1244 return targetUrl + (activeTab ? '#' + encodeURIComponent(activeTab) : ''); 1245 } 1246 1247 const v2HeaderLanguageLinkSelector = [ 1248 '#header-language-menu a', 1249 '#fh5co-mobile-menu .mobile-language-menu a', 1250 '#fh5co-logo-mobile-wrap .mobile-language-selector .dropdown-item' 1251 ].join(', '); 1252 1253 function v2SyncHeaderLanguageLinks() { 1254 $(v2HeaderLanguageLinkSelector).each(function() { 1255 const language = v2GetLanguageFromLink(this); 1256 if (language) this.setAttribute('href', v2BuildBookingLanguageUrl(language)); 1257 }); 1258 } 1259 1260 function v2ChangeBookingLanguage(language) { 1261 const normalizedLanguage = v2NormalizeBookingLanguage(language); 1262 if (!normalizedLanguage) return; 1263 1264 if (normalizedLanguage === bookingLanguageParam) { 1265 v2SyncHeaderLanguageLinks(); 1266 return; 1267 } 1268 1269 v2PersistCartState(); 1270 location.href = v2BuildBookingLanguageUrl(normalizedLanguage); 1271 } 1272 1273 function v2GetPartnerLookupDate() { 1274 const today = getToDay('-'); 1275 if (pStartDt && today < pStartDt) return pStartDt; 1276 if (pEndDt && today > pEndDt) return pEndDt; 1277 return today; 1278 } 1279 1280 function v2BuildDatePickerOptions(options, partnerYn) { 1281 const today = getToDay('-'); 1282 const usePartnerPeriod = partnerYn === true; 1283 const minDate = usePartnerPeriod && pStartDt && pStartDt > today ? pStartDt : 'today'; 1284 return $.extend({ 1285 locale: bookingCalendarLocale, 1286 dateFormat: 'Y-m-d', 1287 minDate: minDate, 1288 maxDate: usePartnerPeriod ? (pEndDt || null) : null, 1289 disableMobile: true 1290 }, options || {}); 1291 } 1292 1293 let selectedDate = ""; // íì¬ ì íë ìì½ì¼ì 1294 let selectedSpNo = ""; // íì¬ ì íë ìíë²í¸ 1295 let selectedPlaceNo = ""; // íì¬ ì íë ì리ë²í¸ 1296 let selectedCavanaType = ""; // ì¹´ë°ë íì (C: ì¹´ë°ë, V: ë©´ì공방) 1297 1298 // V1 í¸íì© ì ì ë³ì (v2SyncToV1 ìì ë®ì´ì) 1299 let bookList = []; 1300 let cBookList = []; 1301 let totalAmt = 0; 1302 let oriTotAmt = 0; 1303 1304 // V2 ì ì© ì ì ë³ì 1305 let v2BookData = {}; 1306 let v2BookList = []; 1307 let v2CBookList = []; 1308 let v2TotalAmt = 0; 1309 let v2OriTotAmt = 0; 1310 let v2SelectedCavanaAmt = 0; 1311 let v2SelectedCavanaTotAmt = 0; 1312 let v2SelectedTimeList = ''; 1313 let v2BookIdx = 0; 1314 let v2NextInProgress = false; 1315 let v2CheckoutBuyerNo = ''; 1316 let v2CheckoutSerialNo = ''; 1317 let v2FacilityHoldClockOffsetMs = 0; 1318 let v2FacilityHoldTimerId = null; 1319 let v2FacilityHoldNoticeShown = false; 1320 let v2FacilityHoldExpiryHandling = false; 1321 let v2FacilityHoldRequestCount = 0; 1322 const V2_CART_STORAGE_PREFIX = 'oasis:v2Cart:'; 1323 const V2_CART_STORAGE_VERSION = 1; 1324 const V2_CART_STORAGE_MAX_AGE_MS = 2 * 60 * 60 * 1000; 1325 let v2CartRestoreState = null; 1326 let v2CartRestorePending = false;
1327 let v2CartRestoreInProgress = false; 1328 1329 // ì ìì¬í: íì¬ ì´ì´ë³¸ ìí ê¸°ì¤ ì í (ì¥ë°êµ¬ë ë¹ì´ìì ë ì¬ì©) 1330 let v2ActiveNoticeType = ''; 1331 1332 // ë¶ëìì¤ ë°ì´í° ìºì 1333 let v2FacilityDataMap = {}; 1334 1335 // v2 모ë íëê·¸ (기존 본문 ë¡ì§ ì°íì©) 1336 window.__V2_MODE__ = true; 1337 1338 function v2GoLoginWithReturnUrl() { 1339 var returnUrl = location.pathname + location.search + '#member'; 1340 location.href = '/join/loginView?returnUrl=' + encodeURIComponent(returnUrl); 1341 } 1342 1343 function v2GoJoinWithReturnUrl() { 1344 var returnUrl = location.pathname + location.search + '#member'; 1345 location.href = '/join/join02?returnUrl=' + encodeURIComponent(returnUrl); 1346 } 1347 1348 function v2PromptMemberLogin() { 1349 Swal.fire({ 1350 icon: 'info', 1351 title: bookingI18n.loginRequiredTitle, 1352 html: bookingI18n.loginRequiredDescription, 1353 showCancelButton: true, 1354 showDenyButton: true, 1355 confirmButtonText: bookingI18n.login, 1356 denyButtonText: bookingI18n.join, 1357 cancelButtonText: bookingI18n.cancel, 1358 confirmButtonColor: '#ff710d', 1359 denyButtonColor: '#6c757d', 1360 heightAuto: false 1361 }).then(function(result) { 1362 if (result.isConfirmed) v2GoLoginWithReturnUrl(); 1363 else if (result.isDenied) v2GoJoinWithReturnUrl(); 1364 }); 1365 } 1366 1367 function v2GetRequestedTab() { 1368 var tab = String(location.hash || '').replace(/^#/, ''); 1369 return ['special', 'ticket', 'facility', 'member'].indexOf(tab) >= 0 ? tab : ''; 1370 } 1371 1372 function v2PromptPartnerCode() { 1373 Swal.fire({ 1374 icon: 'info', 1375 title: bookingI18n.partnerCodeTitle, 1376 input: 'text', 1377 inputValue: pCode || '', 1378 inputPlaceholder: bookingI18n.partnerCodePlaceholder, 1379 showCancelButton: true, 1380 confirmButtonText: bookingI18n.apply, 1381 cancelButtonText: bookingI18n.cancel, 1382 confirmButtonColor: '#ff710d', 1383 heightAuto: false, 1384 inputValidator: function(value) { 1385 if (!value || !String(value).trim()) return bookingI18n.partnerCodePlaceholder; 1386 } 1387 }).then(function(result) { 1388 if (!result.isConfirmed) return; 1389 1390 const nextCode = String(result.value || '').trim(); 1391 const needsResetNotice = (pCode && nextCode !== pCode) || v2BookList.length > 0 || v2CBookList.length > 0; 1392 const confirmReset = needsResetNotice 1393 ? Swal.fire({ 1394 icon: 'warning', 1395 title: bookingI18n.partnerCodeChangeTitle, 1396 text: bookingI18n.partnerCodeChangeDescription, 1397 showCancelButton: true, 1398 confirmButtonText: bookingI18n.continueText, 1399 cancelButtonText: bookingI18n.cancel, 1400 confirmButtonColor: '#ff710d', 1401 heightAuto: false 1402 }) 1403 : Promise.resolve({ isConfirmed: true }); 1404 1405 confirmReset.then(function(confirmResult) { 1406 if (!confirmResult.isConfirmed) return; 1407 v2ValidateAndApplyPartnerCode(nextCode); 1408 }); 1409 }); 1410 } 1411 1412 function v2ValidateAndApplyPartnerCode(nextCode) { 1413 $.ajax({ 1414 url: '/hBook/validatePromotionCode/' + encodeURIComponent(nextCode), 1415 type: 'post', 1416 dataType: 'json', 1417 contentType: 'application/json; charset=utf-8', 1418 beforeSend: function() { v2ShowLoading(bookingI18n.partnerCodeChecking); }, 1419 complete: v2HideLoading, 1420 success: function(data) { 1421 if (!data || data.code !== '0000' || !data.data) { 1422 v2SwalError(data && data.message ? data.message : bookingI18n.partnerCodeInvalid, bookingI18n.codeCheck); 1423 return; 1424 } 1425 1426 const startDt = data.data.StartDt || data.data.startDt || ''; 1427 const endDt = data.data.EndDt || data.data.endDt || ''; 1428 v2ReleaseAllFacilityHolds(function() { 1429 location.href = v2BuildBookMainUrl({ pCode: nextCo
1429de, startDt: startDt, endDt: endDt }); 1430 }); 1431 }, 1432 error: function() { 1433 v2SwalError(bookingI18n.partnerCodeError, bookingI18n.codeCheck); 1434 } 1435 }); 1436 } 1437 1438 /* ë¡ê·¸ì¸ ì²´í¬ ë¹íì±í â ë¹ë¡ê·¸ì¸ë ì¥ë°êµ¬ë ë´ê¸° ê°ë¥ */ 1439 function v2IsMemberOnlyProduct(item) { 1440 if (!item) return false; 1441 return String(item.purchaseContext || 'regular') !== 'partner' 1442 && String(item.saleType || '').toUpperCase() === 'S'; 1443 } 1444 1445 function v2HasMemberOnlyProduct() { 1446 return v2BookList.some(v2IsMemberOnlyProduct) || v2CBookList.some(v2IsMemberOnlyProduct); 1447 } 1448 1449 function v2CreateBuyerNo() { 1450 if (mbNo && String(mbNo).trim() !== '') return String(mbNo).trim(); 1451 if (window.crypto && typeof window.crypto.getRandomValues === 'function') { 1452 const values = new Uint32Array(10); 1453 window.crypto.getRandomValues(values); 1454 let generated = String((values[0] % 9) + 1); 1455 for (let i = 1; i < values.length; i++) generated += String(values[i] % 10); 1456 return generated; 1457 } 1458 return String(Date.now()).slice(-8) + String(Math.floor(Math.random() * 90) + 10); 1459 } 1460 1461 function v2EnsureCheckoutIdentity() { 1462 if (!v2CheckoutBuyerNo) v2CheckoutBuyerNo = v2CreateBuyerNo(); 1463 if (!v2CheckoutSerialNo) { 1464 v2CheckoutSerialNo = getToDay('') + getNowTime('') + v2CheckoutBuyerNo; 1465 history.replaceState('', 'bookMain', v2BuildBookMainUrl({ serialNo: v2CheckoutSerialNo })); 1466 } 1467 return { buyerNo: v2CheckoutBuyerNo, serialNo: v2CheckoutSerialNo }; 1468 } 1469 1470 function v2GetQuerySerialNo() { 1471 try { 1472 return new URLSearchParams(location.search).get('serialNo') || ''; 1473 } catch (e) { 1474 const matched = String(location.search || '').match(/[?&]serialNo=([^&]+)/); 1475 return matched ? decodeURIComponent(matched[1]) : ''; 1476 } 1477 } 1478 1479 function v2CartStorageKey(serialNo) { 1480 return V2_CART_STORAGE_PREFIX + agentCode + ':' + String(serialNo || ''); 1481 } 1482 1483 function v2RemoveCartStorage(serialNo) { 1484 if (!serialNo) return; 1485 try { sessionStorage.removeItem(v2CartStorageKey(serialNo)); } catch (e) {} 1486 } 1487 1488 function v2ReadCartRestoreState(serialNo) { 1489 if (!serialNo) return null; 1490 try { 1491 const raw = sessionStorage.getItem(v2CartStorageKey(serialNo)); 1492 if (!raw) return null; 1493 const state = JSON.parse(raw); 1494 const invalid = !state 1495 || Number(state.version) !== V2_CART_STORAGE_VERSION 1496 || String(state.agentCode || '') !== String(agentCode) 1497 || String(state.serialNo || '') !== String(serialNo) 1498 || !Array.isArray(state.tickets) 1499 || !Array.isArray(state.facilities) 1500 || !Number(state.savedAt) 1501 || Date.now() - Number(state.savedAt) > V2_CART_STORAGE_MAX_AGE_MS 1502 || (mbNo && String(mbNo).trim() && String(state.buyerNo || '') !== String(mbNo).trim()); 1503 if (invalid || (!state.tickets.length && !state.facilities.length)) { 1504 v2RemoveCartStorage(serialNo); 1505 return null; 1506 } 1507 return state; 1508 } catch (e) { 1509 v2RemoveCartStorage(serialNo); 1510 return null; 1511 } 1512 } 1513 1514 function v2PrepareCartRestore() { 1515 const serialNo = v2GetQuerySerialNo(); 1516 const state = v2ReadCartRestoreState(serialNo); 1517 if (!state) return; 1518 1519 v2CheckoutSerialNo = serialNo; 1520 v2CheckoutBuyerNo = mbNo && String(mbNo).trim() 1521 ? String(mbNo).trim() : String(state.buyerNo || ''); 1522 v2CartRestoreState = state; 1523 v2CartRestorePending = true; 1524 } 1525 1526 function v2SerializableCartItems(items) { 1527 return (items || []).map(function(item) { 1528 const copy = $.extend(true, {}, item); 1529 delete copy.releaseInProgress; 1530 return copy; 1531 }); 1532 } 1533 1534 function v2PersistCartState() { 1535 if (v2CartRestorePending || v2CartRestoreInProgress) return; 1536 if (!v2BookList.length && !v2CBookList.length) { 1537 v2RemoveCartStorage(v2CheckoutSerialNo); 1538 return; 1539 } 1540 1541 const identity = v2EnsureCheckoutIdentity(); 1542 const state = { 1543 version: V2_CART_STORAGE_VERSION, 1544 savedAt: Date.now(), 1545 agentCode: agentCode, 1546 serialNo: identity.serialNo, 1547 buyerNo: identity.buyerNo, 1548 partnerCode: pCode || '', 1549 language: bookingLanguageParam, 1550 tickets: v2SerializableCartItems(v2BookList), 1551 facilities: v2SerializableCartItems(v2CBookList) 1552 }; 1553 try { sessionStorage.setItem(v2CartStorageKey(identity.serialNo), JSON.stringify(state)); } catch (e) {} 1554 } 1555 1556 function v2FacilityRestoreKey(item) { 1557 const times = String(item && item.bkTimeList || '').split(',').map(function(value) { 1558 return String(value || '').trim(); 1559 }
1559).filter(Boolean).sort(); 1560 return String(item && item.bkDate || '').replace(/-/g, '') + '|' 1561 + String(item && item.spNo || '').trim() + '|' 1562 + String(item && item.bkSeq || '').trim() + '|' 1563 + times.join(','); 1564 } 1565 1566 function v2RestoreTicketEntry(source) { 1567 const item = $.extend(true, {}, source || {}); 1568 const qty = Number(item.qty || 0); 1569 const spAmt = Number(item.spAmt || 0); 1570 const finalAmt = Number(item.dcSpAmt || item.spAmt || 0); 1571 const saleType = String(item.saleType || '').toUpperCase(); 1572 if (!item.spNo || qty <= 0 || spAmt <= 0 || finalAmt <= 0) return false; 1573 if (saleType === 'R' && (!item.bkDate || String(item.bkDate) < getToDay('-'))) return false; 1574 1575 v2BookIdx++; 1576 item.bookIdx = v2BookIdx; 1577 item.qty = qty; 1578 v2BookList.push(item); 1579 v2TotalAmt += finalAmt * qty; 1580 v2OriTotAmt += spAmt * qty; 1581 v2TicketDivCnt(item.bookIdx, 1582 String(item.spName || '') + (item.bkDate ? ' (' + item.bkDate + ')' : ''), 1583 spAmt, finalAmt, finalAmt < spAmt, item.dcType || '', item.dcValue || item.dcAmt || 0, qty); 1584 return true; 1585 } 1586 1587 function v2RestoreFacilityEntry(source, status) { 1588 const item = $.extend(true, {}, source || {}); 1589 const times = String(item.bkTimeList || '').split(',').map(function(value) { 1590 return String(value || '').trim(); 1591 }).filter(Boolean); 1592 const spAmt = Number(item.spAmt || 0); 1593 const finalAmt = Number(item.dcSpAmt || item.spAmt || 0); 1594 const expiry = Number(status && status.expiresAtEpochMs || 0); 1595 if (!item.spNo || !item.bkSeq || !times.length || spAmt <= 0 || finalAmt <= 0 || expiry <= v2FacilityHoldNow()) { 1596 return false; 1597 } 1598 1599 v2BookIdx++; 1600 item.bookIdx = v2BookIdx; 1601 item.qty = times.length; 1602 item.holdExpiresAtEpochMs = expiry; 1603 item.holdSerialNo = v2CheckoutSerialNo; 1604 item.holdMinutes = Number(item.holdMinutes) || 20; 1605 item.spPlaceValue = item.spPlaceValue || (String(item.spNo) + String(item.bkSeq)); 1606 item.displayTxt = item.displayTxt || v2BuildSpPlaceName(item.spPlaceName || item.spName || '', item.bkTimeList); 1607 v2CBookList.push(item); 1608 v2TotalAmt += finalAmt * item.qty; 1609 v2OriTotAmt += spAmt * item.qty; 1610 v2CavanaDivCnt(item.bookIdx, item.displayTxt, item.spPlaceValue, 1611 finalAmt * item.qty, item.dcCode || '', item.dcType || '', item.dcValue || item.dcAmt || 0, 1612 spAmt * item.qty); 1613 return true; 1614 } 1615 1616 function v2ApplyRestoredCart(tickets, facilities, statusMap, options) { 1617 options = options || {}; 1618 v2BookList = []; 1619 v2CBookList = []; 1620 v2TotalAmt = 0; 1621 v2OriTotAmt = 0; 1622 v2BookIdx = 0; 1623 1624 (tickets || []).forEach(v2RestoreTicketEntry); 1625 let invalidFacilityCount = 0; 1626 (facilities || []).forEach(function(item) { 1627 const status = statusMap[v2FacilityRestoreKey(item)]; 1628 if (!status || status.valid !== true || !v2RestoreFacilityEntry(item, status)) { 1629 invalidFacilityCount++; 1630 } 1631 }); 1632 1633 const firstDateItem = v2CBookList[0] || v2BookList.find(function(item) { return item.bkDate; }); 1634 const cartDate = firstDateItem ? String(firstDateItem.bkDate || '') : ''; 1635 if (cartDate) { 1636 $('#bookDt').val(cartDate); 1637 if (v2CBookList.length) selectedDate = cartDate; 1638 } 1639 1640 $('.totalAmt').html(toLocaleString(v2TotalAmt)); 1641 $('.mobile-total').text(toLocaleString(v2TotalAmt)); 1642 if (v2BookList.length || v2CBookList.length) { 1643 $('.totalAmtInfo').css('display', 'flex'); 1644 $('.btn-next').show(); 1645 $('#v2EmptyMsg, #v2MobileEmptyMsg').hide(); 1646 if (window.innerWidth <= 768) $('#v2MobileTotalInfo').show(); 1647 } else { 1648 $('.totalAmtInfo, .btn-next, #v2MobileTotalInfo').hide(); 1649 $('#v2EmptyMsg, #v2MobileEmptyMsg').show(); 1650 } 1651 1652 v2SyncFacilityHoldTimer(); 1653 v2RenderNotice(); 1654 v2SyncToV1(); 1655 v2CartRestoreInProgress = false;
1656 v2CartRestorePending = false; 1657 v2CartRestoreState = null; 1658 if (options.persist !== false) v2PersistCartState(); 1659 1660 const restoreWarnings = []; 1661 if (invalidFacilityCount > 0 && options.suppressInvalidNotice !== true) { 1662 restoreWarnings.push(bookingI18n.cartRestoreExpired); 1663 } 1664 if (Number(options.languageRemovedCount || 0) > 0) { 1665 restoreWarnings.push(bookingI18n.cartRestoreLanguageUnavailable); 1666 } 1667 if (restoreWarnings.length) { 1668 v2SwalWarning(restoreWarnings.join('\n'), bookingI18n.cartRestoreTitle); 1669 } 1670 if (typeof options.done === 'function') options.done(); 1671 } 1672 1673 function v2LocalizedTicketName(detail, item) { 1674 const children = detail && Array.isArray(detail.childProducts) 1675 ? detail.childProducts : []; 1676 const matched = children.find(function(child) { 1677 return String(child.spNo || '') === String(item.spNo || ''); 1678 }); 1679 if (matched && String(matched.spName || '').trim()) { 1680 return String(matched.spName).trim(); 1681 } 1682 if (detail && String(detail.spNo || '') === String(item.spNo || '')) { 1683 return String(detail.spName || detail.displayTitle || '').trim(); 1684 } 1685 return ''; 1686 } 1687 1688 function v2RelocalizeFacilityCartItem(item) { 1689 const localized = v2FacilityDataMap[String(item.spNo || '')] 1690 || v2FacilityDataMap[String(item.displayId || '')]; 1691 const localizedName = localized 1692 ? String(localized.spName || localized.displayTitle || '').trim() : ''; 1693 if (!localizedName) return; 1694 1695 const oldName = String(item.spName || ''); 1696 const oldPlaceName = String(item.spPlaceName || oldName); 1697 const placeSuffix = oldName && oldPlaceName.indexOf(oldName) === 0 1698 ? oldPlaceName.substring(oldName.length) : ''; 1699 const localizedPlaceName = localizedName + placeSuffix; 1700 const oldDisplayText = String(item.displayTxt || oldPlaceName); 1701 const displaySuffix = oldPlaceName && oldDisplayText.indexOf(oldPlaceName) === 0 1702 ? oldDisplayText.substring(oldPlaceName.length) : ''; 1703 1704 item.spName = localizedName; 1705 item.spPlaceName = localizedPlaceName; 1706 item.displayTxt = localizedPlaceName + displaySuffix; 1707 } 1708 1709 function v2RelocalizeCartState(state, done) { 1710 const result = { 1711 tickets: v2SerializableCartItems(state.tickets || []), 1712 facilities: v2SerializableCartItems(state.facilities || []), 1713 removedCount: 0 1714 }; 1715 const savedLanguage = v2NormalizeBookingLanguage(state.language); 1716 if (savedLanguage && savedLanguage === bookingLanguageParam) { 1717 done(result); 1718 return; 1719 } 1720 1721 result.facilities.forEach(v2RelocalizeFacilityCartItem); 1722 if (!result.tickets.length) { 1723 done(result); 1724 return; 1725 } 1726 1727 const keep = result.tickets.map(function() { return true; }); 1728 const groups = {}; 1729 result.tickets.forEach(function(item, index) { 1730 const displayId = String(item.displayId || '').trim(); 1731 if (!displayId) { 1732 keep[index] = false; 1733 return; 1734 } 1735 const purchaseContext = String(item.purchaseContext || 'regular'); 1736 const detailDate = String(item.bkDate || '').trim() 1737 || (purchaseContext === 'partner' ? v2GetPartnerLookupDate() : getToDay('-')); 1738 const key = purchaseContext + '|' + displayId + '|' + detailDate; 1739 if (!groups[key]) { 1740 groups[key] = { 1741 displayId: displayId, 1742 purchaseContext: purchaseContext, 1743 detailDate: detailDate, 1744 indexes: [] 1745 }; 1746 } 1747 groups[key].indexes.push(index); 1748 }); 1749 1750 const groupList = Object.keys(groups).map(function(key) { return groups[key]; }); 1751 if (!groupList.length) { 1752 result.tickets = []; 1753 result.removedCount = keep.length; 1754 done(result); 1755 return; 1756 } 1757 1758 let remaining = groupList.length; 1759 function finishGroup() { 1760 remaining--; 1761 if (remaining > 0) return; 1762 result.removedCount = keep.filter(function(value) { return !value; }).length; 1763 result.tickets = result.tickets.filter(function(item, index) { return keep[index]; }); 1764 done(result); 1765 } 1766 1767 groupList.forEach(function(group) { 1768 const detailPath = '/hBook/v2/display/detail/' 1769 + encodeURIComponent(group.displayId) 1770 + '?bkDate=' + encodeURIComponent(group.detailDate); 1771 $.ajax({ 1772 url: group.purchaseContext === 'partner' 1773 ? v2AppendPartnerCode(detailPath) 1774 : v2AppendLanguage(detailPath), 1775 type: 'get', 1776 dataType: 'json', 1777 success: function(detail) {
1778 group.indexes.forEach(function(index) { 1779 const localizedName = v2LocalizedTicketName( 1780 detail, result.tickets[index]); 1781 if (localizedName) result.tickets[index].spName = localizedName; 1782 else keep[index] = false; 1783 }); 1784 }, 1785 error: function() { 1786 group.indexes.forEach(function(index) { keep[index] = false; }); 1787 }, 1788 complete: finishGroup 1789 }); 1790 }); 1791 } 1792 1793 function v2RestoreSavedCart(done) { 1794 const state = v2CartRestoreState; 1795 if (!v2CartRestorePending || !state) { 1796 v2CartRestorePending = false; 1797 if (typeof done === 'function') done(); 1798 return; 1799 } 1800 1801 v2CartRestoreInProgress = true; 1802 v2RelocalizeCartState(state, function(localized) { 1803 const tickets = localized.tickets; 1804 const facilities = localized.facilities; 1805 const restoreOptions = function(extra) { 1806 return $.extend({ 1807 done: done, 1808 languageRemovedCount: localized.removedCount 1809 }, extra || {}); 1810 }; 1811 if (!facilities.length) { 1812 v2ApplyRestoredCart(tickets, [], {}, restoreOptions()); 1813 return; 1814 } 1815 1816 const bookingDates = facilities.map(function(item) { return String(item.bkDate || ''); }) 1817 .filter(Boolean).filter(function(value, index, list) { return list.indexOf(value) === index; }); 1818 if (bookingDates.length !== 1) { 1819 v2ApplyRestoredCart(tickets, facilities, {}, restoreOptions()); 1820 return; 1821 } 1822 1823 $.ajax({ 1824 url: '/hBook/v2/facility/hold/status/' + encodeURIComponent(agentCode) 1825 + '/' + encodeURIComponent(bookingDates[0]) 1826 + '/' + encodeURIComponent(v2CheckoutSerialNo), 1827 type: 'post', 1828 dataType: 'json', 1829 contentType: 'application/json; charset=utf-8', 1830 timeout: 10000, 1831 data: JSON.stringify(facilities.map(v2FacilityHoldPayload)), 1832 success: function(data) { 1833 const statusMap = {}; 1834 if (data && data.code === '0000' && Array.isArray(data.facilities)) { 1835 if (Number(data.serverNowEpochMs)) { 1836 v2FacilityHoldClockOffsetMs = Number(data.serverNowEpochMs) - Date.now(); 1837 } 1838 data.facilities.forEach(function(status) { 1839 statusMap[String(status.key || '')] = status; 1840 }); 1841 } 1842 v2ApplyRestoredCart(tickets, facilities, statusMap, restoreOptions()); 1843 }, 1844 error: function() { 1845 v2ApplyRestoredCart(tickets, facilities, {}, restoreOptions({ 1846 persist: false, 1847 suppressInvalidNotice: true, 1848 done: function() { 1849 v2SwalWarning(bookingI18n.cartRestoreUnavailable, bookingI18n.cartRestoreTitle); 1850 if (typeof done === 'function') done(); 1851 } 1852 })); 1853 } 1854 }); 1855 }); 1856 } 1857 1858 function v2RequireLoginBeforeCart(item) { 1859 const requiresLogin = item ? v2IsMemberOnlyProduct(item) : v2HasMemberOnlyProduct(); 1860 if (!requiresLogin) return true; 1861 if (mbNo && String(mbNo).trim() !== '') return true; 1862 v2PromptMemberLogin(); 1863 return false; 1864 } 1865 1866 function v2SelectDisplayProduct(displayId, viewKey, partnerYn, saleType) { 1867 const isGuestSeason = partnerYn !== true 1868 && String(saleType || '').toUpperCase() === 'S' 1869 && (!mbNo || !String(mbNo).trim()); 1870 if (isGuestSeason) { 1871 v2PromptMemberLogin(); 1872 return; 1873 } 1874 v2ToggleDetail(displayId, viewKey, partnerYn, saleType); 1875 } 1876 1877 /* ================================================================ 1878 V1 ë기í í¬í¼ 1879 ================================================================ */ 1880 function v2SyncToV1() { 1881 try { 1882 cBookList = v2CBookList; 1883 bookList = v2BookList; 1884 totalAmt = v2TotalAmt; 1885 oriTotAmt = v2OriTotAmt; 1886 } catch(e) {} 1887 v2PersistCartState(); 1888 } 1889 1890 /* ================================================================ 1891 ìíì¡°í ë¡ë© ì¤í¼ë í¬í¼ 1892 ================================================================ */ 1893 var v2LoadingCount = 0; 1894 var v2LoadingStartAt = 0; 1895 var v2LoadingMinMs = 600; 1896 var v2LoadingHideTimer = null; 1897 1898 function v2ShowLoading(message) { 1899 var loadingText = (typeof message === 'string' && message.trim()) 1900 ? message 1901 : bookingI18n.loading; 1902 1903 v2LoadingCount++; 1904
1905 if (v2LoadingHideTimer) { 1906 clearTimeout(v2LoadingHideTimer); 1907 v2LoadingHideTimer = null; 1908 } 1909 1910 if (v2LoadingCount === 1) { 1911 v2LoadingStartAt = Date.now(); 1912 1913 $('#v2-loading-overlay .v2-loading-text').text(loadingText); 1914 1915 $('#v2-loading-overlay') 1916 .appendTo('body') 1917 .stop(true, true) 1918 .css({ 1919 display: 'flex', 1920 opacity: 1 1921 }); 1922 } 1923 } 1924 1925 function v2HideLoading() { 1926 v2LoadingCount = Math.max(0, v2LoadingCount - 1); 1927 1928 if (v2LoadingCount > 0) { 1929 return; 1930 } 1931 1932 var elapsed = Date.now() - v2LoadingStartAt; 1933 var remain = Math.max(0, v2LoadingMinMs - elapsed); 1934 1935 v2LoadingHideTimer = setTimeout(function() { 1936 if (v2LoadingCount === 0) { 1937 $('#v2-loading-overlay').hide(); 1938 $('#v2-loading-overlay .v2-loading-text').text(bookingI18n.loading); 1939 } 1940 v2LoadingHideTimer = null; 1941 }, remain); 1942 } 1943 1944 function v2ResetLoading() { 1945 v2LoadingCount = 0; 1946 v2LoadingStartAt = 0; 1947 1948 if (v2LoadingHideTimer) { 1949 clearTimeout(v2LoadingHideTimer); 1950 v2LoadingHideTimer = null; 1951 } 1952 1953 $('#v2-loading-overlay').hide(); 1954 $('#v2-loading-overlay .v2-loading-text').text(bookingI18n.loading); 1955 } 1956 1957 /* ================================================================ 1958 ì´ê¸°í 1959 ================================================================ */ 1960 function v2InitPage() { 1961 if (invalidPCodeYn === 'Y') { 1962 v2SwalError(bookingI18n.partnerCodeInvalid, bookingI18n.codeCheck); 1963 return; 1964 } 1965 1966 v2ShowLoading(); 1967 1968 $.when( 1969 v2LoadDisplayList(), 1970 v2LoadFacilityList() 1971 ).always(function() { 1972 v2RestoreSavedCart(v2HideLoading); 1973 }); 1974 1975 v2RenderNotice(); 1976 } 1977 1978 /* ================================================================ 1979 ìë¨ í ì í 1980 ================================================================ */ 1981 function v2SetTabVisibility(tab, visible) { 1982 $('#v2TabNav .nav-link[data-tab="' + tab + '"]').closest('.nav-item').toggle(!!visible); 1983 } 1984 1985 function v2IsTabVisible(tab) { 1986 const $item = $('#v2TabNav .nav-link[data-tab="' + tab + '"]').closest('.nav-item'); 1987 return $item.length > 0 && $item.css('display') !== 'none'; 1988 } 1989 1990 function v2FirstVisibleTab(preferred) { 1991 if (preferred && v2IsTabVisible(preferred)) { 1992 return preferred; 1993 } 1994 1995 const tabOrder = ['special', 'ticket', 'facility', 'member']; 1996 for (let i = 0; i < tabOrder.length; i++) { 1997 if (v2IsTabVisible(tabOrder[i])) { 1998 return tabOrder[i]; 1999 } 2000 } 2001 return null; 2002 } 2003 2004 function v2SwitchTab(tab) { 2005 const nextTab = v2FirstVisibleTab(tab); 2006 if (!nextTab) { 2007 return; 2008 } 2009 2010 $('#v2TabNav .nav-link').removeClass('active'); 2011 $('#v2TabNav .nav-link[data-tab="' + nextTab + '"]').addClass('active'); 2012 $('.tab-pane').hide(); 2013 $('#v2Tab' + nextTab.charAt(0).toUpperCase() + nextTab.slice(1)).show(); 2014 } 2015 2016 function v2EnsureVisibleTab(preferred) { 2017 const activeTab = $('#v2TabNav .nav-link.active').data('tab'); 2018 v2SwitchTab(v2IsTabVisible(activeTab) ? activeTab : preferred); 2019 } 2020 2021 /* ================================================================ 2022 ì ìì¬í ëì ë ¸ì¶ â ì¥ë°êµ¬ë + íì¬ ì í ìí ê¸°ì¤ 2023 ================================================================ 2024 ì°ì ìì: ì¥ë°êµ¬ë ê¸°ì¤ > íì¬ ì í ìí ê¸°ì¤ > 기본ê°(PERIOD) 2025 RESERVE : ë ì§ì§ì ê¶(R) / ì´ë²¤í¸ê¶(E) / ë¶ëìì¤ í¬í¨ ì 2026 PERIOD : ë ì§ë¯¸ì§ì ê¶(P) / ìì¦ê¶(S) ë§ ì í ì 2027 â» S(ìì¦ê¶)ë ì ì± íì ì ê¹ì§ Pì ëì¼ ë¬¸êµ¬ ì ì© 2028 ================================================================ */ 2029 2030 /** ì¥ë°êµ¬ë ê¸°ì¤ ì ìì¬í ì í ë°í */ 2031 function v2GetCartNoticeType() { 2032 var hasFacility = v2CBookList.length > 0; 2033 var hasReserve = v2BookList.some(function(item) { 2034 var st = String(item.saleType || '').toUpperCase(); 2035 return st === 'R'; 2036 }); 2037 var hasPeriod = v2BookList.some(function(item) { 2038 var st = String(item.saleType || '').toUpperCase(); 2039 return st === 'P' || st === 'E' || st === 'S'; 2040 }); 2041 if (hasFacility || hasReserve) return 'RESERVE'; 2042 if (hasPeriod) return 'PERIOD'; 2043 return ''; 2044 } 2045 2046 /** ìµì¢ ì ìì¬í ì í ë°í (ì¥ë°êµ¬ë > íì¬ ì í ìí > 기본ê°) */ 2047 function v2ResolveNoticeType() { 2048 var cartType = v2GetCartNoticeType(); 2049 if (cartType) return cartType; 2050 if (v2ActiveNoticeType) return v2ActiveNoticeType; 2051 return 'PERIOD'; 2052 } 2053 2054 function v2RenderNotice() { 2055 var type = v2ResolveNoticeType(); 2056 var $wrap = $('#v2NoticeWrap'); 2057 var $list = $('#v2NoticeList'); 2058 2059 if (!type) { 2060 $wrap.hide(); 2061 $list.empty(); 2062 return; 2063 } 2064 2065 var items = []; 2066 if (type === 'RESERVE') { 2067 items = [ 2068 bookingI18n.noticeCapacity, 2069 bookingI18n.noticeDateRefund, 2070 bookingI18n.noticeAdvance, 2071 bookingI18n.noticeUsedNoRefund 2072 ]; 2073 } else { 2074 items = [ 2075 bookingI18n.noticeCapacity, 2076 bookingI18n.noticeUnusedRefund, 2077 bookingI18n.noticeAutoRefund, 2078 bookingI18n.noticeNoChange, 2079 bookingI18n.noticeUsedTicketNoRefund 2080 ]; 2081 } 2082 2083 var html = ''; 2084 for (var i = 0; i < items.length; i++) { 2085 html += '<li>' + items[i] + '</li>'; 2086 } 2087 $list.html(html); 2088 $wrap.show(); 2089 } 2090 2091 function v2LoadDisplayList() { 2092 const regularListUrl = "/hBook/v2/display/list/" + agentCode + "/"
2092+ getToDay('-'); 2093 const partnerListUrl = "/hBook/v2/display/list/" + agentCode + "/" + v2GetPartnerLookupDate(); 2094 let regularFailed = false; 2095 let partnerFailed = false; 2096 2097 const regularRequest = $.ajax({ 2098 url: v2AppendLanguage(regularListUrl), 2099 type: "get", 2100 dataType: "json" 2101 }).then(function(list) { 2102 return list || []; 2103 }, function() { 2104 regularFailed = true; 2105 return []; 2106 }); 2107 2108 const partnerRequest = pCode ? $.ajax({ 2109 url: v2AppendPartnerCode(partnerListUrl), 2110 type: "get", 2111 dataType: "json" 2112 }).then(function(list) { 2113 return list || []; 2114 }, function() { 2115 partnerFailed = true; 2116 return []; 2117 }) : $.Deferred().resolve([]).promise(); 2118 2119 return $.when(regularRequest, partnerRequest).done(function(regularList, partnerList) { 2120 v2RenderProductList(regularList, partnerList, regularFailed, partnerFailed); 2121 }); 2122 } 2123 2124 function v2LoadFacilityList() { 2125 return $.ajax({ 2126 url: v2AppendLanguage("/hBook/v2/facility/list/" + agentCode + "/" + getToDay('-')), 2127 type: "get", 2128 dataType: "json", 2129 success: function(list) { v2RenderFacilityList(list || []); }, 2130 error: function() { $('#v2FacilityList').html('<div class="alert alert-light">' + bookingI18n.productLoadFailed + '</div>'); } 2131 }); 2132 } 2133 2134 function v2CalculateFinalPrice(price, dcType, dcAmt) { 2135 const originalPrice = Number(price || 0); 2136 const discountType = Number(dcType || 0); 2137 const discountAmount = Number(dcAmt || 0); 2138 2139 if (discountAmount > 0 && discountType === 1) { 2140 return originalPrice - discountAmount; 2141 } 2142 if (discountAmount > 0 && discountType === 2) { 2143 return Math.round(originalPrice * (1 - discountAmount / 100)); 2144 } 2145 return originalPrice; 2146 } 2147 2148 function v2NormalizeProductPriceRange(minPrice, maxPrice) { 2149 if (minPrice == null || maxPrice == null || minPrice === '' || maxPrice === '') { 2150 return null; 2151 } 2152 2153 const min = Number(minPrice); 2154 const max = Number(maxPrice); 2155 if (!isFinite(min) || !isFinite(max)) return null; 2156 return { min: Math.min(min, max), max: Math.max(min, max) }; 2157 } 2158 2159 function v2GetProductSummaryPriceRange(item, purchaseContext) { 2160 item = item || {}; 2161 if (purchaseContext === 'partner') { 2162 const ownPrice = v2CalculateFinalPrice(item.price, item.dcType, item.dcAmt); 2163 return v2NormalizeProductPriceRange(ownPrice, ownPrice); 2164 } 2165 return v2NormalizeProductPriceRange( 2166 item.childMinFinalPrice, item.childMaxFinalPrice); 2167 } 2168 2169 function v2GetDetailSummaryPriceRange(data, partnerYn) { 2170 data = data || {}; 2171 if (partnerYn === true) { 2172 const ownPrice = v2CalculateFinalPrice(data.price, data.dcType, data.dcAmt); 2173 return v2NormalizeProductPriceRange(ownPrice, ownPrice); 2174 } 2175 2176 const children = Array.isArray(data.childProducts) ? data.childProducts : []; 2177 if (!children.length) return null; 2178 2179 let min = null; 2180 let max = null; 2181 children.forEach(function(child) { 2182 const finalPrice = v2CalculateFinalPrice( 2183 child.spAmt, child.dcType, child.dcAmt); 2184 min = min == null ? finalPrice : Math.min(min, finalPrice); 2185 max = max == null ? finalPrice : Math.max(max, finalPrice); 2186 }); 2187 return v2NormalizeProductPriceRange(min, max); 2188 } 2189 2190 function v2FormatProductPriceRange(range) { 2191 if (!range) return ''; 2192 return range.min === range.max 2193 ? toLocaleString(range.min) 2194 : toLocaleString(range.min) + ' ~ ' + toLocaleString(range.max); 2195 } 2196 2197 function v2BuildProductSummaryPriceHtml(item, purchaseContext, viewKey) { 2198 const priceText = v2FormatProductPriceRange( 2199 v2GetProductSummaryPriceRange(item, purchaseContext)); 2200 return '<p class="product-summary-price' + (priceText ? '' : ' is-hidden') 2201 + '" id="v2ProductSummaryPrice_' + viewKey + '">' 2202 + '<span class="product-summary-price-label">' + bookingI18n.salePrice + '</span>' 2203 + '<strong>' + priceText + '</strong></p>'; 2204 } 2205
2206 function v2UpdateProductSummaryPrice(viewKey, data, partnerYn) { 2207 const $price = $('#v2ProductSummaryPrice_' + viewKey); 2208 if (!$price.length) return; 2209 2210 const priceText = v2FormatProductPriceRange( 2211 v2GetDetailSummaryPriceRange(data, partnerYn)); 2212 $price.find('strong').text(priceText); 2213 $price.toggleClass('is-hidden', !priceText); 2214 } 2215 2216 /* ================================================================ 2217 í°ì¼ ëª©ë¡ ë ëë§ â í¹ê°(ì´ë²¤í¸ê¶) / ì ì¥í°ì¼ íì¼ë¡ ë¶ë¦¬ 2218 ================================================================ */ 2219 function v2RenderProductList(list, partnerList, regularFailed, partnerFailed) { 2220 list = Array.isArray(list) ? list : []; 2221 partnerList = Array.isArray(partnerList) ? partnerList : []; 2222 const reserveList = [], periodList = [], eventList = [], seasonList = [], unknownList = []; 2223 list.forEach(function(item) { 2224 const t = String(item.saleType || '').trim().toUpperCase(); 2225 item.saleType = t; 2226 if (t === 'R') reserveList.push(item); 2227 else if (t === 'P') periodList.push(item); 2228 else if (t === 'E') eventList.push(item); 2229 else if (t === 'S') seasonList.push(item); 2230 else unknownList.push(item); 2231 }); 2232 2233 v2SetTabVisibility('special', eventList.length > 0); 2234 2235 function buildCardHtml(item, purchaseContext) { 2236 const thumb = (item.thumbUrl && item.thumbUrl.trim()) ? item.thumbUrl : '/img/event/event.png'; 2237 const today = getToDay('-'); 2238 const isComingSoon = item.saleStartDate && item.saleStartDate > today; 2239 const isSaleEnded = String(item.saleType || '').toUpperCase() === 'R' 2240 && item.saleEndDate && item.saleEndDate < today; 2241 const clickable = !isComingSoon && !isSaleEnded; 2242 const isPartner = purchaseContext === 'partner'; 2243 const viewKey = (isPartner ? 'partner-' : 'regular-') + item.displayId; 2244 const statusBadge = isComingSoon 2245 ? ' <span class="badge badge-warning">' + bookingI18n.openSoon + '(' + item.saleStartDate + ')</span>' 2246 : isSaleEnded 2247 ? ' <span class="badge badge-secondary">' + bookingI18n.saleEnded + '(' + item.saleEndDate + ')</span>' 2248 : ' <span class="badge badge-success">' + bookingI18n.onSale + '</span>'; 2249 2250 let h = '<div class="product-item" data-display-id="' + item.displayId + '" data-purchase-context="' + purchaseContext + '">'; 2251 h += '<div class="product-summary' + (!clickable ? ' is-unavailable' : '') + '"' 2252 + (clickable ? ' onclick="v2SelectDisplayProduct(\'' + item.displayId + '\', \'' + viewKey + '\', ' + isPartner + ', \'' + (item.saleType || '') + '\')"' : '') 2253 + '>'; 2254 h += '<img class="product-thumb" src="' + thumb + '" onerror="if(!this.dataset.fallback){this.dataset.fallback=\'1\';this.src=\'/img/event/event.png\';}" alt="thumb">'; 2255 h += '<div class="product-info">'; 2256 h += '<h4 class="product-title">' + (item.displayTitle || '') + statusBadge + '</h4>'; 2257 h += '<p class="product-subtitle">' + (item.displaySubTitle || '') + '</p>'; 2258 h += v2BuildProductSummaryPriceHtml(item, purchaseContext, viewKey); 2259 h += '</div></div>'; 2260 if (clickable) h += '<div class="product-detail" id="v2-detail-' + viewKey + '"></div>'; 2261 h += '</div>'; 2262 return h; 2263 } 2264 2265 /* ââ í¹ê° í = ì´ë²¤í¸ê¶(E) ì ì© ââ */ 2266 let specialHtml; 2267 if (eventList.length) { 2268 specialHtml = '<div class="card"><div class="card-scroll">'; 2269 eventList.forEach(function(item) { specialHtml += buildCardHtml(item, 'regular'); }); 2270 specialHtml += '</div></div>'; 2271 } else { 2272 specialHtml = '<div class="alert alert-light">' + bookingI18n.emptySpecial + '</div>'; 2273 } 2274 $('#v2ProductListSpecial').html(specialHtml); 2275 2276 /* ââ ì ì¥í°ì¼ í = ë ì§ì§ì /ë ì§ë¯¸ì§ì /기í ââ */ 2277 const groups = [ 2278 { label: bookingI18n.groupPeriod, list: periodList }, 2279 { label: bookingI18n.groupReserved, list: reserveList }, 2280 { label: bookingI18n.groupOther, list: unknownList } 2281 ]; 2282 let ticketHtml = '<div class="card"><div class="card-scroll">'; 2283 let hasTicket = false;
2284 groups.forEach(function(g) { 2285 if (!g.list.length) return; 2286 hasTicket = true; 2287 ticketHtml += '<div class="subgroup">'; 2288 ticketHtml += '<h5 class="subgroup-title">' + g.label + '</h5>'; 2289 g.list.forEach(function(item) { ticketHtml += buildCardHtml(item, 'regular'); }); 2290 ticketHtml += '</div>'; 2291 }); 2292 ticketHtml += '</div></div>'; 2293 $('#v2ProductListTicket').html(hasTicket ? ticketHtml : '<div class="alert alert-light">' + bookingI18n.emptyTicket + '</div>'); 2294 2295 function buildMemberState(iconClass, title, description, actionsHtml, errorState) { 2296 const centered = !iconClass; 2297 let state = '<div class="member-tab-state' + (centered ? ' member-tab-state--centered' : '') 2298 + (errorState ? ' member-tab-state--error' : '') + '" role="status">'; 2299 if (iconClass) state += '<i class="bi ' + iconClass + '" aria-hidden="true"></i>'; 2300 state += '<div class="member-tab-state-copy"><strong>' + title + '</strong>'; 2301 state += '<p>' + description + '</p>'; 2302 if (actionsHtml) state += '<div class="member-tab-actions">' + actionsHtml + '</div>'; 2303 state += '</div></div>'; 2304 return state; 2305 } 2306 2307 let memberContentHtml = '<div class="member-tab-sections">'; 2308 2309 memberContentHtml += '<section class="member-product-section member-product-section--partner">'; 2310 memberContentHtml += '<div class="member-section-header"><h5 class="member-section-title">' + bookingI18n.partnerProducts + '</h5>'; 2311 if (pCode) { 2312 memberContentHtml += '<button type="button" class="btn btn-outline-secondary btn-sm" onclick="v2PromptPartnerCode()"><i class="bi bi-pencil-square" aria-hidden="true"></i> ' + bookingI18n.partnerCodeChange + '</button>'; 2313 } 2314 memberContentHtml += '</div>'; 2315 2316 if (!pCode) { 2317 memberContentHtml += buildMemberState( 2318 '', 2319 bookingI18n.partnerCodeEnter, 2320 bookingI18n.partnerCodeDescription, 2321 '<button type="button" class="btn btn-orange" onclick="v2PromptPartnerCode()"><i class="bi bi-key" aria-hidden="true"></i> ' + bookingI18n.partnerCodeButton + '</button>', 2322 false 2323 ); 2324 } else { 2325 memberContentHtml += '<div class="partner-code-box"><p class="partner-code-status">' + bookingI18n.partnerCodeApplied + ' <strong>' + v2EscapeHtml(pCode) + '</strong></p></div>'; 2326 if (partnerFailed) { 2327 memberContentHtml += buildMemberState( 2328 'bi-exclamation-circle', 2329 bookingI18n.partnerLoadFailed, 2330 bookingI18n.retrySupport, 2331 '', 2332 true 2333 ); 2334 } else if (partnerList.length) { 2335 partnerList.forEach(function(item) { memberContentHtml += buildCardHtml(item, 'partner'); }); 2336 } else { 2337 memberContentHtml += buildMemberState( 2338 'bi-info-circle', 2339 bookingI18n.partnerEmpty, 2340 bookingI18n.partnerEmptyDescription, 2341 '', 2342 false 2343 ); 2344 } 2345 } 2346 memberContentHtml += '</section>'; 2347 2348 if (membershipProductsEnabled) { 2349 memberContentHtml += '<section class="member-product-section member-product-section--season">'; 2350 memberContentHtml += '<div class="member-section-header"><h5 class="member-section-title">' + bookingI18n.membershipProducts + '</h5></div>'; 2351 const isGuest = !mbNo || !String(mbNo).trim(); 2352 const memberActions = '<button type="button" class="btn btn-orange" onclick="v2GoLoginWithReturnUrl()"><i class="bi bi-box-arrow-in-right" aria-hidden="true"></i> ' + bookingI18n.login + '</button>' 2353 + '<button type="button" class="btn btn-outline-secondary" onclick="v2GoJoinWithReturnUrl()"><i class="bi bi-person-plus" aria-hidden="true"></i> ' + bookingI18n.join + '</button>'; 2354 if (regularFailed) { 2355 memberContentHtml += buildMemberState( 2356 'bi-exclamation-circle', 2357 bookingI18n.membershipEmpty, 2358 bookingI18n.retrySupport, 2359 '', 2360 true 2361 ); 2362 } else if (seasonList.length) { 2363 if (isGuest) { 2364 memberContentHtml += buildMemberState( 2365 '', 2366 bookingI18n.membershipLoginTitle, 2367 bookingI18n.membershipLoginDescription, 2368 memberActions, 2369 false 2370 ); 2371 } 2372 seasonList.forEach(function(item) { memberContentHtml += buildCardHtml(item, 'regular'); }); 2373 } else if (isGuest) { 2374 memberContentHtml += buildMemberState( 2375 'bi-lock', 2376 bookingI18n.loginRequiredTitle, 2377 bookingI18n.membershipLoginDescription, 2378 memberActions, 2379 false 2380 ); 2381 } else { 2382 memberContentHtml += buildMemberState( 2383 'bi-info-circle', 2384 bookingI18n.membershipEmpty, 2385 bookingI18n.membershipEmptyDescription, 2386 '', 2387 false 2388 ); 2389 } 2390 memberContentHtml += '</section>'; 2391 } 2392 memberContentHtml += '</div>'; 2393 2394 const memberHtml = '<div class="card"><div class="card-scroll member-tab-scroll">' + memberContentHtml + '</div></div>'; 2395 $('#v2ProductListMember').html(memberHtml); 2396 2397 if (regularFailed) { 2398 $('#v2ProductListSpecial').html('<div class="alert alert-light">' + bookingI18n.productLoadFailed + '</div>'); 2399 $('#v2ProductListTicket').html('<div class="alert alert-light">' + bookingI18n.productLoadFailed + '</div>'); 2400 } 2401 2402 const requestedTab = v2GetRequestedTab(); 2403 const firstTab = requestedTab || (pCode ? 'member' : (eventList.length ? 'special' : (hasTicket ? 'ticket' : 'member'))); 2404 if (requestedTab || pCode) v2SwitchTab(firstTab); 2405 else v2EnsureVisibleTab(firstTab); 2406 } 2407 2408 /* ================================================================ 2409 ë¶ëìì¤ ëª©ë¡ ë ëë§ 2410 ================================================================ */ 2411 function v2BuildFacilityPriceHtml(price, dcType, dcAmt, minFinalPrice, maxFinalPrice) { 2412 const originalPrice = Number(price || 0); 2413 const discountType = Number(dcType || 0); 2414 const discountValue = Number(dcAmt || 0); 2415 const hasPriceRange = minFinalPrice != null && maxFinalPrice != null 2416 && minFinalPrice !== '' && maxFinalPrice !== '' 2417 && isFinite(Number(minFinalPrice)) && isFinite(Number(maxFinalPrice)); 2418 const hasDiscount = discountValue > 0 && (discountType === 1 || discountType === 2); 2419 2420 if (hasPriceRange) { 2421 const rangeMin = Math.min(Number(minFinalPrice), Number(maxFinalPrice)); 2422 const rangeMax = Math.max(Number(minFinalPrice), Number(maxFinalPrice)); 2423 const rangeText = rangeMin === rangeMax
2424 ? toLocaleString(rangeMin) 2425 : toLocaleString(rangeMin) + ' ~ ' + toLocaleString(rangeMax); 2426 return '<span class="v2-price-original">' 2427 + toLocaleString(originalPrice) + '</span> ' 2428 + '<span class="facility-final-price v2-price-discounted' 2429 + (rangeMin === rangeMax ? '' : ' facility-price-range') 2430 + '">' + rangeText + '</span>'; 2431 } 2432 2433 if (!hasDiscount) return toLocaleString(originalPrice); 2434 2435 const finalPrice = discountType === 1 2436 ? originalPrice - discountValue 2437 : originalPrice - (originalPrice * discountValue / 100); 2438 return '<span class="v2-price-original">' 2439 + toLocaleString(originalPrice) + '</span> ' 2440 + '<span class="v2-price-discounted">' 2441 + toLocaleString(finalPrice) + '</span>'; 2442 } 2443 2444 function v2UpdateFacilityCardPrice(sp) { 2445 if (!sp || sp.spNo == null) return; 2446 2447 const spNo = String(sp.spNo); 2448 const price = Number(sp.spAmt || 0); 2449 const dcAmt = Number(sp.dcAmt || 0); 2450 const dcType = Number(sp.dcType || 0); 2451 const cached = v2FacilityDataMap[spNo] || {}; 2452 2453 cached.price = price; 2454 cached.dcAmt = dcAmt; 2455 cached.dcType = dcType; 2456 cached.sangpumId = sp.sangpumId || ''; 2457 v2FacilityDataMap[spNo] = cached; 2458 2459 $('#v2FacCardPrice_' + spNo).html(v2BuildFacilityPriceHtml(price, dcType, dcAmt)); 2460 $('#v2FacBox_' + spNo).attr('data-spamt', price); 2461 } 2462 2463 function v2SetFacilityCardPriceMessage(spNo, message) { 2464 $('#v2FacCardPrice_' + String(spNo)).text(message || bookingI18n.priceUnavailable); 2465 } 2466 2467 function v2SetAllFacilityCardPriceMessages(message) { 2468 Object.keys(v2FacilityDataMap).forEach(function(spNo) { 2469 v2SetFacilityCardPriceMessage(spNo, message); 2470 }); 2471 } 2472 2473 function v2SyncFacilityCardPrices(list) { 2474 const selectedDateProducts = Array.isArray(list) ? list : []; 2475 Object.keys(v2FacilityDataMap).forEach(function(spNo) { 2476 const matched = selectedDateProducts.find(function(item) { 2477 return String(item.spNo) === String(spNo); 2478 }); 2479 if (matched) v2UpdateFacilityCardPrice(matched); 2480 else v2SetFacilityCardPriceMessage(spNo, bookingI18n.dateUnavailable); 2481 }); 2482 } 2483 2484 function v2RenderFacilityList(list) { 2485 if (!Array.isArray(list) || !list.length) { 2486 $('#v2FacilityList').html(''); 2487 return; 2488 } 2489 2490 v2FacilityDataMap = {}; 2491 list.forEach(function(item) { v2FacilityDataMap[item.spNo] = item; }); 2492 2493 function buildFacilityCardHtml(item) { 2494 const thumb = (item.thumbUrl && item.thumbUrl.trim()) ? item.thumbUrl : '/img/event/event.png'; 2495 const today = getToDay('-'); 2496 const isComingSoon = item.saleStartDate && item.saleStartDate > today; 2497 const clickable = !isComingSoon; 2498 const statusBadge = isComingSoon 2499 ? ' <span class="badge badge-warning">' + bookingI18n.openSoon + '(' + item.saleStartDate + ')</span>' 2500 : ' <span class="badge badge-success">' + bookingI18n.onSale + '</span>'; 2501 var startTime = v2FormatTimeHHmm(item.operStartTime); 2502 var endTime = v2FormatTimeHHmm(item.operEndTime); 2503 let h = '<div class="product-item" data-sp-no="' + item.spNo + '">'; 2504 h += '<div class="product-summary' + (isComingSoon ? ' is-coming-soon' : '') + '"' 2505 + (clickable ? ' onclick="v2ToggleFacilityDetail(\'' + item.spNo + '\', this)"' : '') 2506 + '>'; 2507 h += '<img class="product-thumb" src="' + thumb + '" onerror="if(!this.dataset.fallback){this.dataset.fallback=\'1\';this.src=\'/img/event/event.png\';}" alt="thumb">'; 2508 h += '<div class="product-info">'; 2509 h += '<h4 class="product-title">' + (item.displayTitle || '') + statusBadge + '</h4>'; 2510 h += '<p class="product-subtitle">' 2511 + (startTime ? ' (' + startTime + '~' + endTime + ')' : '') 2512 + '</p>'; 2513 h += '<p class="product-price" id="v2FacCardPrice_' + item.spNo + '">' 2514 + v2BuildFacilityPriceHtml(item.price, item.dcType, item.dcAmt, 2515 item.facilityMinFinalPrice, item.facilityMaxFinalPrice) 2516 + '</p>'; 2517 h += '</div></div>'; 2518 if (clickable) h += '<div class="facility-detail" id="v2-detail-fac-' + item.spNo + '"></div>'; 2519 h += '</div>'; 2520 return h; 2521 } 2522 2523 let html = '<div class="card"><div class="card-scroll">'; 2524 list.forEach(function(item) { html += buildFacilityCardHtml(item); }); 2525 html += '</div></div>'; 2526 2527 $('#v2FacilityList').html(html); 2528 } 2529 function v2FormatTimeHHmm(value) { 2530 if (value == null || value === '') return ''; 2531 2532 var str = String(value).replace(/:/g, '').trim(); 2533 while (str.length < 4) { 2534 str = '0' + str; 2535 } 2536 2537 return str.substring(0, 2) + ':' + str.substring(2, 4); 2538 } 2539 2540 /* ================================================================ 2541 ìì¸ í ê¸ 2542 ================================================================ */ 2543 function v2ToggleFacilityDetail(spNo) { 2544 const $detail = $('#v2-detail-fac-' + spNo); 2545 const $card = $detail.closest('.product-item'); 2546 const isOpen = $detail.is(':visible'); 2547 2548 $('.facility-detail').not($detail).slideUp(200); 2549 $('.product-item').not($card).removeClass('is-open'); 2550 2551 if (isOpen) { 2552 $detail.slideUp(200); 2553 $card.removeClass('is-open'); 2554 return; 2555 } 2556 $card.addClass('is-open'); 2557 2558 v2ActiveNoticeType = 'RESERVE'; 2559 v2RenderNotice(); 2560 2561 if (!$detail.data('loaded')) { 2562 const data = v2FacilityDataMap[spNo]; 2563 if (!data) { 2564 $detail.html('<div class="alert alert-light">' + bookingI18n.detailFailed + '</div>'); 2565 } else { 2566 const enriched = Object.assign({}, data, { displayId: spNo }); 2567 $detail.html(v2BuildDetailHtml(enriched)); 2568 v2InitFacilityDetail(spNo, enriched); 2569 } 2570 $detail.data('loaded', true); 2571 } 2572 $detail.stop(true, true).slideDown(200); 2573 } 2574 2575 function v2ToggleDetail(displayId, viewKey, partnerYn, summarySaleType) {
2576 viewKey = viewKey || ('regular-' + displayId); 2577 partnerYn = partnerYn === true; 2578 const $detail = $('#v2-detail-' + viewKey); 2579 const $card = $detail.closest('.product-item'); 2580 const isOpen = $detail.is(':visible'); 2581 2582 $('.product-detail').not($detail).slideUp(200); 2583 $('.product-item').not($card).removeClass('is-open'); 2584 2585 if (isOpen) { 2586 $detail.slideUp(200); 2587 $card.removeClass('is-open'); 2588 return; 2589 } 2590 $card.addClass('is-open'); 2591 2592 if (!$detail.data('loaded')) { 2593 const cartEntryDate = v2IsDateRequiredSaleType(summarySaleType) 2594 ? v2GetEntryTicketCartDate() 2595 : ''; 2596 const initialDetailDate = cartEntryDate || (partnerYn ? v2GetPartnerLookupDate() : getToDay('-')); 2597 $.ajax({ 2598 url: partnerYn 2599 ? v2AppendPartnerCode('/hBook/v2/display/detail/' + displayId + '?bkDate=' + initialDetailDate) 2600 : v2AppendLanguage('/hBook/v2/display/detail/' + displayId + '?bkDate=' + initialDetailDate), 2601 type: 'get', 2602 dataType: 'json', 2603 beforeSend: v2ShowLoading, 2604 complete: v2HideLoading, 2605 success: function(data) { 2606 data.__viewKey = viewKey; 2607 data.__partnerYn = partnerYn; 2608 v2UpdateProductSummaryPrice(viewKey, data, partnerYn); 2609 const detailSaleType = String(data.saleType || summarySaleType || '').toUpperCase(); 2610 const autoSelectedDate = v2IsDateRequiredSaleType(detailSaleType) ? cartEntryDate : ''; 2611 if (autoSelectedDate) { 2612 data.__selectedBkDate = autoSelectedDate; 2613 } 2614 $detail.html(v2BuildDetailHtml(data)); 2615 2616 var st = detailSaleType; 2617 var spGb = String(data.spGbCode || '').padStart(4, '0'); 2618 if (spGb === '0051') { 2619 v2ActiveNoticeType = 'RESERVE'; 2620 } else { 2621 v2ActiveNoticeType = (st === 'R') ? 'RESERVE' : 'PERIOD'; 2622 } 2623 $detail.data('cachedSaleType', v2ActiveNoticeType); 2624 $detail.data('saleTypeCode', st); 2625 $detail.data('selectedBkDate', autoSelectedDate); 2626 $detail.data('cartDateApplied', !!autoSelectedDate); 2627 $detail.data('detailData', data); 2628 v2RenderNotice(); 2629 2630 if (spGb === '0051') { 2631 v2InitFacilityDetail(viewKey, data); 2632 } else { 2633 const el = document.getElementById('v2TicketDate_' + viewKey); 2634 if (el) { 2635 flatpickr(el, v2BuildDatePickerOptions({ 2636 position: 'below', 2637 defaultDate: autoSelectedDate || null, 2638 onChange: function(dates, dateStr) { 2639 $('#bookDt').val(dateStr); 2640 v2ShowTicketDateState(viewKey, bookingI18n.loading); 2641 v2CheckOperation(dateStr, viewKey, data, function() { 2642 v2ReloadDetailByDate(displayId, viewKey, partnerYn, dateStr); 2643 }); 2644 } 2645 }, partnerYn)); 2646 } 2647 if (autoSelectedDate) { 2648 $('#bookDt').val(autoSelectedDate); 2649 v2CheckOperation(autoSelectedDate, viewKey, data); 2650 } 2651 } 2652 $detail.data('loaded', true); 2653 2654 $detail.stop(true, true).slideDown(200, function() { 2655 const el = document.getElementById('v2TicketDate_' + viewKey); 2656 if (!autoSelectedDate && window.innerWidth > 768 && el && el._flatpickr) { 2657 el._flatpickr.open(); 2658 } 2659 }); 2660 }, 2661 error: function() { 2662 $detail.html('<div class="alert alert-light">' + bookingI18n.detailFailed + '</div>'); 2663 $detail.data('loaded', true); 2664 $detail.stop(true, true).slideDown(200); 2665 } 2666 }); 2667 return; 2668 } 2669 2670 const cartEntryDate = v2GetEntryTicketCartDate(); 2671 const cachedSaleTypeCode = String($detail.data('saleTypeCode') || summarySaleType || '').toUpperCase();
2672 const cachedSelectedDate = String($detail.data('selectedBkDate') || $('#v2TicketDate_' + viewKey).val() || ''); 2673 if (v2IsDateRequiredSaleType(cachedSaleTypeCode) 2674 && cartEntryDate 2675 && cachedSelectedDate !== cartEntryDate) { 2676 $detail.stop(true, true).slideDown(200); 2677 v2CheckOperation(cartEntryDate, viewKey, $detail.data('detailData') || {}, function() { 2678 v2ReloadDetailByDate(displayId, viewKey, partnerYn, cartEntryDate, true); 2679 }); 2680 return; 2681 } 2682 2683 var cached = $detail.data('cachedSaleType') || ''; 2684 if (cached) { 2685 v2ActiveNoticeType = cached; 2686 v2RenderNotice(); 2687 } 2688 2689 $detail.stop(true, true).slideDown(200, function() { 2690 const el = document.getElementById('v2TicketDate_' + viewKey); 2691 if (window.innerWidth > 768 && el && el._flatpickr) { 2692 el._flatpickr.open(); 2693 } 2694 }); 2695 } 2696 2697 /* ================================================================ 2698 ìì¸ HTML ë¹ë 2699 ================================================================ */ 2700 function v2ToggleMore(btn) { 2701 const $left = $(btn).closest('.detail-left'); 2702 const isExpanded = $left.hasClass('is-expanded'); 2703 2704 if (isExpanded) { 2705 $left.removeClass('is-expanded'); 2706 $(btn).text(bookingI18n.more); 2707 } else { 2708 $left.addClass('is-expanded'); 2709 $(btn).text(bookingI18n.close); 2710 } 2711 } 2712 2713 function v2BuildDetailHtml(data) { 2714 const spGb = String(data.spGbCode || '').padStart(4, '0'); 2715 2716 function buildLeftCol(d, extraHtml) { 2717 const hasImg = d.detailImageUrl && d.detailImageUrl.trim(); 2718 const hasNotice = !!d.noticeHtml; 2719 const hasGuide = !!d.guideHtml; 2720 const isEmpty = !hasImg && !hasNotice && !hasGuide && !extraHtml; 2721 2722 let col = '<div class="detail-left col-7' + (isEmpty ? ' detail-left--empty' : '') + '">'; 2723 if (hasImg) col += '<img class="detail-image" src="' + d.detailImageUrl + '" onerror="if(!this.dataset.fallback){this.dataset.fallback=\'1\';this.src=\'/img/event/251209_nightPocha.jpg\';}" alt="' + bookingI18n.detailImage + '">'; 2724 if (hasNotice) col += '<div class="mb-3">' + d.noticeHtml + '</div>'; 2725 if (hasGuide) col += '<div class="mb-3">' + d.guideHtml + '</div>'; 2726 if (extraHtml) col += extraHtml; 2727 2728 if (!isEmpty) { 2729 col += '<button type="button" class="more-btn" onclick="v2ToggleMore(this)">' + bookingI18n.more + '</button>'; 2730 } 2731 2732 col += '</div>'; 2733 return col; 2734 } 2735 2736 /* ë¶ëìì¤ */ 2737 if (spGb === '0051') { 2738 const did = data.__viewKey || data.displayId; 2739 const spNo = data.spNo || ''; 2740 2741 let right = '<div class="fac-box" id="v2FacBox_' + did + '" data-spno="' + spNo + '" data-spamt="' + (data.price || 0) + '">'; 2742 right += '<div class="fac-row"><label class="fac-label">' + bookingI18n.dateSelect + '</label>' 2743 + '<input type="text" id="v2FacDate_' + did + '" class="form-control fac-date-input" placeholder="' + bookingI18n.datePlaceholder + '"></div>'; 2744 right += '<div class="fac-row v2-facility-place-row" id="v2FacPlaceRow_' + did + '">' 2745 + '<label class="fac-label">' + bookingI18n.seatSelect + '</label>' 2746 + '<select id="v2FacPlace_' + did + '" class="form-control fac-place" onchange="v2ChangeFacilityPlace(\'' + did + '\', this.value)">' 2747 + '<option value="">' + bookingI18n.seatPlaceholder + '</option></select></div>'; 2748 right += '<div class="v2-facility-time-table" id="v2FacTimeTable_' + did + '"></div>'; 2749 right += '<div class="v2-facility-selected-area" id="v2FacSelectedArea_' + did + '"></div>'; 2750 right += '<div id="v2FacHidden_' + did + '"></div>'; 2751 right += '</div>'; 2752 2753 return '<div class="detail-layout row">' + buildLeftCol(data, '') + '<div class="detail-right col-5">' + right + '</div></div>'; 2754 } 2755 2756 /* í°ì¼ */ 2757 const saleType = data.saleType || 'R'; 2758 const childs = data.childProducts || []; 2759 const isSingle = childs.length === 0; 2760 const parent = isSingle && data.__partnerYn === true && String(data.parentPurchasableYn || 'N') === 'Y' ? { 2761 spNo: data.spNo || '', 2762 spName: data.spName || data.displayTitle || '', 2763 spAmt: data.price || 0, 2764 dcType: data.dcType || 0, 2765 dcAmt: data.dcAmt || 0, 2766 sangpumId: data.sangpumId || data.spNo || '', 2767 tkGubun: data.tkGubun || '' 2768 } : null; 2769 const did = data.__viewKey || data.displayId; 2770 const purchaseContext = data.__partnerYn === true ? 'partner' : 'regular'; 2771 const tk = String(data.tkGubun || ''); 2772 2773 const selectedBkDate = data.__selectedBkDate || ''; 2774 const isReserve = v2IsDateRequiredSaleType(saleType); 2775 const shouldShowChildren = !isReserve || !!selectedBkDate; 2776 2777 let right = ''; 2778 2779 if (isReserve) { 2780 right += '<div class="reservation-box">'; 2781 right += '<div class="calendar-wrapper"><label class="fac-label">' + bookingI18n.reservationDateSelect + '</label>' 2782 + '<input type="text" class="form-control detail-date" id="v2TicketDate_' + did + '" placeholder="' + bookingI18n.datePlaceholder + '">' 2783 + '</div></div>'; 2784 } 2785 2786 if (shouldShowChildren) { 2787 right += '<div class="child-list" data-did="' + did + '" data-saletype="' + saleType + '" data-tkgubun="' + tk + '" data-context="' + purchaseContext + '">'; 2788 if (!isSingle) { 2789 childs.forEach(function(c) { 2790 right += v2BuildChildItemHtml(c, null); 2791 }); 2792 } else if (parent) { 2793 right += v2BuildChildItemHtml(parent, null); 2794 } else { 2795 if (isReserve && selectedBkDate) { 2796 right += '<p class="text-center py-3 mb-0">' + bookingI18n.dateNoProduct + '</p>'; 2797 } else { 2798 right += '<p class="mt-4 text-center">' + bookingI18n.productEmpty + '</p>'; 2799 } 2800 } 2801 right += '</div>'; 2802 /* í°ì¼ë´ê¸° ë²í¼ ì ê±° â +/- í´ë¦ ì ì¦ì ì¥ë°êµ¬ë ë°ì */ 2803 } else { 2804 right += '<p class="pkg-info-note pkg-info-note--spaced">' + bookingI18n.dateGuide + '</p>'; 2805 } 2806 2807 return '<div class="detail-layout row">' + buildLeftCol(data) + '<div class="detail-right col-5">' + right + '</div></div>'; 2808 } 2809 2810 function v2BuildChildItemHtml(c, pkgDid) { 2811 const price = c.spAmt || 0; 2812 const dcAmtVal = Number(c.dcAmt || 0); 2813 const dcTypeVal = Number(c.dcType || 0); 2814
2815 let priceHtml = toLocaleString(price); 2816 let resultPrice = price; 2817 let savePrice = 0; 2818 2819 if (dcAmtVal > 0) { 2820 if (dcTypeVal == 1) { 2821 resultPrice = price - dcAmtVal; 2822 savePrice = dcAmtVal; 2823 priceHtml = '<span class="v2-price-original">' + toLocaleString(price) + '</span>' 2824 + ' <span class="v2-price-discounted">' + toLocaleString(resultPrice) + '</span>'; 2825 } else if (dcTypeVal == 2) { 2826 resultPrice = Math.round(price * (1 - dcAmtVal / 100)); 2827 savePrice = dcAmtVal; 2828 priceHtml = '<span class="v2-price-original">' + toLocaleString(price) + '</span>' 2829 + ' <span class="v2-price-discounted">' + toLocaleString(resultPrice) + '</span>'; 2830 } 2831 } 2832 2833 let hiddenHtml = ''; 2834 if (dcAmtVal > 0 && (dcTypeVal == 1 || dcTypeVal == 2) && c.spNo) { 2835 const sId = c.sId || c.sangpumId || c.spNo; 2836 hiddenHtml += '<input type="hidden" class="v2-discount-id" value="' + sId + '">'; 2837 hiddenHtml += '<input type="hidden" class="v2-discount-amt" value="' + dcAmtVal + '">'; 2838 hiddenHtml += '<input type="hidden" class="v2-discount-type" value="' + dcTypeVal + '">'; 2839 hiddenHtml += '<input type="hidden" class="v2-save-price" value="' + savePrice + '">'; 2840 hiddenHtml += '<input type="hidden" class="v2-result-price" value="' + resultPrice + '">'; 2841 } 2842 2843 let qtyHtml; 2844 if (pkgDid != null) { 2845 qtyHtml = '<div class="child-qty pkg-rep-qty">' 2846 + '<button type="button" class="qty-minus" onclick="event.stopPropagation();v2PkgQtyChange(' + pkgDid + ', -1)">-</button>' 2847 + '<span class="qty-display" id="v2PkgQty_' + pkgDid + '">1</span>' 2848 + '<button type="button" class="qty-plus" onclick="event.stopPropagation();v2PkgQtyChange(' + pkgDid + ', 1)">+</button>' 2849 + '</div>'; 2850 } else { 2851 qtyHtml = '<div class="child-qty">' 2852 + '<button type="button" class="qty-minus">-</button>' 2853 + '<span class="qty-display">0</span>' 2854 + '<button type="button" class="qty-plus">+</button>' 2855 + '</div>'; 2856 } 2857 2858 return '<div class="child-item" data-spno="' + (c.spNo || '') + '" data-spamt="' + price + '" data-adtgb="' + (c.adtGb || '') + '">' 2859 + '<p class="child-name">' + (c.spName || '') + '</p>' 2860 + '<div class="option">' 2861 + '<p class="child-price">' + priceHtml + '</p>' 2862 + qtyHtml 2863 + '</div>' 2864 + hiddenHtml 2865 + '</div>'; 2866 } 2867 2868 /* ================================================================ 2869 ë¶ëìì¤ ë ì§/ì리/ìê° ì¡°í 2870 ================================================================ */ 2871 function v2InitFacilityDetail(did, data) { 2872 const el = document.getElementById('v2FacDate_' + did); 2873 if (!el) return; 2874 flatpickr(el, v2BuildDatePickerOptions({ 2875 onChange: function(dates, dateStr) { 2876 selectedDate = dateStr; 2877 $('#bookDt').val(dateStr); 2878 2879 if (!v2CanAddCartDate(dateStr)) { 2880 const elC = document.getElementById('v2FacDate_' + did); 2881 if (elC && elC._flatpickr) elC._flatpickr.clear(); 2882 selectedDate = v2GetCartBookingDate() || ''; 2883 $('#bookDt').val(selectedDate); 2884 return; 2885 } 2886 2887 v2CheckOperationFacility(dateStr, did, function() { 2888 v2SearchFacilityDirect(did, dateStr); 2889 }); 2890 } 2891 }, false)); 2892 } 2893 2894 function v2SearchFacilityDirect(did, bkDate) { 2895 const $placeRow = $('#v2FacPlaceRow_' + did); 2896 const $timeDiv = $('#v2FacTimeTable_' + did); 2897 const $hidden = $('#v2FacHidden_' + did); 2898 2899 $placeRow.hide(); 2900 $timeDiv.html(''); 2901 $hidden.html(''); 2902 2903 const cardSpNo = $('#v2FacBox_' + did).attr('data-spno') || ''; 2904 2905 $.ajax({
2906 url: '/hBook/spSearch2/' + agentCode + '/' + bkDate, 2907 type: 'get', dataType: 'json', 2908 success: function(data) { 2909 v2SyncFacilityCardPrices(data); 2910 if (!data || !data.length) { 2911 $timeDiv.html('<p class="text-center py-3 mb-0">' + bookingI18n.dateNoProduct + '</p>'); 2912 return; 2913 } 2914 const sp = data.find(function(d){ return String(d.spNo) === String(cardSpNo); }); 2915 if (checkIsToday(bkDate)) { 2916 v2SetFacilityCardPriceMessage(cardSpNo, bookingI18n.todayUnavailable); 2917 $timeDiv.html('<p class="text-center py-3 mb-0">' + bookingI18n.todayUnavailable + '</p>'); 2918 return; 2919 } 2920 if (!sp) { 2921 v2SetFacilityCardPriceMessage(cardSpNo, bookingI18n.dateUnavailable); 2922 console.error('[V2][Facility] sp ë§¤ì¹ ì¤í¨ â cardSpNoê° ìëµì ìì', { cardSpNo: cardSpNo, responseSpNos: data.map(function(d){ return d.spNo; }) }); 2923 $timeDiv.html('<p class="text-center py-3 mb-0">' + bookingI18n.dateNoProduct + '</p>'); 2924 return; 2925 } 2926 const spNo = sp.spNo; 2927 const spAmt = Number(sp.spAmt || 0); 2928 const dcAmt = Number(sp.dcAmt || 0); 2929 const dcType= Number(sp.dcType || 0); 2930 const sId = sp.sangpumId; 2931 2932 let hiddenHtml = '<input type="hidden" id="v2amt_' + did + '_' + spNo + '" value="' + spAmt + '">'; 2933 if (dcAmt > 0 && sId) { 2934 const dcResult = calc(spAmt, String(dcType), dcAmt); 2935 hiddenHtml += '<input type="hidden" id="v2sId_' + did + '_' + spNo + '" value="' + sId + '">'; 2936 hiddenHtml += '<input type="hidden" id="v2dcAmt_' + did + '_' + spNo + '" value="' + dcAmt + '">'; 2937 hiddenHtml += '<input type="hidden" id="v2dcType_' + did + '_' + spNo + '" value="' + dcType + '">'; 2938 hiddenHtml += '<input type="hidden" id="v2savePrice_' + did + '_' + spNo + '" value="' + dcResult.savePrice + '">'; 2939 hiddenHtml += '<input type="hidden" id="v2resultPrice_'+ did + '_' + spNo + '" value="' + dcResult.resultPrice + '">'; 2940 } 2941 $hidden.html(hiddenHtml); 2942 2943 let $spSel = $('#v2FacSp_' + did); 2944 // ì¥ë°êµ¬ë í기ë ì´ ìµì í ì¤í¸ë¥¼ ê·¸ëë¡ ì°ë¯ë¡, spSearch2ì íê¸ 2945 // ì´ë¦ ëì ë²ìë ìíëª ì ì°ì ì¬ì© (v2RelocalizeFacilityCartItem ê³¼ ëì¼ ê·ì¹) 2946 const facLocalized = v2FacilityDataMap[String(spNo)] || {}; 2947 const facOptionName = String(facLocalized.spName || facLocalized.displayTitle || '').trim() 2948 || sp.spName || spNo; 2949 if (!$spSel.length) { 2950 $hidden.append('<select id="v2FacSp_' + did + '" class="v2-hidden-select"><option value="' + spNo + '" selected>' + facOptionName + '</option></select>'); 2951 } else { 2952 $spSel.html('<option value="' + spNo + '" selected>' + facOptionName + '</option>'); 2953 } 2954 v2ChangeFacility(did, spNo); 2955 }, 2956 error: function() { 2957 v2SetAllFacilityCardPriceMessages(bookingI18n.priceFailed); 2958 $timeDiv.html('<p class="text-center py-3 mb-0">' + bookingI18n.loadError + '</p>'); 2959 } 2960 }); 2961 } 2962 function checkIsToday(someDate) { 2963 const date = new Date(someDate); 2964 const today = new Date(); 2965 return ( 2966 date.getFullYear() === today.getFullYear() && 2967 date.getMonth() === today.getMonth() && 2968 date.getDate() === today.getDate() 2969 ); 2970 } 2971 function v2ChangeFacility(did, spNo) { 2972 selectedSpNo = spNo; 2973 const $placeRow = $('#v2FacPlaceRow_' + did); 2974 const $placeSel = $('#v2FacPlace_' + did); 2975 const $timeDiv = $('#v2FacTimeTable_' + did); 2976 2977 $timeDiv.html(''); 2978 $placeRow.hide(); 2979 $placeSel.html('<option value="">' + bookingI18n.seatPlaceholder + '</option>'); 2980 2981 $.ajax({
2982 url: '/hBook/spPlaceSearch/' + spNo, 2983 type: 'get', dataType: 'json', 2984 success: function(data) { 2985 if (!data || !data.length) { 2986 $placeSel.html('<option value="">' + bookingI18n.seatEmpty + '</option>'); 2987 $placeRow.show(); 2988 return; 2989 } 2990 if (data.length === 1) { 2991 selectedCavanaType = 'V'; 2992 $placeSel.html('<option value="' + data[0].cdCode + '">' + data[0].cdTitle1 + '</option>'); 2993 v2ChangeFacilityPlace(did, data[0].cdCode); 2994 } else { 2995 selectedCavanaType = 'C'; 2996 let opts = '<option value="">' + bookingI18n.seatPlaceholder + '</option>'; 2997 data.forEach(function(d) { opts += '<option value="' + d.cdCode + '">' + d.cdTitle1 + '</option>'; }); 2998 $placeSel.html(opts); 2999 $placeRow.show(); 3000 } 3001 }, 3002 error: function() { $placeSel.html('<option value="">' + bookingI18n.error + '</option>'); $placeRow.show(); } 3003 }); 3004 } 3005 3006 /* ââ ë¶ëìì¤ ìê° ì í: select ë°©ì ââ */ 3007 function v2ChangeFacilityPlace(did, placeNo) { 3008 selectedPlaceNo = placeNo; 3009 if (!placeNo || !selectedDate) return; 3010 3011 const bkDay = selectedDate.replace(/-/g, ''); 3012 const $timeDiv = $('#v2FacTimeTable_' + did); 3013 3014 v2InitTimeBox(did); 3015 $timeDiv.html('<p class="text-muted v2-time-feedback">' + bookingI18n.timeLoading + '</p>'); 3016 3017 $.ajax({ 3018 url: '/hBook/spMarkSearch/' + bkDay + '/' + selectedSpNo + '/' + placeNo, 3019 type: 'post', dataType: 'json', contentType: 'application/json; charset=utf-8', 3020 success: function(data) { 3021 if (!data || !data.length) { $timeDiv.html('<p class="text-muted v2-time-feedback">' + bookingI18n.timeEmpty + '</p>'); return; } 3022 3023 function toHHmm(t) { let s = String(t || '').replace(':',''); while(s.length<4)s='0'+s; return s.slice(0,2)+':'+s.slice(2,4); } 3024 function addMins(hhmm, m) { const p=hhmm.split(':'); const tot=+p[0]*60+ +p[1]+(m||240); const h=Math.floor(tot/60)%24; const mm=tot%60; return (h<10?'0':'')+h+':'+(mm<10?'0':'')+mm; } 3025 3026 let opts = '<option value="">' + bookingI18n.timePlaceholder + '</option>'; 3027 let hiddenTimeTxt = ''; 3028 data.forEach(function(raw) { 3029 const bkTime = String(raw.bkTime || raw.bk_time || ''); 3030 let strtTm = raw.strtTm || raw.strt_tm || ''; 3031 let endTm = raw.endTm || raw.end_tm || ''; 3032 if (!strtTm && bkTime) strtTm = toHHmm(bkTime); 3033 if (!endTm && strtTm) endTm = addMins(strtTm, parseInt(raw.bkUseTime || raw.bk_use_time || 240, 10)); 3034 const tmTxt = strtTm ? strtTm + '~' + endTm : bkTime; 3035 const bkState = raw.bkState || raw.bk_state || 'Y'; 3036 3037 hiddenTimeTxt += '<input type="hidden" id="timeTxt_' + bkTime + '" value="' + tmTxt + '">'; 3038 3039 if (bkState === 'Y') { 3040 opts += '<option value="' + bkTime + '">' + tmTxt + ' (' + bookingI18n.timeAvailable + ')</option>'; 3041 } else { 3042 opts += '<option value="' + bkTime + '" disabled>' + tmTxt + ' (' + (bkState === 'B' ? bookingI18n.timeOnsite : bookingI18n.timeClosed) + ')</option>'; 3043 } 3044 }); 3045 3046 $('#v2FacHidden_' + did).append(hiddenTimeTxt); 3047 3048 $timeDiv.html( 3049 '<label class="fac-label">' + bookingI18n.timeSelect + '</label>' + 3050 '<select id="v2FacTime_' + did + '" class="form-control fac-time-select" onchange="v2TimeSelectChange(\'' + did + '\', this.value)">' + opts + '</select>' 3051 ); 3052 }, 3053 error: function() { $timeDiv.html('<p class="text-muted">' + bookingI18n.timeError + '</p>'); } 3054 }); 3055 } 3056 3057 /* ================================================================ 3058 ìê° ì í/í´ì (select ë¨ì¼ì í ë°©ì) 3059 ================================================================ */ 3060 function v2InitTimeBox(did) { 3061 var $sel = $('#v2FacTime_' + did); 3062 if ($sel.length) $sel.val(''); 3063 v2SelectedTimeList = ''; 3064 v2SelectedCavanaAmt = 0; 3065 v2SelectedCavanaTotAmt = 0; 3066 } 3067 3068 function v2FacilityHoldPayload(item) { 3069 return { 3070 spNo: item.spNo, 3071 bkSeq: item.bkSeq, 3072 bkTimeList: item.bkTimeList, 3073 bkDate: item.bkDate, 3074 cavanaYn: 'Y' 3075 }; 3076 } 3077 3078 function v2SetFacilityControlBusy(did, busy) { 3079 if (!did) return; 3080 $('#v2FacSp_' + did + ', #v2FacPlace_' + did + ', #v2FacTime_' + did + ', #v2FacDate_' + did) 3081 .prop('disabled', !!busy); 3082 } 3083
3084 function v2AcquireFacilityHold(item, onSuccess, onFailure) { 3085 const identity = v2EnsureCheckoutIdentity(); 3086 v2FacilityHoldRequestCount++; 3087 $.ajax({ 3088 url: '/hBook/v2/facility/hold/' + encodeURIComponent(agentCode) 3089 + '/' + encodeURIComponent(item.bkDate) 3090 + '/' + encodeURIComponent(identity.serialNo), 3091 type: 'post', 3092 dataType: 'json', 3093 contentType: 'application/json; charset=utf-8', 3094 timeout: 10000, 3095 data: JSON.stringify(v2FacilityHoldPayload(item)), 3096 success: function(data) { 3097 if (!data || data.code !== '0000' || !Number(data.expiresAtEpochMs)) { 3098 if (typeof onFailure === 'function') onFailure(data && data.message ? data.message : bookingI18n.facilityHoldFailed); 3099 return; 3100 } 3101 if (Number(data.serverNowEpochMs)) { 3102 v2FacilityHoldClockOffsetMs = Number(data.serverNowEpochMs) - Date.now(); 3103 } 3104 if (typeof onSuccess === 'function') onSuccess(data, identity); 3105 }, 3106 error: function() { 3107 if (typeof onFailure === 'function') onFailure(bookingI18n.facilityHoldError); 3108 }, 3109 complete: function() { 3110 v2FacilityHoldRequestCount = Math.max(0, v2FacilityHoldRequestCount - 1); 3111 } 3112 }); 3113 } 3114 3115 function v2ReleaseFacilityHold(item, onSuccess, onFailure) { 3116 const serialNo = item && item.holdSerialNo ? item.holdSerialNo : v2CheckoutSerialNo; 3117 if (!item || !serialNo) { 3118 if (typeof onSuccess === 'function') onSuccess({ code: '0000', releasedCount: 0 }); 3119 return; 3120 } 3121 3122 v2FacilityHoldRequestCount++; 3123 $.ajax({ 3124 url: '/hBook/v2/facility/hold/release/' + encodeURIComponent(agentCode) 3125 + '/' + encodeURIComponent(item.bkDate) 3126 + '/' + encodeURIComponent(serialNo), 3127 type: 'post', 3128 dataType: 'json', 3129 contentType: 'application/json; charset=utf-8', 3130 timeout: 10000, 3131 data: JSON.stringify(v2FacilityHoldPayload(item)), 3132 success: function(data) { 3133 if (!data || data.code !== '0000') { 3134 if (typeof onFailure === 'function') onFailure(data && data.message ? data.message : bookingI18n.facilityReleaseFailed); 3135 return; 3136 } 3137 if (typeof onSuccess === 'function') onSuccess(data); 3138 }, 3139 error: function() { 3140 if (typeof onFailure === 'function') onFailure(bookingI18n.facilityReleaseError); 3141 }, 3142 complete: function() { 3143 v2FacilityHoldRequestCount = Math.max(0, v2FacilityHoldRequestCount - 1); 3144 } 3145 }); 3146 } 3147 3148 function v2ReleaseAllFacilityHolds(done) { 3149 const holds = v2CBookList.slice(); 3150 if (!holds.length) { 3151 if (typeof done === 'function') done(); 3152 return; 3153 } 3154 3155 let remaining = holds.length; 3156 const finishOne = function() { 3157 remaining--; 3158 if (remaining <= 0 && typeof done === 'function') done(); 3159 }; 3160 holds.forEach(function(item) { 3161 v2ReleaseFacilityHold(item, finishOne, finishOne); 3162 }); 3163 } 3164 3165 function v2FacilityHoldNow() { 3166 return Date.now() + v2FacilityHoldClockOffsetMs; 3167 } 3168 3169 function v2FormatFacilityHoldTime(milliseconds) { 3170 const totalSeconds = Math.max(0, Math.ceil(milliseconds / 1000)); 3171 const minutes = Math.floor(totalSeconds / 60); 3172 const seconds = totalSeconds % 60; 3173 return (minutes < 10 ? '0' : '') + minutes + ':' + (seconds < 10 ? '0' : '') + seconds; 3174 } 3175 3176 function v2SyncFacilityHoldTimer() { 3177 const active = v2CBookList.filter(function(item) { 3178 return Number(item.holdExpiresAtEpochMs) > 0; 3179 }); 3180 const hasHold = active.length > 0; 3181 3182 $('#v2FacilityHoldStatus, #v2MobileFacilityHoldStatus').toggleClass('is-active', hasHold); 3183 $('#v2MobileBar').toggleClass('has-facility-hold', hasHold); 3184 $('.hBook.v2-book-main').toggleClass('facility-hold-active', hasHold); 3185 3186 if (!hasHold) { 3187 if (v2FacilityHoldTimerId) window.clearInterval(v2FacilityHoldTimerId); 3188 v2FacilityHoldTimerId = null; 3189 return; 3190 } 3191 3192 v2UpdateFacilityHoldTimer(); 3193 if (!v2FacilityHoldTimerId) { 3194 v2FacilityHoldTimerId = window.setInterval(v2UpdateFacilityHoldTimer, 1000); 3195 } 3196 } 3197
3198 function v2UpdateFacilityHoldTimer() { 3199 const expiries = v2CBookList.map(function(item) { 3200 return Number(item.holdExpiresAtEpochMs) || 0; 3201 }).filter(function(value) { return value > 0; }); 3202 if (!expiries.length) { 3203 v2SyncFacilityHoldTimer(); 3204 return; 3205 } 3206 3207 const remaining = Math.min.apply(null, expiries) - v2FacilityHoldNow(); 3208 const text = v2FormatFacilityHoldTime(remaining); 3209 $('#v2FacilityHoldCountdown, #v2MobileFacilityHoldCountdown').text(text); 3210 $('#v2FacilityHoldStatus, #v2MobileFacilityHoldStatus').toggleClass('is-urgent', remaining <= 5 * 60 * 1000); 3211 3212 if (remaining <= 0) v2HandleFacilityHoldExpiry(); 3213 } 3214 3215 function v2HasExpiredFacilityHold() { 3216 const now = v2FacilityHoldNow(); 3217 return v2CBookList.some(function(item) { 3218 return Number(item.holdExpiresAtEpochMs) > 0 && Number(item.holdExpiresAtEpochMs) <= now; 3219 }); 3220 } 3221 3222 function v2HandleFacilityHoldExpiry() { 3223 if (v2FacilityHoldExpiryHandling) return; 3224 if (v2NextInProgress) return; 3225 if (v2FacilityHoldRequestCount > 0) return; 3226 const now = v2FacilityHoldNow(); 3227 const expired = v2CBookList.filter(function(item) { 3228 return Number(item.holdExpiresAtEpochMs) > 0 && Number(item.holdExpiresAtEpochMs) <= now; 3229 }); 3230 if (!expired.length) return; 3231 3232 v2FacilityHoldExpiryHandling = true; 3233 expired.forEach(function(item) { 3234 v2ReleaseFacilityHold(item); 3235 v2RemoveCavanaLocal(item.bookIdx); 3236 }); 3237 v2FacilityHoldExpiryHandling = false; 3238 v2SyncFacilityHoldTimer(); 3239 const holdMinutes = Number(expired[0].holdMinutes) || 20; 3240 v2SwalWarning(bookingI18n.facilityHoldExpiredDescription.replace('{0}', holdMinutes), bookingI18n.holdExpiredTitle); 3241 } 3242 3243 function v2ShowFacilityHoldNotice(holdMinutes) { 3244 if (v2FacilityHoldNoticeShown) return; 3245 v2FacilityHoldNoticeShown = true; 3246 const minutes = Number(holdMinutes) > 0 ? Number(holdMinutes) : 20; 3247 Swal.fire({ 3248 icon: 'info', 3249 title: bookingI18n.holdConfirmTitle, 3250 html: bookingI18n.holdConfirmDescription.replace('{0}', minutes), 3251 confirmButtonText: bookingI18n.confirm, 3252 confirmButtonColor: '#ff710d', 3253 heightAuto: false 3254 }); 3255 } 3256 3257 function v2TimeSelectChange(did, selectedTime) { 3258 if (!selectedTime) { 3259 v2RemoveFacilitySelection(did); 3260 return; 3261 } 3262 3263 if (!v2RequireLoginBeforeCart()) { 3264 $('#v2FacTime_' + did).val(''); 3265 return; 3266 } 3267 3268 const time = getTimeFormat(selectedTime); 3269 const $spSel = $('#v2FacSp_' + did); 3270 const spNo = $spSel.val(); 3271 if (!spNo) return; 3272 3273 const spName = $spSel.find('option:selected').text(); 3274 const $placeSel = $('#v2FacPlace_' + did); 3275 const placeNo = $placeSel.val() || ''; 3276 const placeName = selectedCavanaType === 'V' ? '' : ($placeSel.find('option:selected').text() || ''); 3277 const spPlaceName = selectedCavanaType === 'V' ? spName : (spName + ' / ' + placeName); 3278 const spPlaceValue = spNo + placeNo; 3279 const tkAmt = Number($('#v2amt_' + did + '_' + spNo).val()) || 0; 3280 3281 if (!v2CanAddCartDate(selectedDate || '')) { 3282 $('#v2FacTime_' + did).val(''); 3283 return; 3284 } 3285 3286 // hidden ë기í 3287 const $fh = $('#v2FacHidden_' + did); 3288 if (!$('#amt_' + spNo).length) $fh.append('<input type="hidden" id="amt_' + spNo + '" value="' + tkAmt + '">'); 3289 else $('#amt_' + spNo).val(tkAmt); 3290 const rp = $('#v2resultPrice_' + did + '_' + spNo).val(); 3291 const sp = $('#v2savePrice_' + did + '_' + spNo).val(); 3292 const dc = $('#v2sId_' + did + '_' + spNo).val() || ''; 3293 const da = $('#v2dcAmt_' + did + '_' + spNo).val(); 3294 const dt = $('#v2dcType_' + did + '_' + spNo).val(); 3295 if (rp && !$('#resultPrice_' + spNo).length) { 3296 $fh.append('<input type="hidden" id="resultPrice_' + spNo + '" value="' + rp + '">'); 3297 $fh.append('<input type="hidden" id="savePrice_' + spNo + '" value="' + sp + '">'); 3298 $fh.append('<input type="hidden" id="sId_' + spNo + '" value="' + dc + '">'); 3299 $fh.append('<input type="hidden" id="dcAmt_' + spNo + '" value="' + da + '">'); 3300 $fh.append('<input type="hidden" id="dcType_' + spNo + '" value="' + dt + '">'); 3301 } 3302 3303 // ê°ì ì리ë í ìê°ë§ ì ì§íë, ì ìê° ì ì ì±ê³µ í 기존 ì ì ì í´ì íë¤. 3304 const existIdx = v2CBookList.findIndex(function(k){ return k.spPlaceName === spPlaceName; }); 3305 const old = existIdx >= 0 ? v2CBookList[existIdx] : null; 3306 const candidate = { 3307 did: did, 3308 spNo: spNo, 3309 bkSeq: placeNo, 3310 bkTimeList: time, 3311 bkDate: selectedDate || '', 3312 cavanaYn: 'Y' 3313 }; 3314 3315 v2SetFacilityControlBusy(did, true); 3316 v2AcquireFacilityHold(candidate, function(data, identity) { 3317 candidate.holdSerialNo = identity.serialNo; 3318 const sameSlot = old 3319 && String(old.spNo) === String(spNo) 3320 && String(old.bkSeq) === String(placeNo) 3321 && String(old.bkTimeList) === String(time); 3322 3323 if (sameSlot) { 3324 old.holdExpiresAtEpochMs = Number(data.expiresAtEpochMs); 3325 old.holdMinutes = Number(data.holdMinutes) || 20; 3326 old.holdSerialNo = identity.serialNo; 3327 v2SetFacilityControlBusy(did, false); 3328 v2SyncFacilityHoldTimer(); 3329 v2PersistCartState(); 3330 v2ShowFacilityHoldNotice(data.holdMinutes); 3331 return; 3332 } 3333 3334 const commitSelection = function() { 3335 if (old) v2RemoveCavanaLocal(old.bookIdx, true); 3336 v2BookIdx++; 3337 v2CavanaDiv('1', v2BookIdx, spPlaceValue, spNo, placeNo, spName, spPlaceName, 3338 tkAmt, time, did, Number(data.expiresAtEpochMs), Number(data.holdMinutes), identity.serialNo); 3339 $('#v2FacTime_' + did).val(selectedTime); 3340 v2SetFacilityControlBusy(did, false); 3341 v2ShowFacilityHoldNotice(data.holdMinutes); 3342 }; 3343 3344 if (!old) { 3345 commitSelection(); 3346 return; 3347 } 3348 3349 v2ReleaseFacilityHold(old, commitSelection, function(message) { 3350 // 기존 ìê° í´ì ì¤í¨ ì ì ì ì ì ë³´ì í´ì íê³ íë©´ ì íë ìë ê°ì¼ë¡ ëë린ë¤. 3351 v2ReleaseFacilityHold(candidate); 3352 $('#v2FacTime_' + did).val(old.bkTimeList || ''); 3353 v2SetFacilityControlBusy(did, false); 3354 v2SwalError(message, bookingI18n.facilityTimeChangeError); 3355 }); 3356 }, function(message) { 3357 // ìëµ íììì ì§ì ì ìë² ì ì ì´ ëë ê²½ì°ë¥¼ ëë¹í´ 미íì ì ì ì ì 리íë¤. 3358 const sameAsOld = old 3359 && String(old.spNo) === String(candidate.spNo) 3360 && String(old.bkSeq) === String(candidate.bkSeq) 3361 && String(old.bkTimeList) === String(candidate.bkTimeList); 3362 if (!sameAsOld) v2ReleaseFacilityHold(candidate); 3363 $('#v2FacTime_' + did).val(old ? old.bkTimeList : ''); 3364 v2SetFacilityControlBusy(did, false); 3365 v2SwalError(message, bookingI18n.facilityHoldFailed); 3366 }); 3367 } 3368 3369 function v2RemoveFacilitySelection(did) { 3370 const $spSel = $('#v2FacSp_' + did); 3371 const spNo = $spSel.val(); 3372 const $placeSel = $('#v2FacPlace_' + did); 3373 const placeNo = $placeSel.val() || ''; 3374 const placeName = selectedCavanaType === 'V' ? '' : ($placeSel.find('option:selected').text() || ''); 3375 const spName = $spSel.find('option:selected').text(); 3376 const spPlaceName = selectedCavanaType === 'V' ? spName : (spName + ' / ' + placeName); 3377 3378 const existIdx = v2CBookList.findIndex(function(k){ return k.spPlaceName === spPlaceName; }); 3379 if (existIdx < 0) return; 3380 v2DeleteCavana(v2CBookList[existIdx].bookIdx); 3381 } 3382 3383 function v2BuildSpPlaceName(spPlaceName, bkTimeList) { 3384 if (!bkTimeList) return spPlaceName; 3385 let txt = '';
3386 bkTimeList.split(',').sort().forEach(function(t) { 3387 const label = $('#timeTxt_' + t).val() || t; 3388 txt += (txt ? ' , ' : ' / ') + label; 3389 }); 3390 return spPlaceName + txt; 3391 } 3392 3393 /* ================================================================ 3394 í°ì¼/ë¶ëìì¤ í¼í© ë´ê¸° ê²ì¦ í¬í¼ 3395 ================================================================ */ 3396 function v2HasSaleType(type) { 3397 var t = String(type || '').toUpperCase(); 3398 return v2BookList.some(function(item) { 3399 return String(item.saleType || '').toUpperCase() === t; 3400 }); 3401 } 3402 3403 function v2IsDateRequiredSaleType(saleType) { 3404 return String(saleType || '').toUpperCase() === 'R'; 3405 } 3406 3407 function v2IsNoDateSaleType(saleType) { 3408 var st = String(saleType || '').toUpperCase(); 3409 return st === 'P' || st === 'E' || st === 'S'; 3410 } 3411 3412 function v2HasMixedDatePolicy(nextSaleType) { 3413 var next = String(nextSaleType || '').toUpperCase(); 3414 3415 if (v2IsDateRequiredSaleType(next)) { 3416 return v2HasSaleType('P') || v2HasSaleType('E') || v2HasSaleType('S'); 3417 } 3418 3419 if (v2IsNoDateSaleType(next)) { 3420 return v2HasSaleType('R'); 3421 } 3422 3423 return false; 3424 } 3425 3426 function v2GetCartBookingDate() { 3427 var allItems = v2BookList.concat(v2CBookList); 3428 for (var i = 0; i < allItems.length; i++) { 3429 if (allItems[i] && allItems[i].bkDate) return allItems[i].bkDate; 3430 } 3431 return ''; 3432 } 3433 3434 function v2GetEntryTicketCartDate() { 3435 for (var i = 0; i < v2BookList.length; i++) { 3436 var item = v2BookList[i]; 3437 if (item && v2IsDateRequiredSaleType(item.saleType) && item.bkDate) { 3438 return item.bkDate; 3439 } 3440 } 3441 return ''; 3442 } 3443 3444 function v2ResetAutoAppliedEntryTicketDates() { 3445 if (v2GetEntryTicketCartDate()) return; 3446 3447 $('.product-detail').each(function() { 3448 var $detail = $(this); 3449 if (!$detail.data('cartDateApplied')) return; 3450 3451 $detail.stop(true, true).hide().empty(); 3452 $detail.removeData('loaded'); 3453 $detail.removeData('cachedSaleType'); 3454 $detail.removeData('saleTypeCode'); 3455 $detail.removeData('selectedBkDate'); 3456 $detail.removeData('cartDateApplied'); 3457 $detail.removeData('detailData'); 3458 $detail.closest('.product-item').removeClass('is-open'); 3459 }); 3460 3461 $('#bookDt').val(v2GetCartBookingDate() || ''); 3462 } 3463 3464 function v2CanAddCartDate(nextDate) { 3465 if (!nextDate) return true; 3466 var existingDate = v2GetCartBookingDate(); 3467 if (!existingDate || existingDate === nextDate) return true; 3468 3469 Swal.fire({ 3470 icon: 'info', 3471 title: bookingI18n.dateCheckTitle, 3472 text: bookingI18n.dateCheckDescription, 3473 confirmButtonText: bookingI18n.confirm, 3474 confirmButtonColor: '#ff710d', 3475 heightAuto: false 3476 }); 3477 return false; 3478 } 3479 3480 /* ================================================================ 3481 í°ì¼ ë´ê¸° / ìë / ìì 3482 ================================================================ */ 3483 function v2TicketDivCnt(idx, spName, tkOriAmt, tkAmt, dcCode, dcType, dcValue, quantity) { 3484 dcType = String(dcType || '0'); 3485 let html = '<div class="border-bottom pb-4 mb-4 li_' + idx + '">'; 3486 html += '<p class="badge badge-info"><i class="bi bi-x-circle" onclick="v2DeleteTicket(' + idx + ')"></i><span>' + spName + '</span></p>'; 3487 html += '<div class="option">'; 3488 if (tkOriAmt > tkAmt && tkAmt > 0) { 3489 const totalSaving = (tkOriAmt - tkAmt) * quantity; 3490 html += '<div>'; 3491 html += '<p class="option_name_sale" id="selTotAmt_' + idx + '">' + toLocaleString(tkOriAmt * quantity) + '</p>'; 3492 html += '<p class="option_name2 mb-0" id="selDcAmt_' + idx + '"> (-' + toLocaleString(totalSaving) + ' ' + bookingI18n.discountSuffix + ') </p>'; 3493 html += '</div>'; 3494 html += '<p class="option_name" id="selDcTotAmt_' + idx + '">
3494' + toLocaleString(tkAmt * quantity) + '</p>'; 3495 } else { 3496 html += '<p class="option_name" id="spPlaceAmt_' + idx + '">' + toLocaleString(tkAmt * quantity) + '</p>'; 3497 } 3498 html += '</div>'; 3499 html += '<div class="ticket_qty">'; 3500 html += '<button type="button" class="qty-minus" onclick="v2ChangeTicketQty(' + idx + ',\'minus\')">-</button>'; 3501 html += '<span class="qty_' + idx + '">' + quantity + '</span>'; 3502 html += '<button type="button" class="qty-plus" onclick="v2ChangeTicketQty(' + idx + ',\'plus\')">+</button>'; 3503 html += '</div></div>'; 3504 $('.selectedTicket1').after(html); 3505 $('#v2EmptyMsg').hide(); 3506 3507 if (window.innerWidth <= 768) { 3508 $('#v2MobileEmptyMsg').hide(); 3509 $('#v2MobileTotalInfo').show(); 3510 } 3511 } 3512 3513 function v2AddTicketSelection(did) { 3514 if (!v2RequireLoginBeforeCart()) { 3515 return; 3516 } 3517 3518 const $btn = $('button[data-did="' + did + '"]'); 3519 const saleType = String($btn.data('saletype') || 'R').toUpperCase(); 3520 const tkGubun = String($btn.data('tkgubun') || ''); 3521 const purchaseContext = String($('#v2-detail-' + did).find('.child-list').data('context') || 'regular'); 3522 const isReserve = v2IsDateRequiredSaleType(saleType); 3523 const bkDate = $('#v2TicketDate_' + did).val() || ''; 3524 if (isReserve && !bkDate) { v2SwalInfo(bookingI18n.dateFirst, bookingI18n.selectionRequired); return; } 3525 3526 if (v2HasMixedDatePolicy(saleType)) { 3527 Swal.fire({ 3528 icon: 'info', title: bookingI18n.cartUnavailable, 3529 html: bookingI18n.cartMixInvalid, 3530 confirmButtonText: bookingI18n.confirm, confirmButtonColor: '#ff710d', heightAuto: false 3531 }); 3532 return; 3533 } 3534 3535 if (bkDate && !v2CanAddCartDate(bkDate)) { 3536 return; 3537 } 3538 3539 { 3540 const selected = []; 3541 $('#v2-detail-' + did).find('.child-item').each(function() { 3542 const qty = parseInt($(this).find('.qty-display').text(), 10) || 0; 3543 if (qty > 0) selected.push({ $item: $(this), spNo: $(this).data('spno') || '', spAmt: Number($(this).data('spamt')) || 0, spName: $(this).find('.child-name').text().trim(), qty }); 3544 }); 3545 if (!selected.length) { v2SwalInfo(bookingI18n.quantityFirst, bookingI18n.selectionRequired); return; } 3546 3547 selected.forEach(function(item) { 3548 const dupIdx = v2BookList.findIndex(function(b){ return b.spNo === item.spNo && b.bkDate === bkDate && String(b.purchaseContext || 'regular') === purchaseContext; }); 3549 const dcCode = item.$item.find('.v2-discount-id').val() || ''; 3550 const dcAmt = Number(item.$item.find('.v2-discount-amt').val()) || 0; 3551 const dcType = item.$item.find('.v2-discount-type').val() || '0'; 3552 const savePrice = Number(item.$item.find('.v2-save-price').val()) || 0; 3553 const resultPrice = Number(item.$item.find('.v2-result-price').val()) || item.spAmt; 3554 const hasDiscount = resultPrice > 0 && resultPrice < item.spAmt; 3555 const finalAmt = hasDiscount ? resultPrice : item.spAmt; 3556 3557 if (dupIdx >= 0) { 3558 const ex = v2BookList[dupIdx]; 3559 ex.qty += item.qty; ex.dcSpAmt = finalAmt; ex.saleType = ex.saleType || saleType; 3560 v2TotalAmt += finalAmt * item.qty; v2OriTotAmt += item.spAmt * item.qty; 3561 $('.qty_' + ex.bookIdx).html(ex.qty); 3562 if (hasDiscount) { 3563 $('#selTotAmt_' + ex.bookIdx).text(toLocaleString(item.spAmt * ex.qty)); 3564 $('#selDcTotAmt_' + ex.bookIdx).text(toLocaleString(finalAmt * ex.qty)); 3565 $('#selDcAmt_' + ex.bookIdx).text('(-' + toLocaleString((item.spAmt - finalAmt) * ex.qty) + ' ' + bookingI18n.discountSuffix + ')'); 3566 } 3567 else { $('#spPlaceAmt_' + ex.bookIdx).text(toLocaleStr
3567ing(finalAmt * ex.qty)); } 3568 } else { 3569 v2BookIdx++; 3570 v2TotalAmt += finalAmt * item.qty; v2OriTotAmt += item.spAmt * item.qty; 3571 const entry = { bookIdx: v2BookIdx, displayId: String(did).replace(/^(regular|partner)-/, ''), spNo: item.spNo, spName: item.spName, spAmt: item.spAmt, dcSpAmt: finalAmt, qty: item.qty, bkDate: bkDate, saleType: saleType, purchaseContext: purchaseContext, partnerCode: purchaseContext === 'partner' ? pCode : '', discountTarget: mbNo ? 'M' : 'G', cavanaYn: 'N', dayPkgGb: 0, tkGubun: tkGubun, dcType: dcType, dcValue: dcAmt }; 3572 if (hasDiscount) { entry.dcCode = dcCode; entry.dcAmt = savePrice; entry.dcYn = 'Y'; } 3573 v2BookList.push(entry); 3574 v2TicketDivCnt(v2BookIdx, item.spName + (bkDate ? ' (' + bkDate + ')' : ''), item.spAmt, finalAmt, hasDiscount, dcType, dcAmt, item.qty); 3575 } 3576 3577 v2ResetTicketQtyDisplay(item.spNo, purchaseContext); 3578 }); 3579 } 3580 3581 $('#bookDt').val(bkDate); 3582 $('.totalAmtInfo').css('display', 'flex'); 3583 $('.btn-next').show(); 3584 $('.totalAmt').html(toLocaleString(v2TotalAmt)); 3585 v2SyncToV1(); 3586 v2RenderNotice(); 3587 } 3588 3589 function v2SyncChildItemToCart($item) { 3590 const $list = $item.closest('.child-list'); 3591 const did = $list.data('did'); 3592 const saleType = String($list.data('saletype') || 'R').toUpperCase(); 3593 const tkGubun = String($list.data('tkgubun') || ''); 3594 const purchaseContext = String($list.data('context') || 'regular'); 3595 const isReserve = v2IsDateRequiredSaleType(saleType); 3596 const bkDate = $('#v2TicketDate_' + did).val() || ''; 3597 3598 const spNo = $item.data('spno') || ''; 3599 if (!spNo) return; 3600 const spAmt = Number($item.data('spamt')) || 0; 3601 const spName = $item.find('.child-name').text().trim(); 3602 const qty = parseInt($item.find('.qty-display').text(), 10) || 0; 3603 3604 if (isReserve && !bkDate) { 3605 $item.find('.qty-display').text('0'); 3606 v2SwalInfo(bookingI18n.dateFirst, bookingI18n.selectionRequired); 3607 return; 3608 } 3609 3610 /* ââ DOMì ì ì¥í´ë bookIdx ì°ì ì¬ì© â spNo/bkDate íì ë¹êµ ì´ì íí¼ ââ */ 3611 let storedBookIdx = $item.data('bookIdx'); 3612 let idx = -1; 3613 if (storedBookIdx != null) { 3614 idx = v2BookList.findIndex(function(b){ return b.bookIdx === storedBookIdx; }); 3615 if (idx < 0) { 3616 /* ì´ë¯¸ ë¤ë¥¸ ê²½ë¡(í´ì§íµ ë±)ë¡ ìì ë ê²½ì° â ì ì¥ê° ì´ê¸°í */ 3617 $item.removeData('bookIdx'); 3618 storedBookIdx = null; 3619 } 3620 } 3621 if (idx < 0) { 3622 idx = v2BookList.findIndex(function(b){ 3623 return String(b.spNo) === String(spNo) 3624 && b.bkDate === bkDate 3625 && String(b.purchaseContext || 'regular') === purchaseContext; 3626 }); 3627 } 3628 3629 /* ì ê· ì¶ê°(0âìì) ììë§ ì ì± ê²ì¦ */ 3630 if (qty > 0 && idx < 0) { 3631 if (v2HasMixedDatePolicy(saleType)) { 3632 $item.find('.qty-display').text('0'); 3633 Swal.fire({ 3634 icon: 'info', title: bookingI18n.cartUnavailable, 3635 html: bookingI18n.cartMixInvalid, 3636 confirmButtonText: bookingI18n.confirm, confirmButtonColor: '#ff710d', heightAuto: false 3637 }); 3638 return; 3639 } 3640 if (bkDate && !v2CanAddCartDate(bkDate)) { 3641 $item.find('.qty-display').text('0'); 3642 return; 3643 } 3644 } 3645 3646 const dcCode = $item.find('.v2-discount-id').val() || ''; 3647 const dcAmt = Number($item.find('.v2-discount-amt').val()) || 0; 3648 const dcType = $item.find('.v2-discount-type').val() || '0'; 3649 const savePrice = Number($item.find('.v2-save-price').val()) || 0; 3650 const resultPrice = Number($item.find('.v2-result-price').val()) || spAmt; 3651 const hasDiscount = resultPrice > 0 && resultPrice < spAmt; 3652 const finalAmt = hasDiscount ? resultPrice : spAmt; 3653 3654 if (qty <= 0) {
3655 /* 0ì´ ëë©´ ì¥ë°êµ¬ëìì ì ê±° */ 3656 if (idx >= 0) { 3657 const entry = v2BookList[idx]; 3658 v2TotalAmt -= (entry.dcSpAmt || entry.spAmt) * entry.qty; 3659 v2OriTotAmt -= entry.spAmt * entry.qty; 3660 v2TotalAmt = Math.max(0, v2TotalAmt); 3661 v2OriTotAmt = Math.max(0, v2OriTotAmt); 3662 v2BookList.splice(idx, 1); 3663 $('.li_' + entry.bookIdx).remove(); 3664 } 3665 $item.removeData('bookIdx'); 3666 } else if (idx >= 0) { 3667 /* 기존 í목 ìë/ê¸ì¡ ê°±ì */ 3668 const entry = v2BookList[idx]; 3669 const oldTotal = (entry.dcSpAmt || entry.spAmt) * entry.qty; 3670 const oldOriTotal = entry.spAmt * entry.qty; 3671 entry.qty = qty; 3672 entry.dcSpAmt = finalAmt; 3673 v2TotalAmt += (finalAmt * qty) - oldTotal; 3674 v2OriTotAmt += (spAmt * qty) - oldOriTotal; 3675 $('.qty_' + entry.bookIdx).html(qty); 3676 if (hasDiscount) { 3677 $('#selTotAmt_' + entry.bookIdx).text(toLocaleString(spAmt * qty)); 3678 $('#selDcTotAmt_' + entry.bookIdx).text(toLocaleString(finalAmt * qty)); 3679 $('#selDcAmt_' + entry.bookIdx).text('(-' + toLocaleString((spAmt - finalAmt) * qty) + ' ' + bookingI18n.discountSuffix + ')'); 3680 } else { 3681 $('#spPlaceAmt_' + entry.bookIdx).text(toLocaleString(finalAmt * qty)); 3682 } 3683 $item.data('bookIdx', entry.bookIdx); 3684 } else { 3685 /* ì ê· í목 ì¶ê° */ 3686 v2BookIdx++; 3687 v2TotalAmt += finalAmt * qty; 3688 v2OriTotAmt += spAmt * qty; 3689 const entry = { bookIdx: v2BookIdx, displayId: String(did).replace(/^(regular|partner)-/, ''), spNo, spName, spAmt, dcSpAmt: finalAmt, qty, bkDate, saleType, purchaseContext, partnerCode: purchaseContext === 'partner' ? pCode : '', discountTarget: mbNo ? 'M' : 'G', cavanaYn:'N', dayPkgGb:0, tkGubun, dcType, dcValue:dcAmt }; 3690 if (hasDiscount) { entry.dcCode = dcCode; entry.dcAmt = savePrice; entry.dcYn = 'Y'; } 3691 v2BookList.push(entry); 3692 v2TicketDivCnt(v2BookIdx, spName + (bkDate ? ' (' + bkDate + ')' : ''), spAmt, finalAmt, hasDiscount, dcType, dcAmt, qty); 3693 $item.data('bookIdx', v2BookIdx); 3694 } 3695 3696 $('#bookDt').val(bkDate); 3697 if (v2BookList.length || v2CBookList.length) { 3698 $('.totalAmtInfo').css('display', 'flex'); 3699 $('.btn-next').show(); 3700 } 3701 $('.totalAmt').html(toLocaleString(v2TotalAmt)); 3702 $('.mobile-total').text(toLocaleString(v2TotalAmt)); 3703 v2SyncToV1(); 3704 v2RenderNotice(); 3705 v2ResetAutoAppliedEntryTicketDates(); 3706 } 3707 3708 function v2PkgQtyChange(did, delta) { 3709 const $el = $('#v2PkgQty_' + did); 3710 const cur = parseInt($el.text(), 10) || 1; 3711 const next = cur + delta; 3712 if (next < 1) return; 3713 if (next > 99) return; 3714 $el.text(next); 3715 } 3716 3717 function v2ChangeTicketQty(bkIdx, type) { 3718 const idx = v2BookList.findIndex(function(b){ return b.bookIdx === bkIdx; }); 3719 if (idx < 0) return; 3720 const item = v2BookList[idx]; 3721 if (type === 'minus' && item.qty <= 1) return; 3722 const delta = type === 'plus' ? 1 : -1; 3723 const newQty = item.qty + delta; 3724 const finalAmt = item.dcSpAmt || item.spAmt; 3725 item.qty = newQty; 3726 v2TotalAmt += finalAmt * delta; 3727 v2OriTotAmt += item.spAmt * delta; 3728 $('.qty_' + bkIdx).html(newQty); 3729 if (item.dcSpAmt && item.dcSpAmt < item.spAmt) { 3730 $('#selTotAmt_' + bkIdx).text(toLocaleString(item.spAmt * newQty)); 3731 $('#selDcTotAmt_' + bkIdx).text(toLocaleString(finalAmt * newQty)); 3732 $('#selDcAmt_' + bkIdx).text('(-' + toLocaleString((item.spAmt - finalAmt) * newQty) + ' ' + bookingI18n.discountSuffix + ')'); 3733 } 3734 else { $('#spPlaceAmt_' + bkIdx).text(toLocaleString(finalAmt * newQty)); }
3735 $('.totalAmt').html(toLocaleString(v2TotalAmt)); 3736 v2SyncToV1(); 3737 v2RenderNotice(); 3738 } 3739 3740 function v2ResetTicketQtyDisplay(spNo, purchaseContext) { 3741 if (!spNo) return; 3742 3743 $('.child-item').each(function() { 3744 var itemSpNo = String($(this).attr('data-spno') || ''); 3745 var itemContext = String($(this).closest('.child-list').data('context') || 'regular'); 3746 if (itemSpNo === String(spNo) && (!purchaseContext || itemContext === String(purchaseContext))) { 3747 $(this).find('.qty-display').text('0'); 3748 $(this).removeData('bookIdx'); 3749 } 3750 }); 3751 } 3752 3753 function v2DeleteTicket(bkIdx) { 3754 const idx = v2BookList.findIndex(function(b){ return b.bookIdx === bkIdx; }); 3755 if (idx < 0) return; 3756 3757 const item = v2BookList[idx]; 3758 const qty = Number(item.qty || 0); 3759 const spAmt = Number(item.spAmt || 0); 3760 const finalAmt = Number(item.dcSpAmt || item.spAmt || 0); 3761 3762 v2TotalAmt -= finalAmt * qty; 3763 v2OriTotAmt -= spAmt * qty; 3764 3765 v2TotalAmt = Math.max(0, v2TotalAmt); 3766 v2OriTotAmt = Math.max(0, v2OriTotAmt); 3767 3768 v2ResetTicketQtyDisplay(item.spNo, item.purchaseContext); 3769 3770 v2BookList.splice(idx, 1); 3771 v2ResetAutoAppliedEntryTicketDates(); 3772 $('.li_' + bkIdx).remove(); 3773 $('.totalAmt').html(toLocaleString(v2TotalAmt)); 3774 $('.mobile-total').text(toLocaleString(v2TotalAmt)); 3775 if (!v2BookList.length && !v2CBookList.length) { 3776 v2TotalAmt = 0; 3777 v2OriTotAmt = 0; 3778 $('.totalAmt').html(toLocaleString(0)); 3779 $('.mobile-total').text(toLocaleString(0)); 3780 $('.totalAmtInfo').hide(); 3781 $('.btn-next').hide(); 3782 $('#v2EmptyMsg').show(); 3783 } 3784 v2SyncToV1(); 3785 v2RenderNotice(); 3786 } 3787 3788 /* ================================================================ 3789 ì¹´ë°ë/ë¶ëìì¤ ë´ê¸° / ìì 3790 ================================================================ */ 3791 /* main.js getDcInfoTxt ë "ì í ì¸"ì´ íê¸ ê³ ì ì´ë¼, í°ì¼ íê³¼ ëì¼íê² 3792 bookingI18n.discountSuffix 를 ì°ë ë¤êµì´ ë²ì ì ë¶ëìì¤ ì¥ë°êµ¬ëì ì¬ì© */ 3793 function v2GetDcInfoTxt(dcType, dcAmt) { 3794 dcType = String(dcType || '0'); 3795 if (dcType === '1') return '-' + toLocaleString(Number(dcAmt) || 0) + ' ' + bookingI18n.discountSuffix; 3796 if (dcType === '2') return '-' + dcAmt + '% ' + bookingI18n.discountSuffix; 3797 return ''; 3798 } 3799 3800 function v2CavanaDivCnt(idx, spName, spPlaceValue, tkAmt, dcCode, dcType, dcValue, tkOriAmt) { 3801 dcType = String(dcType || '0'); 3802 let html = '<div class="border-bottom pb-4 mb-4 li_' + idx + '" id="li_' + spPlaceValue + '">'; 3803 html += '<p class="badge badge-info">' 3804 + '<i class="bi bi-x-circle" id="btnDel_' + spPlaceValue + '" onclick="v2DeleteCavana(' + idx + ')"></i>' 3805 + '<span id="spPlace_' + spPlaceValue + '">' + spName + '</span></p>'; 3806 html += '<div class="option">'; 3807 if (dcCode) { 3808 html += '<div>'; 3809 html += '<p class="option_name_sale" id="selTotAmt_' + spPlaceValue + '">' + toLocaleString(tkOriAmt) + '</p>'; 3810 html += '<p class="option_name2 mb-0" id="selDcAmt_' + spPlaceValue + '"> (' + v2GetDcInfoTxt(dcType, dcValue) + ') </p>'; 3811 html += '</div>'; 3812 html += '<p class="option_name" id="selDcTotAmt_' + spPlaceValue + '">' + toLocaleString(tkAmt) + '</p>'; 3813 } else { 3814 html += '<p class="option_name" id="spPlaceAmt_' + spPlaceValue + '">' + toLocaleString(tkAmt) + '</p>'; 3815 } 3816 html += '</div></div>'; 3817 if ($('.selectedTicket2').length) $('.selectedTicket2').append(html); 3818 $('#v2EmptyMsg').hide(); 3819 } 3820 3821 function v2InitCavana() { 3822 v2TotalAmt -= Number(v2SelectedCavanaTotAmt); 3823 v2OriTotAmt -= Number(v2SelectedCavanaAmt); 3824 v2SelectedCavanaAmt = 0; v2SelectedCavanaTotAmt = 0;
3825 $('.totalAmt').html(toLocaleString(v2TotalAmt)); 3826 v2SyncToV1(); 3827 } 3828 3829 function v2CavanaDiv(type, idx, spPlaceValue, spNo, placeNo, spName, spPlaceName, tkAmt, 3830 timeList, did, holdExpiresAtEpochMs, holdMinutes, holdSerialNo) { 3831 tkAmt = Number(tkAmt); 3832 const tList = timeList.split(',').sort(); 3833 let txtTimeList = ''; 3834 tList.forEach(function(t){ const tTxt = $('#timeTxt_' + t).val() || t; txtTimeList += (txtTimeList ? ' , ' : ' / ') + tTxt; }); 3835 const fullSpName = spPlaceName + txtTimeList; 3836 3837 let dcCode = '', dcValue = '', dcType = '', savePrice = 0, resultPrice = 0; 3838 const dcAmtEl = $('#dcAmt_' + spNo).val(); 3839 if (dcAmtEl !== undefined && dcAmtEl !== '') { 3840 dcCode = $('#sId_' + spNo).val() || ''; 3841 dcValue = Number($('#dcAmt_' + spNo).val()) || 0; 3842 dcType = $('#dcType_' + spNo).val() || ''; 3843 savePrice = Number($('#savePrice_' + spNo).val()) || 0; 3844 resultPrice = Number($('#resultPrice_' + spNo).val()) || 0; 3845 } 3846 3847 const oriTkAmt = Number($('#amt_' + spNo).val()) || tkAmt; 3848 3849 if (dcCode) { tkAmt = resultPrice; v2TotalAmt += resultPrice * tList.length; v2OriTotAmt += oriTkAmt * tList.length; v2SelectedCavanaAmt = oriTkAmt * tList.length; v2SelectedCavanaTotAmt = resultPrice * tList.length; } 3850 else { v2TotalAmt += oriTkAmt; v2OriTotAmt += oriTkAmt; v2SelectedCavanaAmt = oriTkAmt; v2SelectedCavanaTotAmt = oriTkAmt; } 3851 v2CavanaDivCnt(idx, fullSpName, spPlaceValue, v2SelectedCavanaTotAmt, dcCode, dcType, dcValue, v2SelectedCavanaAmt); 3852 $('.totalAmtInfo').css('display', 'flex'); $('.btn-next').show(); 3853 3854 const delIdx = v2CBookList.findIndex(function(k){ return k.spPlaceName === spPlaceName; }); 3855 if (delIdx >= 0) v2CBookList.splice(delIdx, 1); 3856 3857 const entry = { bookIdx: idx, did, displayId: String(did).replace(/^(regular|partner)-/, ''), purchaseContext: 'regular', partnerCode: '', discountTarget: mbNo ? 'M' : 'G', spPlaceValue, spNo, bkSeq: placeNo, spPlaceName, spName, displayTxt: fullSpName, bkTimeList: timeList, spAmt: oriTkAmt, qty: tList.length, cavanaYn: 'Y', dayPkgGb: 0, bkDate: selectedDate || '', holdExpiresAtEpochMs: Number(holdExpiresAtEpochMs) || 0, holdMinutes: Number(holdMinutes) || 20, holdSerialNo: holdSerialNo || v2CheckoutSerialNo, dcType: dcType, dcValue: dcValue }; 3858 if (dcCode && savePrice != null) { entry.dcSpAmt = resultPrice; entry.dcAmt = savePrice; entry.dcYn = 'Y'; entry.dcCode = dcCode; } 3859 v2CBookList.push(entry); 3860 3861 $('.totalAmt').html(toLocaleString(v2TotalAmt)); 3862 v2SyncToV1(); 3863 v2RenderNotice(); 3864 v2SyncFacilityHoldTimer(); 3865 } 3866 3867 function v2DeleteCavana(bkIdx) { 3868 const idx = v2CBookList.findIndex(function(k){ return k.bookIdx === bkIdx; }); 3869 if (idx < 0) return; 3870 3871 const item = v2CBookList[idx]; 3872 if (item.releaseInProgress) return; 3873 item.releaseInProgress = true; 3874 v2SetFacilityControlBusy(item.did, true); 3875 v2ReleaseFacilityHold(item, function() { 3876 v2RemoveCavanaLocal(bkIdx); 3877 v2SetFacilityControlBusy(item.did, false); 3878 }, function(message) { 3879 item.releaseInProgress = false; 3880 if (item.did) $('#v2FacTime_' + item.did).val(item.bkTimeList || ''); 3881 v2SetFacilityControlBusy(item.did, false); 3882 v2SwalError(message, bookingI18n.facilityReleaseError); 3883 }); 3884 } 3885 3886 function v2RemoveCavanaLocal(bkIdx, keepControlValue) { 3887 const idx = v2CBookList.findIndex(function(k){ return k.bookIdx === bkIdx; }); 3888 if (idx < 0) return; 3889 3890 const item = v2CBookList[idx]; 3891 const qty = Number(item.qty || 0); 3892 const spAmt = Number(item.spAmt || 0); 3893 const finalAmt = Number(item.dcSpAmt || item.spAmt || 0); 3894 3895 v2TotalAmt -= finalAmt * qty; 3896 v2OriTotAmt -= spAmt * qty; 3897 3898 v2TotalAmt = Math.max(0, v2TotalAmt); 3899 v2OriTotAmt = Math.max(0, v2OriTotAmt); 3900 3901 // select ì´ê¸°í (í´ë¹ facilityì ìê° select ê° ë¹ì°ê¸°) 3902 if (item.did && !keepControlValue) { 3903 const $sel = $('#v2FacTime_' + item.did); 3904 if ($sel.length) $sel.val(''); 3905 } 3906 3907 v2CBookList.splice(idx, 1); 3908 $('.li_' + bkIdx).remove(); 3909 v2SelectedTimeList = ''; v2SelectedCavanaAmt = 0; v2SelectedCavanaTotAmt = 0; 3910
3911 $('.totalAmt').html(toLocaleString(v2TotalAmt)); 3912 $('.mobile-total').text(toLocaleString(v2TotalAmt)); 3913 3914 if (!v2CBookList.length && !v2BookList.length) { 3915 v2TotalAmt = 0; 3916 v2OriTotAmt = 0; 3917 $('.totalAmt').html(toLocaleString(0)); 3918 $('.mobile-total').text(toLocaleString(0)); 3919 $('.totalAmtInfo').hide(); 3920 $('.btn-next').hide(); 3921 $('#v2EmptyMsg').show(); 3922 } 3923 v2SyncToV1(); 3924 v2RenderNotice(); 3925 v2SyncFacilityHoldTimer(); 3926 } 3927 3928 /* ================================================================ 3929 ë¤ìë¨ê³ (fnNext) 3930 ================================================================ */ 3931 function fnNext() { 3932 const valid = v2ValidateSelection(); 3933 if (!valid.ok) { 3934 if (valid.msgType === 'error') v2SwalError(valid.userMsg, bookingI18n.error); 3935 else if (valid.msgType === 'warning') v2SwalWarning(valid.userMsg, bookingI18n.error); 3936 else v2SwalInfo(valid.userMsg, bookingI18n.selectionRequired); 3937 return false; 3938 } 3939 if (v2FacilityHoldRequestCount > 0) { 3940 v2SwalInfo(bookingI18n.processing, bookingI18n.info); 3941 return false; 3942 } 3943 if (v2HasExpiredFacilityHold()) { 3944 v2HandleFacilityHoldExpiry(); 3945 return false; 3946 } 3947 if (v2NextInProgress) return false; 3948 v2NextInProgress = true; 3949 3950 v2ShowLoading(bookingI18n.reservationProcessing); 3951 3952 try { 3953 if (!v2RequireLoginBeforeCart()) { 3954 v2HideLoading(); 3955 v2NextInProgress = false; 3956 return false; 3957 } 3958 const bkDate = v2ResolveBkDateForSubmit(); 3959 const identity = v2EnsureCheckoutIdentity(); 3960 const serialNo = identity.serialNo; 3961 history.replaceState('', 'bookMain', v2BuildBookMainUrl({ serialNo: serialNo })); 3962 const payload = v2BuildBookPayload(bkDate); 3963 3964 $.ajax({ 3965 url: '/hBook/book/' + agentCode + '/' + bkDate + '/' + serialNo, 3966 type: 'post', dataType: 'json', contentType: 'application/json; charset=utf-8', 3967 data: JSON.stringify(payload), 3968 success: function(data) { 3969 if (data && data.code === '0000') { 3970 location.href = '/hBook/bookDetailConfirm/' + data.data 3971 + '?lang=' + encodeURIComponent(bookingLanguageParam); 3972 return; 3973 } 3974 3975 v2HideLoading(); 3976 v2SwalError((data && data.message) ? data.message : bookingI18n.reservationError, bookingI18n.error) 3977 .then(function(){ 3978 v2ReleaseAllFacilityHolds(function() { 3979 location.href = v2BuildBookMainUrl(); 3980 }); 3981 }); 3982 v2NextInProgress = false; 3983 }, 3984 error: function() { 3985 v2HideLoading(); 3986 v2SwalError(bookingI18n.reservationError, bookingI18n.error); 3987 v2NextInProgress = false; 3988 } 3989 }); 3990 } catch(e) { 3991 v2HideLoading(); 3992 v2SwalError(bookingI18n.reservationError, bookingI18n.error); 3993 v2NextInProgress = false; 3994 } 3995 return false; 3996 } 3997 3998 function v2GetSaleTypeSummary() { 3999 const r = { reserveCnt:0, periodCnt:0, eventCnt:0, seasonCnt:0, unknownCnt:0, facilityCnt: v2CBookList.length }; 4000 v2BookList.forEach(function(item){ 4001 const t = String(item.saleType || '').toUpperCase(); 4002 if (t==='R') r.reserveCnt++; else if(t==='P') r.periodCnt++; else if(t==='E') r.eventCnt++; else if(t==='S') r.seasonCnt++; else r.unknownCnt++; 4003 }); 4004 return r; 4005 } 4006 4007 function v2NeedBookingDate() { 4008 const s = v2GetSaleTypeSummary(); 4009 return s.facilityCnt > 0 || s.reserveCnt > 0; 4010 } 4011 4012 function v2ResolveBkDateForSubmit() { 4013 return ($('#bookDt').val() || '').trim() || getToDay('-'); 4014 } 4015 4016 function v2ValidateSelection() { 4017 const bkDate = ($('#bookDt').val() || '').trim(); 4018 if (v2NeedBookingDate() && !bkDate) return { ok: false, userMsg: bookingI18n.dateFirst, msgType: 'info' }; 4019 if (!v2BookList.length && !v2CBookList.length) return { ok: false, userMsg: bookingI18n.productEmpty, msgType: 'info' }; 4020 if (Number(v2TotalAmt || 0) <= 0) return { ok: false, userMsg: bookingI18n.paymentNone, msgType: 'warning' }; 4021 for (let i = 0; i < v2BookList.length; i++) { 4022 const t = v2BookList[i] || {}; 4023 if (!t.spNo || Number(t.qty || 0) <= 0) return { ok: false, userMsg: bookingI18n.ticketInvalid, msgType: 'error' }; 4024 } 4025 for (let j = 0;
4025 j < v2CBookList.length; j++) { 4026 const c = v2CBookList[j] || {}; 4027 if (!c.spNo || Number(c.qty || 0) <= 0) return { ok: false, userMsg: bookingI18n.facilityInvalid, msgType: 'error' }; 4028 if (!c.bkTimeList || !c.bkTimeList.trim()) return { ok: false, userMsg: bookingI18n.facilityTimeInvalid, msgType: 'error' }; 4029 } 4030 return { ok: true }; 4031 } 4032 4033 function v2BuildBookPayload(bkDate) { 4034 const ticket = v2BookList.map(function(item){ 4035 const itemBkDate = v2IsNoDateSaleType(item.saleType) ? '' : (item.bkDate || bkDate); 4036 return { bookIdx: Number(item.bookIdx||0), displayId: Number(item.displayId||0), purchaseContext: item.purchaseContext||'regular', partnerCode: item.partnerCode||'', discountTarget: item.discountTarget||(mbNo?'M':'G'), spNo: item.spNo||'', spName: item.spName||'', spAmt: Number(item.spAmt||0), dcSpAmt: Number(item.dcSpAmt||item.spAmt||0), qty: Number(item.qty||0), bkDate: itemBkDate, cavanaYn:'N', dayPkgGb: String(item.dayPkgGb!=null?item.dayPkgGb:0), dcCode: item.dcCode||'', dcAmt: Number(item.dcAmt||0), dcYn: item.dcYn||'N', displayTxt: item.displayTxt||'', cardDcNo: item.cardDcNo||'', expensiveYn: item.expensiveYn||'', saleType: item.saleType||'', tkGubun: item.tkGubun||'' }; 4037 }); 4038 const facility = v2CBookList.map(function(item){ 4039 return { bookIdx: Number(item.bookIdx||0), displayId: Number(item.displayId||0), purchaseContext: 'regular', partnerCode: '', discountTarget: item.discountTarget||(mbNo?'M':'G'), spNo: item.spNo||'', bkSeq: item.bkSeq||'', spPlaceName: item.spPlaceName||'', spName: item.spName||'', displayTxt: item.displayTxt||'', bkTimeList: item.bkTimeList||'', spAmt: Number(item.spAmt||0), dcSpAmt: Number(item.dcSpAmt||item.spAmt||0), qty: Number(item.qty||0), bkDate: item.bkDate||bkDate, cavanaYn:'Y', dayPkgGb: String(item.dayPkgGb!=null?item.dayPkgGb:0), dcCode: item.dcCode||'', dcAmt: Number(item.dcAmt||0), dcYn: item.dcYn||'N', cardDcNo: item.cardDcNo||'', expensiveYn: item.expensiveYn||'' }; 4040 }); 4041 return ticket.concat(facility); 4042 } 4043 4044 /* ================================================================ 4045 ì´ìì²´í¬ 4046 ================================================================ */ 4047 function v2ResolveOperationCode(data) { 4048 if (!data) return 'WATERPARK'; 4049 const title = (data.displayTitle || data.spName || '').toLowerCase(); 4050 return (title.indexOf('ì°ì§') >= 0 || title.indexOf('ì¤í') >= 0) ? 'SPA' : 'WATERPARK'; 4051 } 4052 4053 function v2CheckOperation(dateStr, viewKey, data, onAvailable) { 4054 const $detail = $('#v2-detail-' + viewKey); 4055 $detail.find('.v2-op-check-msg').remove(); 4056 $.ajax({ 4057 url: '/hBook/v2/operationCheck/' + v2ResolveOperationCode(data) + '/' + dateStr, 4058 type: 'get', dataType: 'json', 4059 success: function(res) { 4060 if (!res.available) { 4061 v2SwalError(res.message || bookingI18n.dateUnavailableSelected, bookingI18n.dateUnavailable).then(function(){ 4062 const el = document.getElementById('v2TicketDate_' + viewKey); 4063 if (el && el._flatpickr) el._flatpickr.clear(); 4064 v2ShowTicketDateState(viewKey, bookingI18n.dateGuide, true); 4065 $detail.data('selectedBkDate', ''); 4066 $detail.data('cartDateApplied', false); 4067 $('#bookDt').val(v2GetCartBookingDate() || ''); 4068 }); 4069 } else { 4070 if (res.warningOnly) { 4071 $detail.find('.reservation-box').after('<div class="v2-op-check-msg"><b>â ' + bookingI18n.operationNotice + '</b><br>' + res.message + '</div>'); 4072 } 4073 if (typeof onAvailable === 'function') onAvailable(); 4074 } 4075 }, 4076 error: function() {} 4077 }); 4078 } 4079 4080 function v2ShowTicketDateState(viewKey, message, subtle) { 4081 var $right = $('#v2-detail-' + viewKey).find('.detail-right'); 4082 $right.children('.child-list, .pkg-info-note, .v2-ticket-date-state').remove(); 4083 var className = subtle 4084 ? 'pkg-info-note v2-ticket-date-state' 4085 : 'text-center py-3 mb-0 v2-ticket-date-state'; 4086 $right.append('<p class="' + className + '">' + message + '</p>'); 4087 } 4088 4089 function v2ReloadDetailByDate(displayId, viewKey, partnerYn, bkDate, cartDateApplied) { 4090 var $detail = $('#v2-detail-' + viewKey); 4091 $detail.data('pendingBkDate', bkDate); 4092 4093 function applyDetail(newData) { 4094 if (String($detail.data('pendingBkDate') || '') !== String(bkDate)) return; 4095 4096 newData.__selectedBkDate = bkDate; 4097 newData.__viewKey = viewKey; 4098 newData.__partnerYn = partnerYn === true; 4099 v2UpdateProductSummaryPrice(viewKey, newData, partnerYn === true); 4100 4101 $detail.html(v2BuildDetailHtml(newData)); 4102 4103 var st = String(newData.saleType || '').toUpperCase(); 4104 var spGb = String(newData.spGbCode || '').padStart(4, '0'); 4105 if (spGb === '0051') { 4106 v2ActiveNoticeType = 'RESERVE'; 4107 } else { 4108 v2ActiveNoticeType = (st === 'R') ? 'RESERVE' : 'PERIOD'; 4109 } 4110 $detail.data('cachedSaleType', v2ActiveNoticeType); 4111 $detail.data('saleTypeCode', st); 4112 $detail.data('selectedBkDate', bkDate); 4113 $detail.data('cartDateApplied', cartDateApplied === true);
4114 $detail.data('detailData', newData); 4115 v2RenderNotice(); 4116 $('#bookDt').val(bkDate); 4117 4118 if (spGb !== '0051') { 4119 var el = document.getElementById('v2TicketDate_' + viewKey); 4120 if (el) { 4121 flatpickr(el, v2BuildDatePickerOptions({ 4122 position: 'below', 4123 defaultDate: bkDate, 4124 onChange: function(dates, dateStr) { 4125 $('#bookDt').val(dateStr); 4126 v2ShowTicketDateState(viewKey, bookingI18n.loading); 4127 v2CheckOperation(dateStr, viewKey, newData, function() { 4128 v2ReloadDetailByDate(displayId, viewKey, partnerYn, dateStr); 4129 }); 4130 } 4131 }, partnerYn)); 4132 } 4133 } 4134 } 4135 4136 $.ajax({ 4137 url: partnerYn 4138 ? v2AppendPartnerCode('/hBook/v2/display/detail/' + displayId + '?bkDate=' + bkDate) 4139 : v2AppendLanguage('/hBook/v2/display/detail/' + displayId + '?bkDate=' + bkDate), 4140 type: 'get', 4141 dataType: 'json', 4142 beforeSend: v2ShowLoading, 4143 complete: v2HideLoading, 4144 success: function(newData) { 4145 applyDetail(newData); 4146 }, 4147 error: function(xhr) { 4148 if (String($detail.data('pendingBkDate') || '') !== String(bkDate)) return; 4149 4150 if (xhr.status === 404) { 4151 var emptyData = Object.assign({}, $detail.data('detailData') || {}, { 4152 childProducts: [], 4153 parentPurchasableYn: 'N' 4154 }); 4155 applyDetail(emptyData); 4156 return; 4157 } 4158 v2ShowTicketDateState(viewKey, bookingI18n.loadError); 4159 } 4160 }); 4161 } 4162 4163 function v2CheckOperationFacility(dateStr, did, callback) { 4164 const $facBox = $('#v2FacBox_' + did); 4165 $facBox.find('.v2-op-check-msg').remove(); 4166 $.ajax({ 4167 url: '/hBook/v2/operationCheck/WATERPARK/' + dateStr, 4168 type: 'get', dataType: 'json', 4169 success: function(res) { 4170 if (!res.available) { 4171 v2SwalError(res.message || bookingI18n.dateUnavailableSelected, bookingI18n.dateUnavailable).then(function(){ 4172 const el = document.getElementById('v2FacDate_' + did); 4173 if (el && el._flatpickr) el._flatpickr.clear(); 4174 $('#bookDt').val(''); selectedDate = ''; 4175 }); 4176 } else { 4177 if (res.warningOnly) $facBox.find('.fac-row:first').after('<div class="v2-op-check-msg"><b>â ' + bookingI18n.operationNotice + '</b><br>' + res.message + '</div>'); 4178 if (typeof callback === 'function') callback(); 4179 } 4180 }, 4181 error: function() { if (typeof callback === 'function') callback(); } 4182 }); 4183 } 4184 4185 /* ================================================================ 4186 SweetAlert2 í¬í¼ 4187 ================================================================ */ 4188function v2SwalError(msg, title) { return Swal.fire({ icon:'error', title: title || bookingI18n.error, text: msg, confirmButtonText: bookingI18n.confirm, confirmButtonColor:'#ff710d', heightAuto:false }); } 4189function v2SwalWarning(msg, title) { return Swal.fire({ icon:'warning', title: title || bookingI18n.notice, text: msg, confirmButtonText: bookingI18n.confirm, confirmButtonColor:'#ff710d', heightAuto:false }); } 4190function v2SwalInfo(msg, title) { return Swal.fire({ icon:'info', title: title || bookingI18n.info, text: msg, confirmButtonText: bookingI18n.confirm, confirmButtonColor:'#ff710d', heightAuto:false }); } 4191 4192 // bfcache/ë¤ë¡ê°ê¸° ë³µì ì ë¨ììë ë¡ë©/ì§í ìí를 ê°ì ì´ê¸°ííë¤. 4193 window.addEventListener('pageshow', function(event) { 4194 var navEntry = (performance && typeof performance.getEntriesByType === 'function') 4195 ? performance.getEntriesByType('navigation')[0] 4196 : null; 4197 var isBackForward = !!event.persisted || (navEntry && navEntry.type === 'back_forward'); 4198
4199 if (!isBackForward) { 4200 return; 4201 } 4202 4203 v2NextInProgress = false; 4204 v2ResetLoading(); 4205 4206 $('#v2-loading-overlay') 4207 .stop(true, true) 4208 .hide() 4209 .css({ 4210 display: 'none', 4211 opacity: 1 4212 }); 4213 }); 4214 4215 document.addEventListener('visibilitychange', function() { 4216 if (!document.hidden && v2CBookList.length) v2UpdateFacilityHoldTimer(); 4217 }); 4218 4219 /* ================================================================ 4220 Document Ready 4221 ================================================================ */ 4222 $(function() { 4223 v2PrepareCartRestore(); 4224 v2SyncHeaderLanguageLinks(); 4225 v2SyncToV1(); 4226 v2InitPage(); 4227 v2SwitchTab('special'); // â ì¶ê°: ì´ê¸° ì§ì ì í¹ê° í ì¸ë¼ì¸ íì ê°ì 4228 4229 /* ìë¨ í í´ë¦ */ 4230 $(document).on('click', '#v2TabNav .nav-link', function(e) { 4231 e.preventDefault(); 4232 v2SwitchTab($(this).data('tab')); 4233 }); 4234 4235 $(document).on('click', '#v2ProductLanguageSwitcher [data-booking-lang]', function() { 4236 v2ChangeBookingLanguage($(this).data('booking-lang')); 4237 }); 4238 4239 $(document).on('click', v2HeaderLanguageLinkSelector, function(e) { 4240 const language = v2GetLanguageFromLink(this); 4241 if (!language) return; 4242 e.preventDefault(); 4243 v2ChangeBookingLanguage(language); 4244 }); 4245 4246 $(document).on('click', '.qty-plus', function(e) { 4247 e.stopPropagation(); 4248 const $item = $(this).closest('.child-item'); 4249 if (!$item.length) return; // ë¶ëìì¤ ë± í´ë¹ìë ììì 무ì 4250 4251 const $detail = $(this).closest('.product-detail, .facility-detail'); 4252 const did = $detail.attr('id') ? $detail.attr('id').replace('v2-detail-', '') : ''; 4253 const $dateInput = $detail.find('#v2TicketDate_' + did); 4254 4255 if ($dateInput.length && !$dateInput.val()) { 4256 v2SwalInfo(bookingI18n.dateFirst, bookingI18n.selectionRequired); 4257 return; 4258 } 4259 4260 const $el = $(this).siblings('.qty-display'); 4261 $el.text(parseInt($el.text()) + 1); 4262 v2SyncChildItemToCart($item); 4263 }); 4264 4265 $(document).on('click', '.qty-minus', function(e) { 4266 e.stopPropagation(); 4267 const $item = $(this).closest('.child-item'); 4268 if (!$item.length) return; 4269 4270 const $el = $(this).siblings('.qty-display'); 4271 const v = parseInt($el.text()); 4272 if (v <= 0) return; 4273 4274 $el.text(v - 1); 4275 v2SyncChildItemToCart($item); 4276 }); 4277 4278 if (window.innerWidth <= 768) { 4279 const amtObserver = new MutationObserver(function() { 4280 $('.mobile-total').text($('.totalAmt').first().text() || '0'); 4281 }); 4282 document.querySelectorAll('.totalAmt').forEach(function(el) { 4283 amtObserver.observe(el, { childList: true, subtree: true, characterData: true }); 4284 }); 4285 } 4286 4287 window.v2ToggleMobileCart = function() { 4288 const $bar = $('#v2MobileBar'); 4289 const willCollapse = !$bar.hasClass('is-collapsed'); 4290 $bar.toggleClass('is-collapsed', willCollapse); 4291 $('.hBook').toggleClass('mobile-cart-collapsed', willCollapse); 4292 $('#v2MobileCartToggle') 4293 .attr('aria-expanded', String(!willCollapse)) 4294 .attr('aria-label', willCollapse ? bookingI18n.cartExpand : bookingI18n.cartCollapse); 4295 }; 4296 4297 window.v2ScrollToPanel = function() { 4298 $('html, body').animate({ 4299 scrollTop: $('#v2SidebarCol').offset().top - 20 4300 }, 400); 4301 }; 4302 }); 4303 4304</script>
4304 4305 4306</body> 4307</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.