1<!doctype html> 2<!--[if lt IE 7]><html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en"> <![endif]--> 3<!--[if IE 7]><html class="no-js lt-ie9 lt-ie8" lang="en"> <![endif]--> 4<!--[if IE 8]><html class="no-js lt-ie9" lang="en"> <![endif]--> 5<!--[if IE 9 ]><html class="ie9 no-js"> <![endif]--> 6<!--[if (gt IE 9)|!(IE)]><!--> <html class="no-js"> <!--<![endif]--> 7<head> 8 9 <!-- Basic page needs ================================================== --> 10 <meta charset="utf-8"> 11 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> 12 13<meta name="msvalidate.01" content="6FA224880599445DEEFF8B2018AC1C4A" /> 14 15<meta name="google-site-verification" content="2HBIu8xGZUcYtQ8oa34JnBpovAuEd1nkerLH_xLjvFY" /> 16 17 18 19 20 <link rel="icon" href="https://s3-ap-southeast-1.amazonaws.com/eesb.themes/onboarding/mine-style/favicon.png" /> 21 22 23 <!-- Title and description ================================================== --> 24 <title> 25 ç»å ¥ – æ²éå¿èå·¥ä½å®¤ 26 </title> 27 28 29 30 <!-- Social meta ================================================== --> 31 32 33 <meta property="og:type" content="website"> 34 <meta property="og:title" content="ç»å ¥"> 35 <meta property="og:url" content="https://www.sandplayshop.com/account/login"> 36 37 38 39<meta property="og:description" content="æ²éæ²»çéè¦è¨±å¤åå¼å樣çå°ç©ä»¶ï¼è®ä½ è½å¤ æä¾æ²éç©å®¶å種å¯ä»¥åµé å ¶å¿éè±å¯çå°ä¸çãæ¬åºçºå°ç£å°çºæ²éå好åå°å¯¶çç§å¯åºå°ï¼å¹«å©ä½ å¯ä»¥æ¾å°ä½ æéè¦çæ¶èåï¼è±å¯ä½ çæ²ç¤è³æåº«ã"> 40<meta property="og:site_name" content="æ²éå¿èå·¥ä½å®¤"> 41 42 43 44 <meta name="twitter:card" content="summary"> 45 46 47 48 49 50 51 52 <!-- Helpers ================================================== --> 53 <link rel="canonical" href="https://www.sandplayshop.com/account/login"> 54 <meta name="viewport" content="width=device-width,initial-scale=1"> 55 <meta name="theme-color" content=""> 56 57 <!-- CSS ================================================== --> 58 <link href="https://store-themes.easystore.co/217101/themes/338/assets/timber.css?t=1787072452" rel="stylesheet" type="text/css" media="screen" /> 59 <link href="https://store-themes.easystore.co/217101/themes/338/assets/theme.css?t=1787072452" rel="stylesheet" type="text/css" media="screen" /> 60 61 62 63 64 65 66
66<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js" type="text/javascript" ></script>
66 67 <!-- Header hook for plugins ================================================== --> 68 69<!-- ScriptTags -->
70<script>window.__st={p: "customers",cid: null,cem: null,cph: null,cfn: null,cln: null,cge: null,cdb: null,ccc: null};</script>
70<script src='/assets/events.js'></script>
70<script>(function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(d.getElementById(id))return;js=d.createElement(s);js.id=id;js.src='https://connect.facebook.net/zh_TW/sdk/xfbml.customerchat.js#xfbml=1&version=v6.0&autoLogAppEvents=1';fjs.parentNode.insertBefore(js,fjs);}(document,'script','facebook-jssdk'));</script>
70<script>(function(){function asyncLoad(){var urls=['/assets/facebook-pixel.js?v=1.2.1740523612','/assets/traffic.js?v=1','https://apps.easystore.co/facebook-login/script.js?shop=douzi08056714.easy.co','https://apps.easystore.co/social-media-share/script.js?shop=douzi08056714.easy.co','https://apps.easystore.co/google-analytics/script.js?shop=douzi08056714.easy.co','https://apps.easystore.co/timer-discount/script.js?shop=douzi08056714.easy.co'];for(var i=0;i<urls.length;i++){var s=document.createElement('script');s.type='text/javascript';s.async=true;s.src=urls[i];var x=document.getElementsByTagName('script')[0];x.parentNode.insertBefore(s, x);}}window.attachEvent ? window.attachEvent('onload', asyncLoad) : window.addEventListener('load', asyncLoad, false);})();</script>
70 71<!-- /ScriptTags --> 72 73 74 75<!--[if lt IE 9]>
76<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.2/html5shiv.min.js" type="text/javascript" ></script>
vendor: 2 bytes, line 76
76
77<script src="https://store-themes.easystore.co/217101/themes/338/assets/respond.min.js?t=1787072452" type="text/javascript" ></script>
77 78<link href="https://store-themes.easystore.co/217101/themes/338/assets/respond-proxy.html" id="respond-proxy" rel="respond-proxy" /> 79<link href="//www.sandplayshop.com/search?q=e191d93668a6a3bd1fef346a87dc3573" id="respond-redirect" rel="respond-redirect" />
80<script src="//www.sandplayshop.com/search?q=e191d93668a6a3bd1fef346a87dc3573" type="text/javascript"></script>
80 81<![endif]--> 82 83 84 85
85<script src="https://store-themes.easystore.co/217101/themes/338/assets/modernizr.min.js?t=1787072452" type="text/javascript" ></script>
85 86 87 88 89
89<script src="/assets/shop_common.js" type="text/javascript" ></script>
89 90
90<script src="/assets/customer_area.js" type="text/javascript" ></script>
90 91 92
92<script> 93 (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 94 (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 95 m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 96 })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); 97 98 ga('create', '', 'auto','myTracker'); 99 ga('myTracker.send', 'pageview'); 100 101 </script>
101 102 103<!-- Snippet:global/head: Bing Webmaster --> 104<meta name="msvalidate.01" content="E871216FFDC519015313441521993575" /> 105<!-- /Snippet --> 106 107<!-- Snippet:global/head: Google Search Console --> 108<meta name="google-site-verification" content="HqRSv06LfxITEtatCwidOTSEfzo-y7B_CQuDaseiZnA" /> 109<!-- /Snippet --> 110 111 112
112<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
vendor: 1 bytes, line 112
112
113<script> 114 (adsbygoogle = window.adsbygoogle || []).push({ 115 google_ad_client: "ca-pub-7484581862148945", 116 enable_page_level_ads: true 117 }); 118</script>
118 119 120 121</head> 122 123<body id="------" class="template-customers" > 124 125<!-- Snippet:global/body_start: Announcement Bar --> 126<style type="text/css"> 127body{ 128 background-position: top 45px center; 129} 130div#announcementBarTop{ 131 height: 45px; 132} 133 134div#announcementBar{ 135 136 width:100%; 137 padding:0px; 138 margin:0px; 139 z-index:2000; 140 background:transparent; 141 border:0px; 142 boxShadow:none; 143 display:block; 144 opacity:1; 145 font-family: Helvetica, Arial, sans-serif; font-size:15px; 146} 147 148div#announcementBar>div{ 149 width:100%; 150 padding:3px 0px; 151 margin:0px; 152 position: relative; 153} 154 155div#announcementBar>div>div{ 156 margin:0px; 157 padding:6px 10px; 158 border-top:1px dashed transparent; 159 border-bottom:1px dashed transparent; 160} 161 162div#announcementBar>div *{ 163 padding:0px; 164 margin:0px; 165 width:auto; 166 background:transparent; 167 color:inherit; opacity:1; 168 vertical-align:middle; 169 position:static; 170 border:0px; 171 border-radius:0px; 172 box-shadow:none; 173} 174 175div#announcementBar img{ 176 cursor:pointer; 177 float:right; 178} 179 180div#announcementBar span{ 181 text-align:center; 182 display: -webkit-flex; 183 display: -moz-flex; 184 display: -ms-flex; 185 display: -o-flex; 186 display: flex; 187 align-items: center; 188 flex-wrap: wrap; 189 justify-content: center; 190} 191 192div#announcementBar a{ 193 padding:4px 8px; 194 vertical-align:baseline; 195 border-radius: 5px; 196 margin-left: 10px; 197 opacity: 1; 198 line-height: 1.1; 199 text-decoration: none; 200 display: inline-block; 201 font-weight: 500; 202} 203 204div#announcementBar a:hover{ 205 opacity: 0.8; 206} 207 208div#announcementBar #announcementBar_countdown:empty{ 209 display: none; 210} 211div#announcementBar .announcementBar_timer.style_1, 212div#announcementBar .announcementBar_timer.style_2{ 213 display: -webkit-inline-flex; 214 display: -moz-inline-flex; 215 display: -ms-inline-flex; 216 display: -o-inline-flex; 217 display: inline-flex; 218 align-items: center; 219 vertical-align: middle; 220 padding: 5px 8px; 221 margin: 3px 0px 3px 10px; 222 border-radius: 8px; 223 font-weight: bold; 224} 225div#announcementBar .announcementBar_timer.style_1 > div, 226div#announcementBar .announcementBar_timer.style_2 > div{ 227 margin: 0 5px; 228 line-height: 1.2; 229 font-size: 115%; 230} 231div#announcementBar .announcementBar_timer.style_1 small, 232div#announcementBar .announcementBar_timer.style_2 small{ 233 display: block; 234 font-size: 55%; 235 line-height: 1.4; 236} 237div#announcementBar .announcementBar_timer.style_2{ 238 flex-direction: row; 239} 240div#announcementBar .announcementBar_timer.style_2 > div{ 241 position: relative; 242 width: 31px; 243 font-size: 18px; 244} 245div#announcementBar .announcementBar_timer.style_2 > div:not(:last-child):after{ 246 position: absolute; 247 content: ':'; 248 top: 0; 249 right: 0; 250 margin-right: -7px; 251} 252div#announcementBar .announcementBar_timer.style_3{ 253 display: -webkit-inline-flex; 254 display: -moz-inline-flex; 255 display: -ms-inline-flex; 256 display: -o-inline-flex; 257 display: inline-flex; 258 align-items: center; 259 vertical-align: middle; 260 border-radius: 5px; 261 padding: 5px 5px; 262 margin-left: 10px; 263 font-weight: bold; 264} 265div#announcementBar .announcementBar_timer.style_3 > div{ 266 margin: 0 3px; 267 line-height: 1.1; 268} 269div#announcementBar .announcementBar_timer.style_3 small{ 270 font-weight: bold; 271 line-height: 1.1; 272 font-size: 65%; 273} 274 275 276div#announcementBar .announcement-close { 277 position: relative; 278 float: right; 279 width: 24px; 280 height: 24px; 281 opacity: 1; 282 background: #000; 283 border-radius: 50%; 284 cursor: pointer; 285} 286div#announcementBar .announcement-close:hover { 287 opacity: 0.6; 288} 289div#announcementBar .announcement-close:before,div#announcementBar .announcement-close:after { 290 position: absolute; 291 left: 11px; 292 top: 5px; 293 content: ' '; 294 height: 14px; 295 width: 2px; 296 background-color: #fff; 297 border-radius: 3px; 298} 299div#announcementBar .announcement-close:before { 300 transform: rotate(45deg); 301} 302div#announcementBar .announcement-close:after { 303 transform: rotate(-45deg); 304} 305</style> 306 307 308<div id="announcementBar"> 309 <div style="color: rgb(255, 255, 255); background: rgb(233, 69, 69);"> 310 <div > 311 312 <span> 313 è¶ åå貨滿1000å éè²»ï¼éµå¯æå® é æ»¿1500å éè²» 314 <a href="https://www.sandplayshop.com/a/help-center" style="color: rgb(233, 69, 69); background: rgb(255, 255, 255);">è³¼ç©èªªæ</a> 315 316 </span> 317 </div> 318 </div> 319</div> 320 321
322<script> 323 324 var countdown_interval = setInterval(function(){ 325 var startDate = new Date(); 326 var endDateStr = "2022-01-01 00:00"; 327 var endDate = new Date(endDateStr.replace(/-/g, "/")); 328 var seconds = (endDate.getTime() - startDate.getTime()) / 1000; 329 330 var days = parseInt(seconds / 86400); 331 var hours = parseInt((seconds % 86400) / 3600); 332 var mins = parseInt((seconds % 86400 % 3600) / 60); 333 var secs = parseInt((seconds % 86400 % 3600) % 60); 334 335 // use to translate countdown unit 336 // (translate based on the preferred language when save announcement bar setting) 337 String.prototype.translate=function(){ 338 try{ 339 if("zh_TW"=="en_US"){ 340 if(this.toString()==='day'){ 341 if (days > 0){return "Days";} 342 else {return "Day";} 343 } 344 else if(this.toString()==='hour'){ 345 if (hours > 0){return "Hours";} 346 else {return "Hour";} 347 } 348 else if(this.toString()==='min'){ 349 if (mins > 0){return "Mins";} 350 else {return "Min";} 351 } 352 else if(this.toString()==='sec'){ 353 if (secs > 0){return "Secs";} 354 else {return "Sec";} 355 } 356 } 357 else if("zh_TW"=="zh_TW"){ 358 if(this.toString()==='day'){return "天";} 359 if(this.toString()==='hour'){return "å°æ";} 360 if(this.toString()==='min'){return "åé";} 361 if(this.toString()==='sec'){return "ç§";} 362 } 363 } 364 catch(error){ 365 console.log("Some errors heres",error); 366 } 367 }; 368 369 var announcementBar_countdown = document.getElementById("announcementBar_countdown"); 370 if(announcementBar_countdown){ 371 announcementBar_countdown.innerHTML = "<div>"+days + " <small>"+'day'.translate()+"</small> </div><div>" + hours + " <small>"+'hour'.translate()+"</small></div><div> " + mins + " <small>"+'min'.translate()+"</small> </div><div>" + secs + " <small>"+'sec'.translate()+"</small></div>"; 372 $('#announcementBarTop').css('height', $('#announcementBar').height()); 373 if(seconds <= 0){ 374 $("#announcementBarTop,#announcementBar").remove() 375 } 376 }else{ 377 clearInterval(countdown_interval); 378 } 379 }, 1000); 380 381(function(){ 382 383 var loadScript = function(url, callback){ 384 var script = document.createElement("script"); 385 script.type = "text/javascript"; 386 // If the browser is Internet Explorer. 387 if (script.readyState){ 388 script.onreadystatechange = function(){ 389 if (script.readyState == "loaded" || script.readyState == "complete"){ 390 script.onreadystatechange = null; 391 callback(); 392 } 393 }; 394 // For any other browser. 395 } else { 396 script.onload = function(){ 397 callback(); 398 }; 399 } 400 script.src = url; 401 document.getElementsByTagName("head")[0].appendChild(script); 402 }; 403 404 var announcementBarAppJS = function($){ 405 406 $('#announcementBarTop').css('height', $('#announcementBar').height()); 407 408 $('#announcementBar #announcement_close').on('click', function(){ 409 $('#announcementBar, #announcementBarTop').animate({ 410 marginTop : '-='+$('#announcementBar').height()+'px' 411 }, 200, 'linear'); 412 }); 413 414 415 } 416 417 if(typeof jQuery === 'undefined') { 418 loadScript('//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js', function(){ 419 jQuery = jQuery.noConflict(true); 420 announcementBarAppJS(jQuery); 421 }); 422 }else{ 423 announcementBarAppJS(jQuery); 424 } 425 426})(); 427</script>
427 428 429<!-- /Snippet --> 430 431<!-- Snippet:global/body_start: Console Extension --> 432<div id='es_console' style='display: none;'>217101</div> 433<!-- /Snippet --> 434<link href="/assets/css/wishlist.css?v1.02" rel="stylesheet" type="text/css" media="screen" /> 435<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css"> 436
437<script src="/assets/js/wishlist/wishlist.js" type="text/javascript"></script>
437 438 439 <style> 440 .search-modal__form{ 441 position: relative; 442 } 443 444 .dropdown-wrapper { 445 position: relative; 446 } 447 448 .dropdown-wrapper:hover .dropdown { 449 display: block; 450 } 451 452 .dropdown { 453 display: none; 454 position: absolute; 455 top: 100%; 456 left: 0px; 457 width: 100%; 458 padding: 8px 0; 459 margin: 0; 460 background-color: #fff; 461 z-index: 1000; 462 border-radius: 5px; 463 border: 1px solid rgba(var(--color-foreground), 1); 464 border-top: none; 465 overflow-x: hidden; 466 overflow-y: auto; 467 max-height: 350px; 468 min-width: 150px; 469 box-shadow: 0 0 5px rgba(0,0,0,0.1); 470 } 471 472 .dropdown li,.dropdown a{ 473 display: block; 474 } 475 .dropdown a{ 476 text-decoration: none; 477 padding: 2px 10px; 478 color: #000 !important; 479 } 480 481 .dropdown-item { 482 padding: 0.4rem 2rem; 483 cursor: pointer; 484 line-height: 1.4; 485 overflow: hidden; 486 text-overflow: ellipsis; 487 white-space: nowrap; 488 } 489 490 .dropdown-item:hover{ 491 background-color: #f3f3f3; 492 } 493 494 .search__input.is-focus{ 495 border-radius: 15px; 496 border-bottom-left-radius: 0; 497 border-bottom-right-radius: 0; 498 border: 1px solid rgba(var(--color-foreground), 1); 499 border-bottom: none; 500 box-shadow: none; 501 } 502 503 .clear-all { 504 text-align: right; 505 padding: 0 2rem 0.2rem; 506 line-height: 1; 507 font-size: 70%; 508 margin-bottom: -2px; 509 } 510 511 .clear-all:hover{ 512 background-color: #fff; 513 } 514 515 .referral-notification { 516 position: absolute; 517 top: 100%; 518 right: -12px; 519 background: white; 520 border: 1px solid #e0e0e0; 521 border-radius: 8px; 522 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 523 width: 280px; 524 z-index: 1000; 525 padding: 16px; 526 margin-top: 8px; 527 font-size: 14px; 528 line-height: 1.4; 529 } 530 531 .referral-notification::before { 532 content: ''; 533 position: absolute; 534 top: -8px; 535 right: 20px; 536 width: 0; 537 height: 0; 538 border-left: 8px solid transparent; 539 border-right: 8px solid transparent; 540 border-bottom: 8px solid white; 541 } 542 543 .referral-notification::after { 544 content: ''; 545 position: absolute; 546 top: -9px; 547 right: 20px; 548 width: 0; 549 height: 0; 550 border-left: 8px solid transparent; 551 border-right: 8px solid transparent; 552 border-bottom: 8px solid #e0e0e0; 553 } 554 555 .referral-notification h4 { 556 margin: 0 0 8px 0; 557 color: #2196F3; 558 font-size: 16px; 559 font-weight: 600; 560 } 561 562 .referral-notification p { 563 margin: 0 0 12px 0; 564 color: #666; 565 } 566 567 .referral-notification .referral-code { 568 background: #f5f5f5; 569 padding: 8px; 570 border-radius: 4px; 571 font-family: monospace; 572 font-size: 13px; 573 text-align: center; 574 margin: 8px 0; 575 border: 1px solid #ddd; 576 } 577 578 .referral-notification .close-btn { 579 position: absolute; 580 top: 8px; 581 right: 8px; 582 background: none; 583 border: none; 584 font-size: 18px; 585 color: #999; 586 cursor: pointer; 587 padding: 0; 588 width: 20px; 589 height: 20px; 590 line-height: 1; 591 } 592 593 .referral-notification .close-btn:hover { 594 color: #333; 595 } 596 597 .referral-notification .action-btn { 598 background: #2196F3; 599 color: white; 600 border: none; 601 padding: 8px 16px; 602 border-radius: 4px; 603 cursor: pointer; 604 font-size: 12px; 605 margin-right: 8px; 606 margin-top: 8px; 607 } 608 609 .referral-notification .action-btn:hover { 610 background: #1976D2; 611 } 612 613 .referral-notification .dismiss-btn { 614 background: transparent; 615 color: #666; 616 border: 1px solid #ddd; 617 padding: 8px 16px; 618 border-radius: 4px; 619 cursor: pointer; 620 font-size: 12px; 621 margin-top: 8px; 622 } 623 624 .referral-notification .dismiss-btn:hover { 625 background: #f5f5f5; 626 } 627 .referral-modal__dialog { 628 transform: translate(-50%, 0); 629 transition: transform var(--duration-default) ease, visibility 0s; 630 z-index: 10000; 631 position: fixed; 632 top: 20vh; 633 left: 50%; 634 width: 100%; 635 max-width: 350px; 636 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 637 border-radius: 12px; 638 background-color: #fff; 639 color: #000; 640 padding: 20px; 641 text-align: center; 642 } 643 .modal-overlay.referral-modal-overlay { 644 position: fixed; 645 top: 0; 646 left: 0; 647 width: 100%; 648 height: 100%; 649 background-color: rgba(0, 0, 0, 0.7); 650 z-index: 2001; 651 display: block; 652 opacity: 1;
653 transition: opacity var(--duration-default) ease, visibility 0s; 654 } 655</style> 656 657<div class="header-bar"> 658 <div class="wrapper medium-down--hide"> 659 <div class="large--display-table"> 660 661 <div class="header-bar__left large--display-table-cell"> 662 663 664 665 666 667 668 669 <div class="header-bar__module header-bar__search"> 670 671 <form action="/search" method="get" class="header-bar__search-form clearfix" role="search"> 672 673 <button type="submit" class="btn icon-fallback-text header-bar__search-submit"> 674 <span class="icon icon-search" aria-hidden="true"></span> 675 <span class="fallback-text">æå°</span> 676 </button> 677 <input type="search" name="q" value="" aria-label="æå°" class="header-bar__search-input" placeholder="æå°" autocomplete="off"> 678 <div class="dropdown" id="searchDropdown"></div> 679 <input type="hidden" name="search_history" id="search_history"> 680 </form> 681 682
683<script> 684 var _search_history_listings = "" 685 if(_search_history_listings != "") localStorage.setItem('searchHistory', JSON.stringify(_search_history_listings)); 686 687 function clearAll() { 688 localStorage.removeItem('searchHistory'); 689 690 var customer = ""; 691 if(customer) { 692 // Your logic to clear all data or call an endpoint 693 fetch('/account/search_histories', { 694 method: 'DELETE', 695 headers: { 696 "Content-Type": "application/json", 697 "X-Requested-With": "XMLHttpRequest" 698 }, 699 body: JSON.stringify({ 700 _token: "BpiVmNGjM7cuDtx8wKgnVgi9dDLn3bsnlk7tVmaT" 701 }), 702 }) 703 .then(response => response.json()) 704 } 705 } 706 // End - store view history in local storage 707 708 // search history dropdown 709 var searchInputs = document.querySelectorAll('input[type=search]'); 710 711 if(searchInputs.length > 0) { 712 713 searchInputs.forEach((searchInput)=>{ 714 715 searchInput.addEventListener('focus', function() { 716 var searchDropdown = searchInput.closest('form').querySelector('#searchDropdown'); 717 searchDropdown.innerHTML = ''; // Clear existing items 718 719 // retrieve data from local storage 720 var searchHistoryData = JSON.parse(localStorage.getItem('searchHistory')) || []; 721 722 // Convert JSON to string 723 var search_history_json = JSON.stringify(searchHistoryData); 724 725 // Set the value of the hidden input 726 searchInput.closest('form').querySelector("#search_history").value = search_history_json; 727 728 // create dropdown items 729 var clearAllItem = document.createElement('div'); 730 clearAllItem.className = 'dropdown-item clear-all'; 731 clearAllItem.textContent = 'Clear'; 732 clearAllItem.addEventListener('click', function() { 733 clearAll(); 734 searchDropdown.style.display = 'none'; 735 searchInput.classList.remove('is-focus'); 736 }); 737 searchDropdown.appendChild(clearAllItem); 738 739 // create dropdown items 740 searchHistoryData.forEach(function(item) { 741 var dropdownItem = document.createElement('div'); 742 dropdownItem.className = 'dropdown-item'; 743 dropdownItem.textContent = item.term; 744 dropdownItem.addEventListener('click', function() { 745 searchInput.value = item.term; 746 searchDropdown.style.display = 'none'; 747 }); 748 searchDropdown.appendChild(dropdownItem); 749 }); 750 751 if(searchHistoryData.length > 0){ 752 // display the dropdown 753 searchInput.classList.add('is-focus'); 754 document.body.classList.add('search-input-focus'); 755 searchDropdown.style.display = 'block'; 756 } 757 }); 758 759 searchInput.addEventListener("focusout", (event) => { 760 var searchDropdown = searchInput.closest('form').querySelector('#searchDropdown'); 761 searchInput.classList.remove('is-focus'); 762 searchDropdown.classList.add('transparent'); 763 setTimeout(()=>{ 764 searchDropdown.style.display = 'none'; 765 searchDropdown.classList.remove('transparent'); 766 document.body.classList.remove('search-input-focus'); 767 }, 400); 768 }); 769 }) 770 } 771 // End - search history dropdown 772</script>
772 773 </div> 774 775 776 777 </div> 778 779 <div class="header-bar__right large--display-table-cell"> 780 781 <ul class="header-bar__module header-bar__module--list" style="position: relative;"> 782 783 784 785 <li> 786 <a href="/account/login">ç»å ¥ Login</a> 787 </li><li>OR</li> 788 789 790 791 <li> 792 <a href="/account/register">註å帳è Register</a> 793 </li> 794 795 796 797 <!-- Referral Notification Popup for Desktop --> 798 <div id="referralNotification" class="referral-notification" style="display: none;"> 799 <button class="close-btn" onclick="dismissReferralNotification()">×</button> 800 <div style="text-align: center; margin-bottom: 16px;"> 801 <div style="width: 100px; height: 100px; margin: 0 auto;"> 802 <img src="https://resources.easystore.co/storefront/icons/channels/vector_referral_program.svg"> 803 </div> 804 </div> 805 <p id="referralSignupMessage" style="text-align: center; font-size: 14px; color: #666; margin: 0 0 20px 0;"> 806 æ¨ç好å鿍 åé¥éï¼ç«å³è¨»åé åã 807 </p> 808 <div style="text-align: center;"> 809 <button class="btn" onclick="goToSignupPage()" style="width: 100%; color: #fff;"> 810 註å 811 </button> 812 </div> 813 </div> 814 815 </ul> 816 817 818 819 820 821 <div class="header-bar__module"> 822 <span class="header-bar__sep" aria-hidden="true"></span> 823 <a href="/cart" class="cart-page-link"> 824 <span class="icon icon-cart header-bar__cart-icon" aria-hidden="true"></span> 825 </a> 826 </div> 827 828 <div class="header-bar__module"> 829 830 <a href="/cart" class="cart-page-link"> 831 è³¼ç©è»<span class="hidden-count">:</span> 832 <span class="cart-count header-bar__cart-count hidden-count">0</span> 833 </a> 834 </div> 835 836 837 838 839 840 </div> 841 </div> 842 </div> 843 <div class="wrapper large--hide" style="padding: 5px 30px 0;"> 844 <button type="button" class="mobile-nav-trigger" id="MobileNavTrigger"> 845 <span class="icon icon-hamburger" aria-hidden="true"></span> 846 é¸å® 847 </button> 848 849 850 851 <!-- Referral Notification Modal for Mobile --> 852 <details-modal class="referral-modal" id="referralMobileModal"> 853 <details> 854 <summary class="referral-modal__toggle" aria-haspopup="dialog" aria-label="Open referral modal" style="display: none;"> 855 </summary> 856 <div class="modal-overlay referral-modal-overlay"></div> 857 <div class="referral-modal__dialog" role="dialog" aria-modal="true" aria-label="Referral notification"> 858 <div class="referral-modal__content"> 859 <button type="button" class="modal__close-button" aria-label="Close" onclick="closeMobileReferralModal()" style="position: absolute; top: 16px; right: 16px; background: none; border: none; font-size: 24px; cursor: pointer;"> 860 × 861 </button> 862 863 <div style="margin-bottom: 24px;"> 864 <div style="width: 120px; height: 120px; margin: 0 auto 20px;"> 865 <img src="https://resources.easystore.co/storefront/icons/channels/vector_referral_program.svg" style="width: 100%; height: 100%;"> 866 </div> 867 </div> 868 869 <p id="referralSignupMessageMobile" style="font-size: 16px; color: #333; margin: 0 0 30px 0; line-height: 1.5;"> 870 æ¨ç好å鿍 åé¥éï¼ç«å³è¨»åé åã 871 </p> 872 873 <button type="button" class="btn" onclick="goToSignupPageFromMobile()" style="width: 100%; color: #fff;"> 874 註å 875 </button> 876 </div> 877 </div> 878 </details> 879 </details-modal> 880 881 882 883 884 <a href="/cart" class="cart-page-link mobile-cart-page-link">
885 <span class="icon icon-cart header-bar__cart-icon" aria-hidden="true"></span> 886 è³¼ç©è» 887 <span class="cart-count hidden-count">0</span> 888 </a> 889 <div class="mobile-cart-page-link" style="margin-right: 20px;"> 890 891 892 893 894 </div> 895 </div> 896 <ul id="MobileNav" class="mobile-nav large--hide"> 897 <li class="mobile-nav__link"> 898 899 <div class="header-bar__module header-bar__search"> 900 901 <form action="/search" method="get" class="header-bar__search-form clearfix" role="search"> 902 903 <button type="submit" class="btn icon-fallback-text header-bar__search-submit"> 904 <span class="icon icon-search" aria-hidden="true"></span> 905 <span class="fallback-text">æå°</span> 906 </button> 907 <input type="search" name="q" value="" aria-label="æå°" class="header-bar__search-input" placeholder="æå°" autocomplete="off"> 908 <div class="dropdown" id="searchDropdown"></div> 909 <input type="hidden" name="search_history" id="search_history"> 910 </form> 911 912
913<script> 914 var _search_history_listings = "" 915 if(_search_history_listings != "") localStorage.setItem('searchHistory', JSON.stringify(_search_history_listings)); 916 917 function clearAll() { 918 localStorage.removeItem('searchHistory'); 919 920 var customer = ""; 921 if(customer) { 922 // Your logic to clear all data or call an endpoint 923 fetch('/account/search_histories', { 924 method: 'DELETE', 925 headers: { 926 "Content-Type": "application/json", 927 "X-Requested-With": "XMLHttpRequest" 928 }, 929 body: JSON.stringify({ 930 _token: "BpiVmNGjM7cuDtx8wKgnVgi9dDLn3bsnlk7tVmaT" 931 }), 932 }) 933 .then(response => response.json()) 934 } 935 } 936 // End - store view history in local storage 937 938 // search history dropdown 939 var searchInputs = document.querySelectorAll('input[type=search]'); 940 941 if(searchInputs.length > 0) { 942 943 searchInputs.forEach((searchInput)=>{ 944 945 searchInput.addEventListener('focus', function() { 946 var searchDropdown = searchInput.closest('form').querySelector('#searchDropdown'); 947 searchDropdown.innerHTML = ''; // Clear existing items 948 949 // retrieve data from local storage 950 var searchHistoryData = JSON.parse(localStorage.getItem('searchHistory')) || []; 951 952 // Convert JSON to string 953 var search_history_json = JSON.stringify(searchHistoryData); 954 955 // Set the value of the hidden input 956 searchInput.closest('form').querySelector("#search_history").value = search_history_json; 957 958 // create dropdown items 959 var clearAllItem = document.createElement('div'); 960 clearAllItem.className = 'dropdown-item clear-all'; 961 clearAllItem.textContent = 'Clear'; 962 clearAllItem.addEventListener('click', function() { 963 clearAll(); 964 searchDropdown.style.display = 'none'; 965 searchInput.classList.remove('is-focus'); 966 }); 967 searchDropdown.appendChild(clearAllItem); 968 969 // create dropdown items 970 searchHistoryData.forEach(function(item) { 971 var dropdownItem = document.createElement('div'); 972 dropdownItem.className = 'dropdown-item'; 973 dropdownItem.textContent = item.term; 974 dropdownItem.addEventListener('click', function() { 975 searchInput.value = item.term; 976 searchDropdown.style.display = 'none'; 977 }); 978 searchDropdown.appendChild(dropdownItem); 979 }); 980 981 if(searchHistoryData.length > 0){ 982 // display the dropdown 983 searchInput.classList.add('is-focus'); 984 document.body.classList.add('search-input-focus'); 985 searchDropdown.style.display = 'block'; 986 } 987 }); 988 989 searchInput.addEventListener("focusout", (event) => { 990 var searchDropdown = searchInput.closest('form').querySelector('#searchDropdown'); 991 searchInput.classList.remove('is-focus'); 992 searchDropdown.classList.add('transparent'); 993 setTimeout(()=>{ 994 searchDropdown.style.display = 'none'; 995 searchDropdown.classList.remove('transparent'); 996 document.body.classList.remove('search-input-focus'); 997 }, 400); 998 }); 999 }) 1000 } 1001 // End - search history dropdown 1002</script>
1002 1003 </div> 1004 1005 </li> 1006 1007 1008 1009 <li class="mobile-nav__link" aria-haspopup="true"> 1010 1011 <a href="/" class="mobile-nav"> 1012 é¦é 1013 </a> 1014 1015 </li> 1016 1017 1018 <li class="mobile-nav__link" aria-haspopup="true"> 1019 1020 1021 <a href="/collections/all" class="mobile-nav__sublist-trigger"> 1022 ç¢åç®é 1023 <span class="icon-fallback-text mobile-nav__sublist-expand"> 1024 <span class="icon icon-plus" aria-hidden="true"></span> 1025 <span class="fallback-text">+</span> 1026</span> 1027<span class="icon-fallback-text mobile-nav__sublist-contract"> 1028 <span class="icon icon-minus" aria-hidden="true"></span> 1029 <span class="fallback-text">-</span> 1030</span> 1031 </a> 1032 <ul class="mobile-nav__sublist"> 1033 <li class="mobile-nav__sublist-link"> 1034 <a href="/collections/all">ç¢åç®é</a> 1035 </li> 1036 1037 <li class="mobile-nav__sublist-link"> 1038 <a href="/collections/ä¸-人ç©é¡">ï¼ä¸ï¼äººç©é¡</a> 1039 </li> 1040 1041 <li class="mobile-nav__sublist-link"> 1042 <a href="/collections/äº-å± å®¶ç¨åé¡">ï¼äºï¼å± å®¶ææ¥å¸¸ç¨åé¡</a> 1043 </li> 1044 1045 <li class="mobile-nav__sublist-link"> 1046 <a href="/collections/ä¸-建ç¯é¡">ï¼ä¸ï¼å»ºç¯é¡</a> 1047 </li> 1048 1049 <li class="mobile-nav__sublist-link"> 1050 <a href="/collections/å-交éå·¥å ·é¡">ï¼åï¼äº¤éå·¥å ·é¡</a> 1051 </li> 1052 1053 <li class="mobile-nav__sublist-link"> 1054 <a href="/collections/äº-è»äºé¡">ï¼äºï¼è»äºé¡</a> 1055 </li> 1056 1057 <li class="mobile-nav__sublist-link"> 1058 <a href="/collections/å -åç©é¡">ï¼å ï¼åç©é¡</a> 1059 </li> 1060 1061 <li class="mobile-nav__sublist-link"> 1062 <a href="/collections/ä¸-å¥å¹»çç©é¡">ï¼ä¸ï¼å¥å¹»çç©é¡</a> 1063 </li> 1064 1065 <li class="mobile-nav__sublist-link"> 1066 <a href="/collections/å «-å¹»æ³äººç©é¡">ï¼å «ï¼å¹»æ³äººç©é¡</a> 1067 </li> 1068 1069 <li class="mobile-nav__sublist-link"> 1070 <a href="/collections/ä¹-åå§-ç-åç©é¡">ï¼ä¹ï¼åå§ï¼çï¼åç©é¡</a> 1071 </li> 1072 1073 <li class="mobile-nav__sublist-link"> 1074 <a href="/collections/å-ååé¡">ï¼åï¼ååé¡</a> 1075 </li> 1076 1077 <li class="mobile-nav__sublist-link"> 1078 <a href="/collections/åä¸-é°å½±åæ»äº¡é¡">ï¼åä¸ï¼é°å½±åæ»äº¡é¡</a> 1079 </li> 1080 1081 <li class="mobile-nav__sublist-link"> 1082 <a href="/collections/åäº-宿åéæ§é¡">ï¼åäºï¼å®æåéæ§é¡</a> 1083 </li> 1084 1085 <li class="mobile-nav__sublist-link"> 1086 <a href="/collections/åä¸-å ç´ é¡">ï¼åä¸ï¼å ç´ é¡</a> 1087 </li> 1088 1089 <li class="mobile-nav__sublist-link"> 1090 <a href="/collections/åå-大èªç¶æ¯è§é¡">ï¼ååï¼å¤§èªç¶æ¯è§é¡</a> 1091 </li> 1092 1093 <li class="mobile-nav__sublist-link"> 1094 <a href="/collections/åäº-é£çµèåéé¡">ï¼åäºï¼é£çµèåéé¡</a> 1095 </li> 1096 1097 <li class="mobile-nav__sublist-link"> 1098 <a href="/collections/åå -å¯åµé åæä½ææ">ï¼åå ï¼å¯åµé åæä½ææ</a> 1099 </li> 1100 1101 <li class="mobile-nav__sublist-link">
1102 <a href="/collections/äºæçå¡">ï¼åä¸ï¼å ¨æ°/äºæçå¡/æ¸ç±</a> 1103 </li> 1104 1105 <li class="mobile-nav__sublist-link"> 1106 <a href="/collections/-åå «-æ¸ åç¹å¹">ï¼åå «ï¼æ¸ åç¹å¹</a> 1107 </li> 1108 1109 <li class="mobile-nav__sublist-link"> 1110 <a href="/collections/æ²éè¨å">æ²éè¨å</a> 1111 </li> 1112 1113 <li class="mobile-nav__sublist-link"> 1114 <a href="/collections/æ¬åºæå">æ¬åºæå Services</a> 1115 </li> 1116 1117 <li class="mobile-nav__sublist-link"> 1118 <a href="/collections/æä½ç©ä»¶">æä½ç©ä»¶</a> 1119 </li> 1120 1121 <li class="mobile-nav__sublist-link"> 1122 <a href="/collections/æ²éæ¸ç±">æ²éæ¸ç±</a> 1123 </li> 1124 1125 <li class="mobile-nav__sublist-link"> 1126 <a href="/collections/é è³¼åå">é è³¼åå</a> 1127 </li> 1128 1129 <li class="mobile-nav__sublist-link"> 1130 <a href="/collections/ç·ä¸èª²ç¨">ç·ä¸èª²ç¨</a> 1131 </li> 1132 1133 </ul> 1134 1135 </li> 1136 1137 1138 <li class="mobile-nav__link" aria-haspopup="true"> 1139 1140 <a href="/blogs/blog" class="mobile-nav"> 1141 æ¬åºé»é»æ»´æ»´ 1142 </a> 1143 1144 </li> 1145 1146 1147 <li class="mobile-nav__link" aria-haspopup="true"> 1148 1149 <a href="/pages/æ¬åºéè¡è½å¸³ä»æ¬¾è³è¨" class="mobile-nav"> 1150 æ¬åºéè¡è½å¸³ä»æ¬¾è³è¨ 1151 </a> 1152 1153 </li> 1154 1155 1156 <li class="mobile-nav__link" aria-haspopup="true"> 1157 1158 <a href="https://learn.sandplayshop.com" class="mobile-nav"> 1159 å¸ç¿ç¶² 1160 </a> 1161 1162 </li> 1163 1164 1165 <li class="mobile-nav__link" aria-haspopup="true"> 1166 1167 <a href="https://soul.douzistudio.com" class="mobile-nav"> 1168 測é©ç¶² 1169 </a> 1170 1171 </li> 1172 1173 1174 <li class="mobile-nav__link" aria-haspopup="true"> 1175 1176 <a href="/a/help-center" class="mobile-nav"> 1177 FAQ 1178 </a> 1179 1180 </li> 1181 1182 1183 1184 1185 1186 1187 1188 <li class="mobile-nav__link"> 1189 <a href="/account/login">ç»å ¥ Login</a> 1190 </li> 1191 1192 <li class="mobile-nav__link"> 1193 <a href="/account/register">註å帳è Register</a> 1194 </li> 1195 1196 1197 1198 1199</ul> 1200</div> 1201
1202<script> 1203 // referral notification popup 1204 const referralMessageTemplate = 'æ¨ç好å鿍 __CURRENCY_CODE__ __CREDIT_AMOUNT__ åé¥éï¼ç«å³è¨»åé åã'; 1205 const shopPrefix = 'NT$'; 1206 1207 function getCookie(name) { 1208 const value = `; ${document.cookie}`; 1209 const parts = value.split(`; ${name}=`); 1210 if (parts.length === 2) return parts.pop().split(';').shift(); 1211 return null; 1212 } 1213 1214 function removeCookie(name) { 1215 document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`; 1216 } 1217 1218 function showReferralNotification() { 1219 const referralCode = getCookie('customer_referral_code'); 1220 1221 1222 const customerReferralCode = null; 1223 1224 1225 const activeReferralCode = referralCode || customerReferralCode; 1226 const referralData = JSON.parse(localStorage.getItem('referral_notification_data')) || {}; 1227 1228 if (referralData.timestamp && referralData.expirationDays) { 1229 const expirationTime = referralData.timestamp + (referralData.expirationDays * 24 * 60 * 60 * 1000); 1230 if (Date.now() > expirationTime) { 1231 localStorage.removeItem('referral_notification_data'); 1232 return; 1233 } 1234 } 1235 1236 if (referralData.dismissed) return; 1237 1238 if (activeReferralCode) { 1239 if (referralData.creditAmount) { 1240 displayReferralNotification(referralData.creditAmount); 1241 } else { 1242 fetchReferralCampaignData(activeReferralCode); 1243 } 1244 } 1245 } 1246 1247 function fetchReferralCampaignData(referralCode) { 1248 fetch(`/customer/referral_program/campaigns/${referralCode}`) 1249 .then(response => { 1250 if (!response.ok) { 1251 throw new Error('Network response was not ok'); 1252 } 1253 return response.json(); 1254 }) 1255 .then(data => { 1256 let refereeCreditAmount = null; 1257 if (data.data.campaign && data.data.campaign.referral_rules) { 1258 const refereeRule = data.data.campaign.referral_rules.find(rule => 1259 rule.target_type === 'referee' && 1260 rule.event_name === 'customer/create' && 1261 rule.entitlement && 1262 rule.entitlement.type === 'credit' 1263 ); 1264 1265 if (refereeRule) { 1266 refereeCreditAmount = refereeRule.entitlement.amount; 1267 } 1268 } 1269 1270 const referralData = JSON.parse(localStorage.getItem('referral_notification_data')) || {}; 1271 referralData.creditAmount = refereeCreditAmount; 1272 referralData.dismissed = referralData.dismissed || false; 1273 referralData.timestamp = Date.now(); 1274 referralData.expirationDays = 3; 1275 localStorage.setItem('referral_notification_data', JSON.stringify(referralData)); 1276 1277 displayReferralNotification(refereeCreditAmount); 1278 }) 1279 .catch(error => { 1280 removeCookie('customer_referral_code'); 1281 }); 1282 } 1283 1284 function displayReferralNotification(creditAmount = null) { 1285 const isMobile = innerWidth <= 749; 1286 1287 if (isMobile) { 1288 const mobileModal = document.querySelector('#referralMobileModal details'); 1289 const mobileMessageElement = document.getElementById('referralSignupMessageMobile'); 1290 1291 if (mobileModal && mobileMessageElement) { 1292 if (creditAmount) { 1293 const finalMessage = referralMessageTemplate 1294 .replace('__CREDIT_AMOUNT__', creditAmount) 1295 .replace('__CURRENCY_CODE__', shopPrefix); 1296 1297 mobileMessageElement.textContent = finalMessage; 1298 } 1299 1300 mobileModal.setAttribute('open', ''); 1301 } 1302 } else { 1303 const notification = document.getElementById('referralNotification'); 1304 1305 if (notification) { 1306 if (creditAmount) { 1307 const messageElement = document.getElementById('referralSignupMessage'); 1308 if (messageElement) { 1309 const finalMessage = referralMessageTemplate 1310 .replace('__CREDIT_AMOUNT__', creditAmount) 1311 .replace('__CURRENCY_CODE__', shopPrefix); 1312 1313 messageElement.textContent = finalMessage; 1314 } 1315 } 1316 1317 notification.style.display = 'block'; 1318 } 1319 } 1320 } 1321 1322 showReferralNotification(); 1323 1324 addEventListener('beforeunload', function() { 1325 const referralData = JSON.parse(localStorage.getItem('referral_notification_data')) || {}; 1326 1327 if (referralData.creditAmount !== undefined) { 1328 referralData.creditAmount = null; 1329 localStorage.setItem('referral_notification_data', JSON.stringify(referralData)); 1330 } 1331 }); 1332 1333 function closeReferralNotification() { 1334 const notification = document.getElementById('referralNotification'); 1335 if (notification) { 1336 notification.style.display = 'none'; 1337 } 1338 } 1339 1340 function dismissReferralNotification() { 1341 const referralData = JSON.parse(localStorage.getItem('referral_notification_data')) || {}; 1342 1343 referralData.dismissed = true; 1344 referralData.timestamp = referralData.timestamp || Date.now(); 1345 referralData.expirationDays = referralData.expirationDays || 3; 1346 1347 localStorage.setItem('referral_notification_data', JSON.stringify(referralData)); 1348 1349 closeReferralNotification(); 1350 } 1351 1352 function goToSignupPage() { 1353 location.href = '/account/register'; 1354 dismissReferralNotification(); 1355 } 1356 1357 function closeMobileReferralModal() { 1358 const mobileModal = document.querySelector('#referralMobileModal details'); 1359 if (mobileModal) { 1360 mobileModal.removeAttribute('open'); 1361 } 1362 1363 const referralData = JSON.parse(localStorage.getItem('referral_notification_data')) || {}; 1364 referralData.dismissed = true; 1365 referralData.timestamp = referralData.timestamp || Date.now(); 1366 referralData.expirationDays = referralData.expirationDays || 3; 1367 localStorage.setItem('referral_notification_data', JSON.stringify(referralData)); 1368 } 1369 1370 function goToSignupPageFromMobile() { 1371 location.href = '/account/register'; 1372 closeMobileReferralModal(); 1373 } 1374 // End - referral notification popup 1375</script>
1375 1376 1377 <header class="site-header" role="banner"> 1378 1379 <div class="wrapper"> 1380 1381 1382 1383 <div class="grid--full"> 1384 <div class="grid__item"> 1385 1386 <div class="h1 site-header__logo" itemscope itemtype="http://schema.org/Organization"> 1387 1388 1389 <a href="/" itemprop="url">æ²éå¿èå·¥ä½å®¤</a> 1390 1391 1392 </div> 1393 1394 1395 <p class="header-message large--hide"> 1396 <small></small> 1397 </p> 1398 1399 </div> 1400 </div> 1401 <div class="grid--full medium-down--hide"> 1402 <div class="grid__item"> 1403 1404<ul class="site-nav" id="AccessibleNav"> 1405 1406 1407 1408 <li > 1409 <a href="/" class="site-nav__link">é¦é </a> 1410 </li> 1411 1412 1413 1414 1415 1416 <li class="site-nav--has-dropdown" aria-haspopup="true"> 1417 <a href="/collections/all" class="site-nav__link"> 1418 ç¢åç®é 1419 <span class="icon-fallback-text"> 1420 <span class="icon icon-arrow-down" aria-hidden="true"></span> 1421 </span> 1422 </a> 1423 <ul class="site-nav__dropdown"> 1424 1425 1426 1427 <li > 1428 <a href="/collections/ä¸-人ç©é¡" class="site-nav__link">ï¼ä¸ï¼äººç©é¡</a> 1429 </li> 1430 1431 1432 1433 1434 <li > 1435 <a href="/collections/äº-å± å®¶ç¨åé¡" class="site-nav__link">ï¼äºï¼å± å®¶ææ¥å¸¸ç¨åé¡</a> 1436 </li> 1437 1438 1439 1440 1441 <li > 1442 <a href="/collections/ä¸-建ç¯é¡" class="site-nav__link">ï¼ä¸ï¼å»ºç¯é¡</a> 1443 </li> 1444 1445 1446 1447 1448 <li > 1449 <a href="/collections/å-交éå·¥å ·é¡" class="site-nav__link">ï¼åï¼äº¤éå·¥å ·é¡</a> 1450 </li> 1451 1452 1453 1454 1455 <li > 1456 <a href="/collections/äº-è»äºé¡" class="site-nav__link">ï¼äºï¼è»äºé¡</a> 1457 </li> 1458 1459 1460 1461 1462 <li > 1463 <a href="/collections/å -åç©é¡" class="site-nav__link">ï¼å ï¼åç©é¡</a> 1464 </li> 1465 1466 1467 1468 1469 <li > 1470 <a href="/collections/ä¸-å¥å¹»çç©é¡" class="site-nav__link">ï¼ä¸ï¼å¥å¹»çç©é¡</a> 1471 </li> 1472 1473 1474 1475 1476 <li > 1477 <a href="/collections/å «-å¹»æ³äººç©é¡" class="site-nav__link">ï¼å «ï¼å¹»æ³äººç©é¡</a> 1478 </li> 1479 1480 1481 1482 1483 <li > 1484 <a href="/collections/ä¹-åå§-ç-åç©é¡" class="site-nav__link">ï¼ä¹ï¼åå§ï¼çï¼åç©é¡</a> 1485 </li> 1486 1487 1488 1489 1490 <li > 1491 <a href="/collections/å-ååé¡" class="site-nav__link">ï¼åï¼ååé¡</a> 1492 </li> 1493 1494 1495 1496 1497 <li > 1498 <a href="/collections/åä¸-é°å½±åæ»äº¡é¡" class="site-nav__link">ï¼åä¸ï¼é°å½±åæ»äº¡é¡</a> 1499 </li> 1500 1501 1502 1503 1504 <li > 1505 <a href="/collections/åäº-宿åéæ§é¡" class="site-nav__link">ï¼åäºï¼å®æåéæ§é¡</a> 1506 </li> 1507 1508 1509 1510 1511 <li > 1512 <a href="/collections/åä¸-å ç´ é¡" class="site-nav__link">ï¼åä¸ï¼å ç´ é¡</a> 1513 </li> 1514 1515 1516 1517 1518 <li > 1519 <a href="/collections/åå-大èªç¶æ¯è§é¡" class="site-nav__link">ï¼ååï¼å¤§èªç¶æ¯è§é¡</a> 1520 </li> 1521 1522 1523 1524 1525 <li > 1526 <a href="/collections/åäº-é£çµèåéé¡" class="site-nav__link">ï¼åäºï¼é£çµèåéé¡</a> 1527 </li> 1528 1529 1530 1531 1532 <li > 1533 <a href="/collections/åå -å¯åµé åæä½ææ" class="site-nav__link">ï¼åå ï¼å¯åµé åæä½ææ</a> 1534 </li> 1535 1536 1537 1538 1539 <li >
1540 <a href="/collections/äºæçå¡" class="site-nav__link">ï¼åä¸ï¼å ¨æ°/äºæçå¡/æ¸ç±</a> 1541 </li> 1542 1543 1544 1545 1546 <li > 1547 <a href="/collections/-åå «-æ¸ åç¹å¹" class="site-nav__link">ï¼åå «ï¼æ¸ åç¹å¹</a> 1548 </li> 1549 1550 1551 1552 1553 <li > 1554 <a href="/collections/æ²éè¨å" class="site-nav__link">æ²éè¨å</a> 1555 </li> 1556 1557 1558 1559 1560 <li > 1561 <a href="/collections/æ¬åºæå" class="site-nav__link">æ¬åºæå Services</a> 1562 </li> 1563 1564 1565 1566 1567 <li > 1568 <a href="/collections/æä½ç©ä»¶" class="site-nav__link">æä½ç©ä»¶</a> 1569 </li> 1570 1571 1572 1573 1574 <li > 1575 <a href="/collections/æ²éæ¸ç±" class="site-nav__link">æ²éæ¸ç±</a> 1576 </li> 1577 1578 1579 1580 1581 <li > 1582 <a href="/collections/é è³¼åå" class="site-nav__link">é è³¼åå</a> 1583 </li> 1584 1585 1586 1587 1588 <li > 1589 <a href="/collections/ç·ä¸èª²ç¨" class="site-nav__link">ç·ä¸èª²ç¨</a> 1590 </li> 1591 1592 1593 </ul> 1594 </li> 1595 1596 1597 1598 1599 <li > 1600 <a href="/blogs/blog" class="site-nav__link">æ¬åºé»é»æ»´æ»´</a> 1601 </li> 1602 1603 1604 1605 1606 <li > 1607 <a href="/pages/æ¬åºéè¡è½å¸³ä»æ¬¾è³è¨" class="site-nav__link">æ¬åºéè¡è½å¸³ä»æ¬¾è³è¨</a> 1608 </li> 1609 1610 1611 1612 1613 <li > 1614 <a href="https://learn.sandplayshop.com" class="site-nav__link">å¸ç¿ç¶²</a> 1615 </li> 1616 1617 1618 1619 1620 <li > 1621 <a href="https://soul.douzistudio.com" class="site-nav__link">測é©ç¶²</a> 1622 </li> 1623 1624 1625 1626 1627 <li > 1628 <a href="/a/help-center" class="site-nav__link">FAQ</a> 1629 </li> 1630 1631 1632 1633</ul> 1634 </div> 1635 </div> 1636 1637 1638 1639 </div> 1640 </header> 1641 1642 <main class="wrapper main-content" role="main"> 1643 <div class="grid"> 1644 <div class="grid__item">
vendor: 94 bytes, lines 1644-1648
1644 1645 1646 1647 1648<script src="https://www.recaptcha.net/recaptcha/enterprise.js?render=
16486LdMNdIqAAAAALegJp7y4weVcCIlnwmIN8TWseEF
vendor: 12 bytes, line 1648
1648"></script>
1649<link rel="preload" href="https://store-themes.easystore.co/217101/themes/338/assets/customer.css?t=1787072452" as="style" onload="this.onload=null;this.rel='stylesheet'"> 1650<style> 1651 main.main-content .field > label{ 1652 display: none; 1653 } 1654 .h3{ 1655 font-size: 18px; 1656 } 1657 .button--full-width{ 1658 width: 100%; 1659 } 1660 .tab-wrapper{ 1661 display: inline-flex; 1662 justify-content: center; 1663 align-items: center; 1664 margin: 10px auto 20px; 1665 background-color: #eaeaea; 1666 border-radius: 200px; 1667 padding: 5px; 1668 gap: 10px; 1669 } 1670 .tab-wrapper .tab-label { 1671 display: inline-block; 1672 min-width: 130px; 1673 padding: 8px 15px; 1674 border-radius: 100px; 1675 cursor: pointer; 1676 font-size: 17px; 1677 line-height: 1.1; 1678 user-select: none; 1679 margin: 0; 1680 color: #000; 1681 } 1682 #register-tos a { 1683 display: inline; 1684 } 1685 @media screen and (max-width: 400px){ 1686 .tab-wrapper .tab-label { 1687 min-width: 110px; 1688 } 1689 } 1690 .tab-wrapper input[type='radio']:checked + .tab-label { 1691 background-color: #fff; 1692 box-shadow: 2px 2px 25px -10px #333; 1693 } 1694 1695 @media screen and (min-width: 500px){ 1696 .large--five-twelfths{ 1697 min-width: 420px; 1698 } 1699 } 1700</style> 1701 1702 1703 1704 1705<div class="customer login section-width-control small-width large--five-twelfths text-center wrapper"> 1706 <div class="card"> 1707 <h1 id="login" tabindex="-1" class="mb-0"> 1708 æ¡è¿ 1709 </h1> 1710 <p class="h3 mt-0"> 1711 1712 註åæç»å ¥ä»¥ç¹¼çº 1713 1714 </p> 1715 <div> 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 <div class="tab-wrapper"> 1734 <div> 1735 <input type="radio" name="type" id="tab-register" class="hide hidden" checked> 1736 <label for="tab-register" class="tab-label"> 1737 註å帳è 1738 </label> 1739 </div> 1740 <div> 1741 <input type="radio" name="type" id="tab-login" class="hide hidden"> 1742 <label for="tab-login" class="tab-label"> 1743 ç»å ¥ 1744 </label> 1745 </div> 1746 </div> 1747 1748 1749 <form id="form-login" action="/account/auth" method="post" class="mt-2"> 1750 <input type="hidden" name="_token" value="BpiVmNGjM7cuDtx8wKgnVgi9dDLn3bsnlk7tVmaT"> 1751 <input type="hidden" name="location_id" value=""> 1752 <input type="hidden" name="staff_id" value=""> 1753 <input type="hidden" id="recaptchaResponse" name="g-recaptcha-response"> 1754 1755 1756 <div id="error-content"></div> 1757 1758 <div class="field"> 1759 <input 1760 type="text" 1761 name="customer[email_or_phone]" 1762 id="CustomerDetail" 1763 autocorrect="off" 1764 autocapitalize="off" 1765 placeholder="é»åéµä»¶å°å" 1766 required 1767 > 1768 <label for="CustomerDetail"> 1769 é»åéµä»¶å°å 1770 </label> 1771 </div> 1772 1773 <button 1774 type="submit" 1775 form="form-login" 1776 class="btn button--full-width g-recaptcha"> 1777 ç¹¼çº 1778 </button> 1779 1780 <div class="button-snippet"> 1781 1782<!-- Snippet:login/button: Facebook Login --> 1783 <style> 1784 #fb-login-btn.btn{ 1785 background-color: #fff; 1786 color: #000; 1787 padding: 0; 1788 overflow: hidden; 1789 display: flex; 1790 justify-content: center; 1791 align-items: center; 1792 width: 100%; 1793 margin: 15px auto; 1794 font-size: 14px; 1795 border: 1px solid currentColor; 1796 box-shadow: none; 1797 } 1798 #fb-login-btn.btn:hover{ 1799 background-color: #fff; 1800 } 1801 #fb-login-btn.btn svg{ 1802 width: 45px; 1803 vertical-align: middle; 1804 padding: 5px; 1805 } 1806 #fb-login-btn.btn .fb-login-text{ 1807 padding: 13px 75px 13px 30px; 1808 display: inline-block; 1809 width: calc(100% - 52px); 1810 } 1811 @media screen and (max-width: 500px) { 1812 #fb-login-btn.btn .fb-login-text{ 1813 padding: 13px 25px 13px 8px; 1814 font-size: 13px; 1815 } 1816 } 1817 </style> 1818 <button id="fb-login-btn" type="button" class="btn" onclick="fb_login()" style="background-color: #fff; color: #000; font-weight: normal;"> 1819 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 700"><g><path d="M650,350C650,184.32,515.68,50,350,50S50,184.32,50,350c0,140.69,96.86,258.74,227.53,291.17V441.68H215.66V350h61.87V310.5c0-102.11,46.2-149.44,146.4-149.44,19,0,51.81,3.72,65.22,7.45V251.6c-7.08-.74-19.38-1.12-34.65-1.12-49.2,0-68.2,18.64-68.2,67.08V350h98.1l-16.8,91.68H386.35V647.8C534.88,629.86,650,503.38,650,350Z" fill="#0866ff"/><path d="M467.5,441.68,484.4,350h-98V317.6c0-48.44,19-67.08,68.19-67.08,15.28,0,27.6.37,34.66,1.12V168.51c-13.42-3.73-46.21-7.45-65.22-7.45-100.25,0-146.4,47.33-146.4,149.44V350H2
181915.66v91.68h61.87V641.17a301.61,301.61,0,0,0,108.82,6.63V441.68Z" fill="#fff"/></g></svg> 1820 <span class="fb-login-text">以 Facebook 帳èç¹¼çº</span> 1821 </button> 1822 1823
1823<script> 1824 function fb_login(){ 1825 location.href = 'https://apps.easystore.co/facebook-login/run?domain='+window.location.protocol+'//'+window.location.hostname+'&shop=douzi08056714.easy.co'; 1826 } 1827 </script>
1827 1828<!-- /Snippet --> 1829 1830 <style> 1831 #line-login-btn.btn{ 1832 background-color: #fff; 1833 color: #000; 1834 padding: 0; 1835 overflow: hidden; 1836 display: flex; 1837 justify-content: center; 1838 align-items: center; 1839 width: 100%; 1840 margin: 15px auto; 1841 font-size: 14px; 1842 border: 1px solid currentColor; 1843 box-shadow: none; 1844 } 1845 #line-login-btn.btn:hover{ 1846 background-color: #fff; 1847 } 1848 #line-login-btn.btn svg{ 1849 width: 45px; 1850 vertical-align: middle; 1851 padding: 5px; 1852 } 1853 #line-login-btn.btn .line-login-text{ 1854 padding: 13px 75px 13px 30px; 1855 display: inline-block; 1856 width: calc(100% - 52px); 1857 } 1858 @media screen and (max-width: 500px) { 1859 #line-login-btn.btn .line-login-text{ 1860 padding: 13px 25px 13px 8px; 1861 font-size: 13px; 1862 } 1863 } 1864 </style> 1865 <button type="button" onclick="location.href='/account/oauth/line'" id="line-login-btn" class="btn" style="color: #000; font-weight: normal; margin-bottom: 10px;"> 1866 <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"> 1867 <path d="M27.91,12a8.73,8.73,0,0,0-.53-2,9.84,9.84,0,0,0-2.2-3.31A12.91,12.91,0,0,0,20.85,4a14.67,14.67,0,0,0-4-1A17.13,17.13,0,0,0,15,2.83c-.27,0-.55,0-.82,0l-.82.06a15.07,15.07,0,0,0-1.62.24,14.77,14.77,0,0,0-3,1A12.64,12.64,0,0,0,4.81,6.78a9.82,9.82,0,0,0-2.29,3.55A8.38,8.38,0,0,0,2,13a9.45,9.45,0,0,0,.1,1.5A8.52,8.52,0,0,0,2.88,17a10.52,10.52,0,0,0,3.23,3.84A13.68,13.68,0,0,0,10.5,23a14.8,14.8,0,0,0,2.36.5,2.31,2.31,0,0,1,.69.16,1,1,0,0,1,.58.72,2,2,0,0,1,0,.74c-.05.34-.12.67-.17,1a2.55,2.55,0,0,0,0,.55c0,.38.22.53.59.45a4.21,4.21,0,0,0,.63-.2A11.83,11.83,0,0,0,16.92,26a47.13,47.13,0,0,0,5.68-4c.67-.55,1.31-1.14,2-1.71a10.26,10.26,0,0,0,1.64-1.78,9.31,9.31,0,0,0,1.2-2.16,8.3,8.3,0,0,0,.59-3A9.5,9.5,0,0,0,27.91,12ZM10.26,16.52l-.18,0H7.49a.67.67,0,0,1-.67-.67v-5a.67.67,0,0,1,.66-.66.68.68,0,0,1,.71.7v4.26H10a.66.66,0,0,1,.6.31A.69.69,0,0,1,10.26,16.52Zm2.43-.58a.63.63,0,0,1-.63.6.65.65,0,0,1-.69-.46.89.89,0,0,1,0-.28V11a1.1,1.1,0,0,1,0-.26.68.68,0,0,1,.61-.48.64.64,0,0,1,.61.29.92.92,0,0,1,.11.29.72.72,0,0,1,0,.21v4.77A1,1,0,0,1,12.69,15.94Zm6.14,0a.64.64,0,0,1-.5.63.7.7,0,0,1-.76-.27C16.79,15.17,16,14.1,15.22,13c0,0-.06-.11-.16-.14v3a.6.6,0,0,1-.1.39.64.64,0,0,1-.52.3.79.79,0,0,1-.45-.11.66.66,0,0,1-.29-.52v-5a.67.67,0,0,1,.1-.39.7.7,0,0,1,1.14,0c.46.64.93,1.26,1.4,1.89l1,1.33.09.11c.06-.05,0-.1,0-.15V10.89a.65.65,0,0,1,.5-.65.67.67,0,0,1,.87.64v3.67C18.83,15,18.82,15.45,18.83,15.89Zm4.39.62a.93.93,0,0,1-.29,0H20.47a.76.76,0,0,1-.41-.1.62.62,0,0,1-.31-.51,1.09,1.09,0,0,1,0-.18c0-1.59,0-3.19,0-4.78a.88.88,0,0,1,0-.28.66.66,0,0,1,.63-.49H23a.63.63,0,0,1,.6.43.68.68,0,0,1-.16.76.58.58,0,0,1-.39.17H21.14a5.87,5.87,0,0,0,0,1.11h.17c.56,0,1.13,0,1.69,0a.64.64,0,0,1,.65.52.66.66,0,0,1-.66.84H21.14a6.56,6.56,0,0,0,0,1.09l.19,0h1.61a1,1,0,0,1,.24,0,.67.67,0,0,1,.5.63A.63.63,0,0,1,23.22,16.51Z" fill="#02C755"/> 1868 </svg> 1869 <span class="line-login-text">LINE ç»å ¥</span> 1870 </button> 1871 <!-- <p style="font-size: 12px; color: grey;">æåä½¿ç¨æ¨çLINEé»åéµä»¶åµå»ºå®¢æ¶å¸³æ¶</p> --> 1872 1873 </div> 1874 1875 1876 1877 <div id="register-tos"> 1878 䏿¦é»æè¨»åï¼å³è¡¨ç¤ºä½ åææ²éå¿èå·¥ä½å®¤ç <a target="_blank" href="/pages/terms-of-service">è³¼ç©èªªæ HOW TO BUY</a>, <a target="_blank" href="/pages/privacy-policy">é±ç§æ¬æ¿ç Privacy Policy</a> å <a target="_blank" href="/legal/refund-policy">éæè²¨è¾¦æ³ Returns Policy</a>ã 1879 </div> 1880 1881 </form> 1882 </div> 1883 </div> 1884 1885</div> 1886 1887 1888
1889<script> 1890 const register_tab = document.querySelector('#tab-register'); 1891 const login_tab = document.querySelector('#tab-login'); 1892 const register_tos = document.querySelector('#register-tos'); 1893 const isRegistrationEnabled = "1"; 1894 1895 if (register_tab) { 1896 register_tab.addEventListener('click', function() { 1897 if (register_tos) { 1898 register_tos.style.display = 'block'; 1899 } 1900 }); 1901 } 1902 if (login_tab) { 1903 login_tab.addEventListener('click', function() { 1904 if (register_tos) { 1905 register_tos.style.display = 'none'; 1906 } 1907 }); 1908 } 1909 1910 const methodSwitchBtn = document.getElementById('phone-login-btn'); 1911 if (methodSwitchBtn) { 1912 methodSwitchBtn.onclick = function () { 1913 var type = (register_tab && register_tab.checked) ? 'register' : 'login'; 1914 window.location.href = '/account/login?method=phone&type=' + type; 1915 }; 1916 } 1917 1918 let lastPathSegment = window.location.pathname.split('/').filter(segment => segment.length > 0).pop(); 1919 1920 // Redirect to login if trying to access register when self registration is disabled 1921 if (!isRegistrationEnabled) { 1922 if (lastPathSegment === 'register') { 1923 window.location.href = '/account/login'; 1924 } else if (lastPathSegment === 'login' || lastPathSegment === 'auth') { 1925 if (register_tos) { 1926 register_tos.style.display = 'none'; 1927 } 1928 } 1929 } else { 1930 // Only proceed with tab logic if not redirecting 1931 if(lastPathSegment === 'login') { 1932 if (login_tab) { 1933 login_tab.checked = true; 1934 } 1935 if (register_tos) { 1936 register_tos.style.display = 'none'; 1937 } 1938 } else { 1939 if (register_tos) { 1940 register_tos.style.display = 'block'; 1941 } 1942 } 1943 } 1944 1945 const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; 1946 const phonePattern = /^(?:\+?\d{1,3}[\s-]?)?(?:\d{1,14}[\s-]?){1,5}$/; 1947 1948 let form = document.querySelector('#form-login'), 1949 input = document.querySelector('#CustomerDetail'), 1950 error_content = document.querySelector('#error-content'); 1951 1952 1953 // One attempt at a time: the reCAPTCHA wait below is async, so without 1954 // this a double-click starts two continuations and posts the form twice. 1955 var submitting = false; 1956 1957 form.addEventListener('submit', async function (event) { 1958 // preventDefault FIRST: a re-entrant submit must still be cancelled, or the 1959 // browser performs a native post with no captcha token. 1960 event.preventDefault(); 1961 if (submitting) return; 1962 1963 let input_value = input.value 1964 error_content.innerHTML = ''; 1965 if (!input_value.match(emailPattern) && !input_value.match(phonePattern)) { 1966 error_content.innerHTML = `<div class="errors note">Invalid Email or Phone</div>`; 1967 return false; 1968 } 1969 1970 submitting = true; 1971 document.querySelector('#form-login .btn').classList.add('btn--loading','loading'); 1972 1973 // reCAPTCHA is unreachable from some regions (google.com is blocked in 1974 // mainland China). Fail open the same way checkout and forgot-password do: 1975 // submit without a token rather than leave the button spinning forever. 1976 try { 1977 if (window.grecaptcha && typeof window.grecaptcha.enterprise === 'object') { 1978 // The loader defines a stub {ready} synchronously and fetches the real 1979 // implementation asynchronously, so execute() may not exist yet. 1980 // ready() queues until it does; the timeout stops a bundle that never 1981 // arrives from hanging the button. 1982 await new Promise(function (resolve) { 1983 setTimeout(resolve, 3000); 1984 try { grecaptcha.enterprise.ready(resolve); } catch (e) { resolve(); } 1985 }); 1986 1987 const token = await grecaptcha.enterprise.execute('6LdMNdIqAAAAALegJp7y4weVcCIlnwmIN8TWseEF', { action: 'LOGIN' }); 1988 document.getElementById('recaptchaResponse').value = token; 1989 } 1990 } catch (e) { /* fail open */ } 1991 1992 document.getElementById('form-login').submit(); 1993 }); 1994 1995 1996</script>
1996 1997 1998 1999 2000 </div> 2001 </div> 2002 2003 </main> 2004 2005 <footer class="site-footer small--text-center" role="contentinfo"> 2006 2007 <div class="wrapper"> 2008 2009 <div class="grid-uniform"> 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 <div class="grid__item large--one-half medium--one-half"> 2032 2033 <h4>å¿«éé£çµ</h4> 2034 2035 <ul class="site-footer__links"> 2036 2037 <li><a href="/a/contact-form" >èæåè¯ç¹«</a></li> 2038 2039 <li><a href="/pages/contact" >è¯ç¹«æå</a></li> 2040 2041 <li><a href="/pages/æ²éç©ä»¶åå實æ³å±ç¤º" >æ¬åºåå實æ³å±ç¤º</a></li> 2042 2043 <li><a href="http://youtu.be/vURZAUa2tN4?si=o0tw733llvQQd1yB" >å¦ä½æ¶éæ²éç©ä»¶</a></li> 2044 2045 <li><a href="/pages/å®ä½æ¡è³¼æµç¨è辦æ³" >å®ä½æ¡è³¼æµç¨è辦æ³</a></li> 2046 2047 <li><a href="https://www.sandplayshop.com/products/æ²é-æ²éæ²»çæå" target="_blank" >æ²éæåï¼ç¾ æ±å°åé ç´å¶ï¼</a></li> 2048 2049 <li><a href="https://www.sandplayshop.com/blogs/blog/è©é¨å®£å°" >è©é¨å®£å°</a></li> 2050 2051 2052 </ul> 2053 </div> 2054 2055 2056 2057 <div class="grid__item large--one-half medium--one-half"> 2058 <h4>éæ³¨æå</h4> 2059 2060 <ul class="inline-list social-icons"> 2061 2062 2063 <li> 2064 <a class="icon-fallback-text" href="https://www.facebook.com/taiwansandplaytool/" title="æ²éå¿èå·¥ä½å®¤å¨Facebookä¸" target="_blank"> 2065 <span class="icon icon-facebook" aria-hidden="true"></span> 2066 <span class="fallback-text">Facebook</span> 2067 </a> 2068 </li> 2069 2070 2071 2072 <li> 2073 <a class="icon-fallback-text" href="https://plus.google.com/u/0/107783961947828243832" title="æ²éå¿èå·¥ä½å®¤å¨Google Plusä¸" rel="publisher" target="_blank"> 2074 <span class="icon icon-google_plus" aria-hidden="true"></span> 2075 <span class="fallback-text">Google</span> 2076 </a> 2077 </li> 2078 2079 2080 <li> 2081 <a class="icon-fallback-text" href="https://instagram.com/liujiadouzi" title="æ²éå¿èå·¥ä½å®¤å¨Instagramä¸" target="_blank"> 2082 <span class="icon icon-instagram" aria-hidden="true"></span> 2083 <span class="fallback-text">Instagram</span> 2084 </a> 2085 </li> 2086 2087 2088 <li> 2089 <a class="icon-fallback-text" href="https://wetouchtraining.tumblr.com/" title="æ²éå¿èå·¥ä½å®¤å¨Tumblrä¸" target="_blank"> 2090 <span class="icon icon-tumblr" aria-hidden="true"></span> 2091 <span class="fallback-text">Tumblr</span> 2092 </a> 2093 </li> 2094 2095 2096 <li> 2097 <a class="icon-fallback-text" href="https://www.youtube.com/channel/UC5a_nixFF60mQq9gcIb7WIw?sub_confirmation=1" title="æ²éå¿èå·¥ä½å®¤å¨YouTubeä¸" target="_blank"> 2098 <span class="icon icon-youtube" aria-hidden="true"></span> 2099 <span class="fallback-text">YouTube</span> 2100 </a> 2101 </li> 2102 2103 2104 2105 <li> 2106 <a class="icon-fallback-text" href="http://sandplay.siterubix.com/blog" title="æ²éå¿èå·¥ä½å®¤å¨RSSä¸" target="_blank"> 2107 <span class="icon icon-rss" aria-hidden="true"></span> 2108 <span class="fallback-text">RSS</span> 2109 </a> 2110 </li> 2111 2112 2113</ul> 2114 2115 </div> 2116 2117 </div> 2118 2119 <hr class="hr--small hr--clear"> 2120 2121 2122 <div class="grid"> 2123 2124 <div class="grid__item text-center"> 2125 <ul class="inline-list payment-icons"> 2126 2127 <li> 2128 <span class="icon-fallback-text"> 2129 <span class="icon icon-visa" aria-hidden="true"></span> 2130 <span class="fallback-text">Visa</span> 2131 </span> 2132 </li> 2133 2134 2135 <li>
2136 <span class="icon-fallback-text"> 2137 <span class="icon icon-master" aria-hidden="true"></span> 2138 <span class="fallback-text">Master</span> 2139 </span> 2140 </li> 2141 2142 2143 2144 2145 2146 </ul> 2147 </div> 2148 2149 2150 <div class="grid__item text-center"> 2151 <ul class="inline-list payment-icons"> 2152 2153 <li> 2154 <img src="https://s3.dualstack.ap-southeast-1.amazonaws.com/eesb.public/images/payment/payments_ecpay.svg" alt="payment_ecpay"> 2155 </li> 2156 2157 <li> 2158 <img src="https://s3.dualstack.ap-southeast-1.amazonaws.com/eesb.public/images/payment/payments_bank-transfer.svg" alt="payment_bank-transfer"> 2159 </li> 2160 2161 <li> 2162 <img src="https://s3.dualstack.ap-southeast-1.amazonaws.com/eesb.public/images/payment/payments_paypal.svg" alt="payment_paypal"> 2163 </li> 2164 2165 </ul> 2166 </div> 2167 2168 2169 <div class="grid__item text-center"> 2170 <ul class="inline-list payment-icons"> 2171 <li> 2172 <img src="https://s3-ap-southeast-1.amazonaws.com/eesb.public/images/payment/ssl_certified.png"> 2173 </li> 2174 </ul> 2175 </div> 2176 2177 </div> 2178 2179 2180 <div class="grid"> 2181 <div class="grid__item text-center"> 2182 <p class="site-footer__links">@ ï¼æ¬é ï¼æ²éå¿èå·¥ä½å®¤ Sandplay Creative Studio 統編ï¼76392596ï¼ æ²éç©ä»¶å°å¯¶å°ç« @</p> 2183 2184 <p class="site-footer__links"> 2185 2186 2187 2188 <a href="/pages/terms-of-service">è³¼ç©èªªæ HOW TO BUY</a> 2189 2190 2191 2192 2193 | <a href="/pages/privacy-policy">é±ç§æ¬æ¿ç Privacy Policy</a> 2194 2195 2196 2197 2198 | <a href="/legal/refund-policy">éæè²¨è¾¦æ³ Returns Policy</a> 2199 2200 2201 2202 </p> 2203 2204 </div> 2205 </div> 2206 2207 </div> 2208 </footer> 2209 2210 2211 2212
2212<script src="https://store-themes.easystore.co/217101/themes/338/assets/fastclick.min.js?t=1787072452" type="text/javascript" ></script>
2212 2213
2213<script src="https://store-themes.easystore.co/217101/themes/338/assets/timber.js?t=1787072452" type="text/javascript" ></script>
2213 2214
2214<script src="https://store-themes.easystore.co/217101/themes/338/assets/theme.js?t=1787072452" type="text/javascript" ></script>
2214 2215 2216 2217 2218 2219 2220 2221
2222<script src="/assets/currencies.js" type="text/javascript" ></script>
vendor: 2 bytes, line 2222
2222
2223<script src="/assets/jquery.currencies.min.js" type="text/javascript" ></script>
vendor: 2 bytes, line 2223
2223
2224<script src="/assets/currencies_format.js" type="text/javascript" ></script>
2224 2225
2226<script> 2227Currency.format = "money_with_currency_format"; 2228var shopCurrency = 'TWD'; 2229var selectedCurrency = ''; 2230 2231 2232 Currency.convert = function(amount, from, to) { 2233 return amount; 2234 }; 2235 2236 2237if( $.cookie("currency") == null ){ 2238 2239 if( selectedCurrency != false ) { 2240 2241 Currency.convertAll(shopCurrency, selectedCurrency); 2242 jQuery('.selected-currency').text(Currency.currentCurrency); 2243 2244 } 2245 2246} 2247 2248/* Sometimes merchants change their shop currency, let's tell our JavaScript file */ 2249// Currency.moneyFormats[shopCurrency].money_with_currency_format = ""; 2250// Currency.moneyFormats[shopCurrency].money_format = "NT$ {{amount}} "; 2251 2252/* Default currency */ 2253var defaultCurrency = 'TWD'; 2254 2255/* Cookie currency */ 2256var cookieCurrency = Currency.cookie.read(); 2257 2258$("input[name=current_currency]").val(cookieCurrency); 2259if(cookieCurrency == null){ 2260 cookieCurrency = shopCurrency; 2261 Currency.cookie.write(shopCurrency); 2262} 2263/* Fix for customer account pages */ 2264jQuery('span.money span.money').each(function() { 2265 jQuery(this).parents('span.money').removeClass('money'); 2266}); 2267/* Saving the current price */ 2268jQuery('span.money').each(function() { 2269 jQuery(this).attr('data-currency-TWD', jQuery(this).html()); 2270}); 2271// If there's no cookie. 2272if (cookieCurrency == null) { 2273 if (shopCurrency !== defaultCurrency) { 2274 Currency.convertAll(shopCurrency, defaultCurrency); 2275 } 2276 else { 2277 Currency.currentCurrency = defaultCurrency; 2278 } 2279} 2280// If the cookie value does not correspond to any value in the currency dropdown. 2281else if (jQuery('[name=currencies]').size() && jQuery('[name=currencies] option[value=' + cookieCurrency + ']').size() === 0) { 2282 Currency.currentCurrency = shopCurrency; 2283 Currency.cookie.write(shopCurrency); 2284} 2285else if (cookieCurrency === shopCurrency) { 2286 Currency.currentCurrency = shopCurrency; 2287 Currency.convertAll(shopCurrency, cookieCurrency); 2288} 2289else { 2290 Currency.convertAll(shopCurrency, cookieCurrency); 2291} 2292jQuery('[name=currencies]').val(Currency.currentCurrency).change(function() { 2293 var newCurrency = jQuery(this).val(); 2294 2295 2296 Currency.cookie.write(newCurrency) 2297 if ('customers' == 'cart') { 2298 const params = new URLSearchParams(window.location.search); 2299 params.set('delete_cache', 1); 2300 window.location.search = params; 2301 } else { 2302 window.location.reload(); 2303 } 2304 2305}); 2306var original_selectCallback = window.selectCallback; 2307var selectCallback = function(variant, selector) { 2308 var currencies = null; 2309 original_selectCallback(variant, selector); 2310 if(typeof jQuery('[name=currencies]').val() == "undefined"){ 2311 currencies = shopCurrency; 2312 } 2313 else{ 2314 currencies = jQuery('[name=currencies]').val(); 2315 } 2316 Currency.convertAll(shopCurrency, currencies); 2317 jQuery('.selected-currency').text(Currency.currentCurrency); 2318}; 2319$('body').on('ajaxCart.afterCartLoad', function(cart) { 2320 Currency.convertAll(shopCurrency, jQuery('[name=currencies]').val()); 2321 jQuery('.selected-currency').text(Currency.currentCurrency); 2322}); 2323jQuery('.selected-currency').text(Currency.currentCurrency); 2324</script>
2324 2325 2326
2327<script> 2328 2329 /* 2330 2331 ------ 2332 Events 2333 ------ 2334 2335 pages/viewed 2336 2337 customers/signup 2338 2339 customers/login 2340 2341 products/searched 2342 2343 collections/viewed 2344 2345 products/viewed 2346 2347 products/shared 2348 2349 wishlists/item_added 2350 2351 carts/items_added 2352 2353 carts/viewed 2354 2355 carts/item_removed 2356 2357 checkouts/initiated 2358 2359 checkouts/shipping_info_added 2360 2361 checkouts/payment_info_added 2362 2363 checkouts/completed 2364 2365 orders/placed 2366 2367 orders/purchased 2368 2369 payments/captured 2370 2371 payments/failed 2372 2373 */ 2374 2375 window.__latest_cart = window.__latest_cart || null; 2376 2377 const _selector = document.querySelector.bind(document), 2378 _selectorAll = document.querySelectorAll.bind(document) 2379 2380 document.addEventListener('DOMContentLoaded', (event) => { 2381 2382 const default_currency = getCookie("currency") 2383 const customer_id = window.__st.cid 2384 const template = window.__st.p 2385 2386 onPageView() 2387 2388 //-----------------------// 2389 // Configs // 2390 //-----------------------// 2391 2392 window.dataLayer = window.dataLayer || [] 2393 2394 Array.prototype.last = Array.prototype.last || function() { 2395 return this[this.length - 1] || null 2396 } 2397 2398 Array.prototype.first = Array.prototype.first || function() { 2399 return this[0] || null 2400 } 2401 2402 //----------------------// 2403 // Triggers // 2404 //----------------------// 2405 2406 const XHR = window.XMLHttpRequest 2407 2408 function xhr() { 2409 2410 const xhr = new XHR() 2411 2412 xhr.addEventListener("readystatechange", function() { 2413 2414 if(xhr.readyState != 4) return 2415 2416 try { 2417 2418 const response_url = xhr.responseURL 2419 2420 if (response_url.includes("cart/add")) { 2421 2422 window.__latest_cart = JSON.parse(xhr.response) 2423 2424 onCartItemsAdded() // ok 2425 2426 } 2427 2428 if (response_url.includes("cart/remove_item_quantity")) { 2429 2430 window.__latest_cart = JSON.parse(xhr.response) 2431 2432 onCartItemRemoved() // ok 2433 2434 } 2435 2436 if (response_url.includes("new_cart?retrieve=true")) { 2437 2438 result = JSON.parse(xhr.response) 2439 2440 if (result.cart) { 2441 window.__latest_cart = result.cart 2442 } 2443 2444 } 2445 2446 } catch(e) { 2447 2448 console.error(e) 2449 2450 } 2451 2452 }, false); 2453 2454 return xhr 2455 2456 } 2457 2458 window.XMLHttpRequest = xhr 2459 2460 switch(template) { 2461 2462 case 'cart': onCartView() // ok 2463 break 2464 case 'product': onProductView() // ok 2465 break 2466 case 'collection': onCollectionView() // ok 2467 break 2468 case 'blog': onBlogView() 2469 break 2470 case 'article': onArticleView() 2471 break 2472 case 'payment_completed': onOrderPlace() 2473 break 2474 case 'payment_fail': onPaymentFail() 2475 break 2476 2477 } 2478 2479 // _selector('form[action="/checkout/payments"]').addEventListener("submit", onPaymentInfoAdded(_selector('form[action="/checkout/payments"]'))) 2480 if (_selector('form[action="/checkout/detail"]')) _selector('form[action="/checkout/detail"]').addEventListener("submit", ()=>{onShippingInfoAdded(_selector('form[action="/checkout/detail"]'))}) // ok 2481 if (_selector('form[action="/checkout/shipping"]')) _selector('form[action="/checkout/shipping"]').addEventListener("submit", ()=>{onShippingInfoAdded(_selector('form[action="/checkout/shipping"]'))}) // ok 2482 if (_selector('form[action="/account/register"]')) _selector('form[action="/account/register"]').addEventListener("submit", onSignUp) // ok 2483 if (_selector('form[action="/account/login"]')) _selector('form[action="/account/login"]').addEventListener("submit", onLogin) // ok 2484 if (_selector('form[action="/search"]')) _selector('form[action="/search"]').addEventListener("submit", onProductSearch()) // ok 2485 if (_selector('#line-login-btn')) _selector('#line-login-btn').addEventListener('click', onLineLogin) // ok 2486 if (_selector('#PlaceOrder')) _selector('#PlaceOrder').addEventListener("click", ()=>{onCheckoutComplete(_selector('form[action="/checkout/payments"]'))}); // ok 2487 if (_selector('#add_wishlist')) _selector('#add_wishlist').addEventListener("click", onWishlistItemAdded) // ok 2488 if (_selectorAll('a[href="/account/logout"]')) _selectorAll('a[href="/account/logout"]').forEach(logoutButton => logoutButton.addEventListener("click", onLogout)) // ok 2489 if (_selector('.CartDrawerTrigger.cart-page-link')) _selector('.CartDrawerTrigger.cart-page-link').addEventListener("click", onCartView) // ok 2490 if (_selector('.CartDrawerTrigger.cart-page-link.mobile-cart-page-link')) _selector('.CartDrawerTrigger.cart-page-link.mobile-cart-page-link').addEventListener("click", onCartView) // ok 2491 if (_selectorAll('a[class^="share-"]')) _selectorAll('a[class^="share-"]').forEach(shareButton => shareButton.addEventListener("click", ()=>{onProductShare(shareButton)}) ) // ok 2492 if (_selectorAll('[name$="checkout"]')) _selectorAll('[name$="checkout"]').forEach(checkoutButton => checkoutButton.addEventListener("click", onCheckoutInitiate)) 2493 2494 // For append elements 2495 const bodyMutationObserver = new MutationObserver(() => { 2496 if (_selector('#form__spc #PlaceOrder')) _selector('#form__spc #PlaceOrder').addEventListener("click", onSinglePageCheckout) // ok 2497 if (_selectorAll('[name$="checkout"]')) _selectorAll('[name$="checkout"]').forEach(checkoutButton => checkoutButton.addEventListener("click", onCheckoutInitiate)) 2498 }); 2499 bodyMutationObserver.observe(_selector("body"), {subtree: true, childList: true}); 2500 2501 //------------------------------// 2502 // Event handlers // 2503 //------------------------------// 2504 2505 function onPageView() { 2506 2507 EasyStore.Event.dispatch('pages/viewed', { 2508 page: { 2509 type: template, 2510 title: document.title, 2511 description: _selector('meta[name=description]') ? _selector('meta[name=description]').getAttribute('content') : null, 2512 url: location.href, 2513 } 2514 }) 2515 2516 } 2517 2518 function onSignUp() { 2519 2520 const email_regex = new RegExp(/^[a-zA-Z0-9.!#$%&"*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/) 2521 const phone_regex = new RegExp(/^(\+?6?01)[0-46-9]-*[0-9]{7,8}$/) 2522 2523 const email_or_phone = _selector(`input[name="customer[email_or_phone]"]`).value.replace("+", "").replace(/\s+/, "") 2524 2525 let data = {} 2526 2527 if (email_regex.test(email_or_phone)) { 2528 data.method = 'email' 2529 data.email = email_or_phone 2530 } 2531 2532 if (phone_regex.test(email_or_phone)) { 2533 data.method = "phone" 2534 data.phone = email_or_phone 2535 } 2536 2537 if (!data.method) return 2538 2539 EasyStore.Event.dispatch('customers/signup', data) 2540 2541 } 2542 2543 function onLogin() { 2544 2545 const email_regex = new RegExp(/^[a-zA-Z0-9.!#$%&"*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/) 2546 const phone_regex = new RegExp(/^(\+?6?01)[0-46-9]-*[0-9]{7,8}$/) 2547 2548 const email_or_phone = _selector(`input[name="customer[email_or_phone]"]`).value.replace("+", "").replace(/\s+/, "") 2549 2550 let data = {} 2551 2552 if (email_regex.test(email_or_phone)) { 2553 data.method = 'email' 2554 data.email = email_or_phone 2555 } 2556 2557 if (phone_regex.test(email_or_phone)) { 2558 data.method = "phone" 2559 data.phone = email_or_phone 2560 } 2561 2562 if (!data.method) return 2563 2564 EasyStore.Event.dispatch('customers/login', data) 2565 2566 } 2567 2568 function onLineLogin() { 2569 2570 EasyStore.Event.dispatch('customers/login', { method: 'line' }) 2571 2572 } 2573 2574 function onLogout() { 2575 2576 EasyStore.Event.dispatch('customers/logout', { customer_id }) 2577 2578 } 2579 2580 function onBlogView() { 2581 2582 const blog = "" 2583 2584 EasyStore.Event.dispatch('blogs/viewed', { blog }) 2585 2586 } 2587 2588 function onArticleView() { 2589 2590 const article = "" 2591 2592 EasyStore.Event.dispatch('articles/viewed', { article }) 2593 2594 } 2595 2596 function onProductSearch() { 2597 2598 const query = _selector('input[name=q]').value 2599 2600 if (!query) return 2601 2602 EasyStore.Event.dispatch('products/searched', { query }) 2603 2604 } 2605 2606 function onCollectionView() { 2607 2608 const collection = "" 2609 2610 EasyStore.Event.dispatch('collections/viewed', { collection }) 2611 2612 } 2613 2614 function onProductView() { 2615 2616 const product = "" 2617 2618 EasyStore.Event.dispatch("products/viewed", { product }) 2619 2620 } 2621 2622 function onProductShare(el) { 2623 2624 const product = "" 2625 2626 const channel = el.getAttribute("class").split("-").last() 2627 2628 EasyStore.Event.dispatch('products/shared', { product, channel }) 2629 2630 } 2631 2632 function onWishlistItemAdded() { 2633 2634 let product = "" 2635 const quantity = _selector("#Quantity").value 2636 2637 EasyStore.Event.dispatch('wishlists/item_added', { product }) 2638 2639 } 2640 2641 async function onCartItemsAdded() { 2642 2643 const cart = await getCart() 2644 2645 if(cart && cart.items != undefined && cart.items.length > 0) { 2646 2647 EasyStore.Event.dispatch('carts/item_added', { cart }) 2648 2649 } 2650 2651 } 2652 2653 async function onCartView() { 2654 2655 const cart = await getCart() 2656 2657 EasyStore.Event.dispatch('carts/viewed', { cart }) 2658 2659 } 2660 2661 async function onCartItemRemoved() { 2662 2663 const cart = await getCart() 2664 2665 cart.items = [] 2666 2667 if(cart) { 2668 2669 EasyStore.Event.dispatch('carts/item_removed', { cart }) 2670 2671 } 2672 2673 } 2674 2675 async function onCheckoutInitiate() { 2676 2677 const cart = await getCart() 2678 2679 EasyStore.Event.dispatch('checkouts/initiated', { cart }) 2680 2681 } 2682 2683 async function onShippingInfoAdded(form) { 2684 let checkout = getCheckout() 2685 2686 let form_data = {} 2687 new FormData(form).forEach((value, key) => form_data[key] = value) 2688 2689 let shipping_tier = null 2690 2691 if (form_data.type && form_data.type.startsWith("r_pickup")) { 2692 shipping_tier = `Pickup - ${form_data.pick_location}` 2693 } 2694 2695 if (form_data.s_id) {
2696 shipping_tier = _selector(`input[id='`+form_data.s_id+`']`).getAttribute("data-shipping-name") 2697 } 2698 2699 if (!shipping_tier) return 2700 2701 EasyStore.Event.dispatch('checkouts/shipping_info_added', { checkout, shipping_tier }) 2702 2703 } 2704 2705 async function onPaymentInfoAdded(form) { 2706 2707 let checkout = getCheckout() 2708 2709 let payment_type = new FormData(form).get('payment_method') 2710 2711 EasyStore.Event.dispatch("checkouts/payment_info_added", { checkout, payment_type }) 2712 2713 } 2714 2715 async function onOrderPlace() { 2716 2717 let cart_token = getCookie("cart_js") 2718 let previous_cart_token = getCookie("previous_cart_ga4_js") 2719 2720 if (previous_cart_token && previous_cart_token == cart_token) { 2721 // Prevent duplicate purchase tracking 2722 return 2723 } 2724 2725 let order = getOrder() 2726 2727 EasyStore.Event.dispatch('orders/placed', { order }) 2728 2729 if(order.is_manual_payment) { 2730 2731 EasyStore.Event.dispatch('orders/purchased', { order }) 2732 2733 } 2734 2735 const last_transaction = order.transactions.last() 2736 2737 if (last_transaction.status) { 2738 2739 EasyStore.Event.dispatch('orders/purchased', { order }) 2740 EasyStore.Event.dispatch('payments/captured', { order }) 2741 2742 } 2743 2744 } 2745 2746 async function onPaymentFail() { 2747 2748 const order = getOrder() 2749 2750 EasyStore.Event.dispatch('payments/failed', { order }) 2751 2752 } 2753 2754 async function onCheckoutComplete(form) { 2755 2756 onPaymentInfoAdded(form) 2757 2758 const checkout = getCheckout() 2759 2760 EasyStore.Event.dispatch('checkouts/completed', { checkout }) 2761 2762 } 2763 2764 async function onSinglePageCheckout() { 2765 2766 // let checkout = $("[data-app-checkout]").data("app-checkout") 2767 2768 let checkout = getCheckout() 2769 2770 // const payment_type = _selector("#app_spc_payment_method").find(`[class*="label-content"]`).first().find("b").first().text() 2771 const payment_type = _selector("#app_spc_payment_method").getElementsByClassName('label-content')[0].getElementsByTagName('b')[0].innerHTML 2772 2773 const shipping_method = _selector("#delivery_method").value 2774 2775 let shipping_tier = null 2776 let app_spc_customer_info_label = _selector("#app_spc_customer_info").getElementsByClassName('label-content') 2777 if (shipping_method == "shipping") { 2778 shipping_tier = app_spc_customer_info_label[app_spc_customer_info_label.length - 1].getElementsByTagName('b')[0].innerHTML 2779 } 2780 2781 if (shipping_method == "pickup") { 2782 shipping_tier = app_spc_customer_info_label[0].innerHTML 2783 } 2784 2785 if (shipping_tier) { 2786 2787 EasyStore.Event.dispatch('checkouts/shipping_info_added', { 2788 checkout, 2789 shipping_tier, 2790 }) 2791 2792 } 2793 2794 if (payment_type) { 2795 2796 EasyStore.Event.dispatch('checkouts/payment_info_added', { 2797 checkout, 2798 payment_type, 2799 }) 2800 2801 } 2802 2803 EasyStore.Event.dispatch('checkouts/completed', { checkout }) 2804 2805 } 2806 2807 }) 2808 2809 2810 //-----------------------// 2811 // Functions // 2812 //-----------------------// 2813 2814 function getCookie(name) { 2815 2816 name += "=" 2817 2818 decodedCookie = decodeURIComponent(document.cookie) 2819 2820 ca = decodedCookie.split(";") 2821 2822 for(i = 0; i < ca.length; i++) { 2823 c = ca[i] 2824 while (c.charAt(0) == " ") { 2825 c = c.substring(1) 2826 } 2827 if (c.indexOf(name) == 0) { 2828 return c.substring(name.length, c.length) 2829 } 2830 } 2831 2832 return "" 2833 2834 } 2835 2836 const parsePrice = price => { 2837 if(typeof price === 'string') { 2838 return parseFloat(price.split(',').join('')) 2839 } 2840 2841 return price 2842 } 2843 2844 const requestCart = async (method, data) => { 2845 2846 let response = await fetch('/cart.json', {method: 'GET',headers: {'Content-Type': 'application/json'}}) 2847 result = await response.json() 2848 2849 return result.cart 2850 2851 } 2852 const getCart = async (latest = false) => { 2853 2854 let cart = window.__latest_cart 2855 2856 const cart_invalid = !cart || !cart.items || (cart.total_price > 0 && !cart.items.length) 2857 2858 if(latest || cart_invalid) { 2859 cart = await requestCart() 2860 } 2861 2862 cart.items = cart.items || [] 2863 2864 return mapCart(cart) 2865 2866 } 2867 2868 const getCheckout = async () => { 2869 mapCheckout(window.__latest_cart || await requestCart()) 2870 } 2871 2872 const getOrder = async () => { 2873 mapOrder(window.__latest_cart || await requestCart()) 2874 } 2875 2876 //-----------------------// 2877 // Mappers // 2878 //-----------------------// 2879 2880 const fallbackAttribute = (object, attribute) => { 2881 2882 if(!Array.isArray(attribute)) { 2883 attribute = [attribute] 2884 } 2885 2886 let final_value = null 2887 2888 do { 2889 2890 final_value = object[attribute.shift()] 2891 2892 } while(!final_value && attribute.length) 2893 2894 return final_value 2895 2896 } 2897 2898 const map = (object, mapper) => { 2899 2900 const newObject = {} 2901 2902 for(const key in mapper) { 2903 2904 // Handle different keys between new and old format
2905 newObject[key] = fallbackAttribute(object, mapper[key]) 2906 2907 // Cast price to float 2908 if(newObject[key] && ['price', 'amount', 'discount', 'discounts'].some(x => key.endsWith(x))) { 2909 newObject[key] = parsePrice(newObject[key]) 2910 } 2911 2912 if(newObject[key] === undefined) { 2913 delete newObject[key] 2914 } 2915 2916 } 2917 2918 return newObject 2919 2920 } 2921 2922 const mapCart = cart => { 2923 2924 const oldCart = cart 2925 2926 const mapper = { 2927 id: 'id', 2928 currency: 'currency', 2929 item_count: 'item_count', 2930 items: 'items', 2931 total_price: 'total_price', 2932 latest_items: 'latest_items' 2933 } 2934 2935 cart = map(cart, mapper) 2936 2937 const discounts = (oldCart.storewide_discounts || []).concat((oldCart.voucher_discounts || [])) 2938 2939 cart.discount_applications = discounts.map(discount => ({ 2940 title: discount.voucher_code || null, 2941 value: parsePrice(discount.amount), 2942 })) 2943 2944 cart.original_total_price = cart.items.length 2945 ? cart.items 2946 .map(item => item.original_price) 2947 .reduce((sum, price) => sum + price) 2948 : 0 2949 2950 cart.total_discount = cart.discount_applications.length 2951 ? cart.discount_applications 2952 .map(discount => parsePrice(discount.value)) 2953 .reduce((sum, value) => sum + value) 2954 : 0 2955 2956 cart.items = cart.items.map(item => mapLineItem(item)) 2957 2958 return cart 2959 2960 } 2961 2962 const mapCheckout = checkout => { 2963 2964 const mapper = { 2965 note: 'note', 2966 attributes: 'note_attributes', 2967 billing_address: 'billing_address', 2968 currency: 'currency', 2969 customer_id: 'customer_id', 2970 discounts_amount: 'total_discount', 2971 id: 'id', 2972 line_items: 'order_item', 2973 order_number: 'order_number', 2974 shipping_address: 'shipping_address', 2975 shipping_price: 'shipping_tax', 2976 shipping_method: 'shipping_method_name', 2977 tax_price: 'total_tax', 2978 } 2979 2980 checkout = map(checkout, mapper) 2981 2982 checkout.requires_shipping = checkout.line_items.some(item => item.shipping_required) 2983 2984 checkout.line_items = checkout.line_items.map(item => mapLineItem(item)) 2985 2986 return checkout 2987 2988 } 2989 2990 const mapOrder = order => { 2991 2992 const mapper = { 2993 attributes: 'note_attributes', 2994 billing_address: 'billing_address', 2995 cancelled: 'is_cancelled', 2996 cancelled_at: 'cancelled_at', 2997 created_at: 'created_at', 2998 customer_id: 'customer_id', 2999 // discount_applications: 'discount_applications', 3000 email: 'email', 3001 financial_status: 'financial_status', 3002 fulfillment_status: 'fulfillment_status', 3003 line_items: 'order_item', 3004 note: 'note', 3005 order_number: 'order_number', 3006 phone: 'phone', 3007 shipping_address: 'shipping_address', 3008 shipping_methods: 'shipping_methods', 3009 shipping_price: 'total_shipping', 3010 subtotal_price: 'subtotal_price', 3011 // tax_lines: 'tax_lines', 3012 tax_price: 'total_tax', 3013 total_discounts: 'total_discount', 3014 total_net_amount: 'total_amount_include_transaction', 3015 total_price: 'total_price', 3016 transactions: 'transaction_records', 3017 is_manual_payment: 'is_manual_payment', 3018 } 3019 3020 order.email = order.billing_address.email 3021 order.phone = order.billing_address.phone 3022 order.shipping_method = order.shipping_method_name 3023 3024 order = map(order, mapper) 3025 3026 order.line_items = order.line_items.map(item => mapLineItem(item)) 3027 order.transactions = order.transactions.map(transaction => mapTransaction(transaction)) 3028 3029 return order 3030 3031 } 3032 3033 const mapLineItem = line_item => { 3034 3035 const old_line_item = line_item 3036 3037 const mapper = { 3038 final_price: 'price', 3039 image: 'img_url', 3040 message: 'message', 3041 original_line_price: 'original_line_price', 3042 original_price: 'original_price', 3043 properties: 'properties', 3044 quantity: 'quantity', 3045 requires_shipping: 'shipping_required', 3046 sku: 'sku', 3047 taxable: 'taxable', 3048 title: 'product_name', 3049 product_name: 'product_name', 3050 url: 'url', 3051 product_id: 'product_id', 3052 variant_id: 'variant_id', 3053 id: ['id', 'i_id'], 3054 } 3055 3056 line_item = map(line_item, mapper) 3057 3058 line_item.product_id = line_item.product_id || (old_line_item.product && old_line_item.product.id) || null 3059 line_item.variant_id = line_item.variant_id || (old_line_item.variant && old_line_item.variant.id) || null 3060 line_item.image = line_item.image || (old_line_item.image && old_line_item.image.url) || null 3061 3062 return line_item 3063 3064 } 3065 3066 const mapTransaction = transaction => { 3067 3068 const mapper = { 3069 amount: 'amount', 3070 created_at: 'created_at', 3071 gateway: 'gateway_type', 3072 id: 'id', 3073 status: 'status', 3074 } 3075 3076 transaction = map(transaction, mapper) 3077 3078 return transaction 3079 3080 } 3081 3082 3083</script>
3083 3084 3085</body> 3086</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.