PageSourceSearch

https://www.sandplayshop.com/account/login

html sandplayshop.com collected 2026-09-29 20:32:19 UTC 94,476 bytes, 3,086 lines download raw bytes

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  登入 &ndash; 沙遊心藝工作室
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()">&times;</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                  &times;
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.