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 14 <link rel="icon" href="https://cdn.store-assets.com/s/39481/f/1112552.png" /> 15 16 17 <!-- Title and description ================================================== --> 18 <title> 19 Login – TS Bathroom Mall - Your One Stop Bathroom Solutions Need 20 </title> 21 22 23 24 <!-- Social meta ================================================== --> 25 26 27 <meta property="og:type" content="website"> 28 <meta property="og:title" content="Login"> 29 <meta property="og:url" content="https://www.thyesengplumbing.com/account/login"> 30 31 <meta property="og:image" content="https://cdn.store-assets.com/s/39481/f/1112531.png?width=350&format=webp"> 32 <meta property="og:image:secure_url" content="https://cdn.store-assets.com/s/39481/f/1112531.png?width=350&format=webp"> 33 34 35 36<meta property="og:description" content="Shop online for bathroom needs. Brands like Johnson Suisse, Inno, AIMER, Techplas and etc. Choices from stainless steel tank, WC, basin, towel rack, water filter, water filter catridge, poly tank (HDPE tank), solar panel tank, solar water heater system, electric water heater, plumbing, johnson suisse, Inno, econax, aimer, zelia, buteline, PE Tank, stainless steel sink, sink, york, stainless steel fitting, bathtub, hansen, bina UPVC pipe"> 37<meta property="og:site_name" content="TS Bathroom Mall - Your One Stop Bathroom Solutions Need"> 38 39 40 41 <meta name="twitter:card" content="summary"> 42 43 44 45 <meta name="twitter:site" content="@"> 46 47 48 49 50 51 <!-- Helpers ================================================== --> 52 <link rel="canonical" href="https://www.thyesengplumbing.com/account/login"> 53 <meta name="viewport" content="width=device-width,initial-scale=1"> 54 <meta name="theme-color" content=""> 55 56 <!-- CSS ================================================== --> 57 <link href="https://store-themes.easystore.co/39481/themes/336/assets/timber.css?t=1787072412" rel="stylesheet" type="text/css" media="screen" /> 58 <link href="https://store-themes.easystore.co/39481/themes/336/assets/theme.css?t=1787072412" rel="stylesheet" type="text/css" media="screen" /> 59 60 61 62 63 64 65
65<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js" type="text/javascript" ></script>
65 66 <!-- Header hook for plugins ================================================== --> 67 68<!-- ScriptTags -->
69<script>window.__st={p: "customers",cid: null,cem: null,cph: null,cfn: null,cln: null,cge: null,cdb: null,ccc: null};</script>
69<script src='/assets/events.js'></script>
69<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/en_US/sdk/xfbml.customerchat.js#xfbml=1&version=v6.0&autoLogAppEvents=1';fjs.parentNode.insertBefore(js,fjs);}(document,'script','facebook-jssdk'));</script>
69<script>(function(){function asyncLoad(){var urls=['/assets/facebook-pixel.js?v=1.2.1605084597','/assets/traffic.js?v=1','https://apps.easystore.co/zopim-live-chat/script.js?shop=lcw8807257666.easy.co','https://apps.easystore.co/marketplace/shopee-malaysia/script.js?shop=lcw8807257666.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>
69 70<!-- /ScriptTags --> 71 72 73 74<!--[if lt IE 9]>
75<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.2/html5shiv.min.js" type="text/javascript" ></script>
vendor: 2 bytes, line 75
75
76<script src="https://store-themes.easystore.co/39481/themes/336/assets/respond.min.js?t=1787072412" type="text/javascript" ></script>
76 77<link href="https://store-themes.easystore.co/39481/themes/336/assets/respond-proxy.html" id="respond-proxy" rel="respond-proxy" /> 78<link href="//www.thyesengplumbing.com/search?q=dba8240407e3ea0f0b023a2030b01159" id="respond-redirect" rel="respond-redirect" />
79<script src="//www.thyesengplumbing.com/search?q=dba8240407e3ea0f0b023a2030b01159" type="text/javascript"></script>
79 80<![endif]--> 81 82 83 84
84<script src="https://store-themes.easystore.co/39481/themes/336/assets/modernizr.min.js?t=1787072412" type="text/javascript" ></script>
84 85 86 87 88
88<script src="/assets/shop_common.js" type="text/javascript" ></script>
88 89
89<script src="/assets/customer_area.js" type="text/javascript" ></script>
89 90 91
91<script> 92 (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 93 (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 94 m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 95 })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); 96 97 ga('create', '', 'auto','myTracker'); 98 ga('myTracker.send', 'pageview'); 99 100 </script>
100 101 102<!-- Snippet:global/head: Google Search Console --> 103<meta name="google-site-verification" content="RR2uZSdoCkvDnFqgezIYJ0UpVLFx8C3FOnY9Qbphxw8" /> 104<!-- /Snippet --> 105 106<!-- Snippet:global/head: Shopee Malaysia --> 107<link type="text/css" rel="stylesheet" href="https://apps.easystore.co/assets/css/marketplace/main.css?v1.71"> 108<!-- /Snippet --> 109 110</head> 111 112<body id="login" class="template-customers" > 113 114<!-- Snippet:global/body_start: Announcement Bar --> 115<style type="text/css"> 116 body { 117 background-position: top 45px center; 118 } 119 120 div#announcement-bar-top { 121 height: 45px; 122 } 123 124 span .announcement-close-button { 125 text-align: center; 126 display: -webkit-flex; 127 display: -moz-flex; 128 display: -ms-flex; 129 display: -o-flex; 130 display: inline-block; 131 align-items: center; 132 flex-wrap: wrap; 133 justify-content: center; 134 } 135 136 #announcementBar_countdown:empty { 137 display: none; 138 } 139 140 141 .announcementBar_timer { 142 margin: 5px 0px; 143 } 144 145 .announcementBar_timer.style_1, 146 .announcementBar_timer.style_2 { 147 display: -webkit-inline-flex; 148 display: -moz-inline-flex; 149 display: -ms-inline-flex; 150 display: -o-inline-flex; 151 display: inline-flex; 152 align-items: center; 153 padding: 5px 8px; 154 margin: 3px 0px 3px 10px; 155 border-radius: 8px; 156 font-weight: bold; 157 } 158 159 .announcementBar_timer.style_1>div, 160 .announcementBar_timer.style_2>div { 161 margin: 0 5px; 162 line-height: 1; 163 } 164 165 .announcementBar_timer.style_1 small, 166 .announcementBar_timer.style_2 small { 167 display: block; 168 font-size: 55%; 169 line-height: 1; 170 } 171 172 .announcementBar_timer.style_2 { 173 flex-direction: row; 174 } 175 176 .announcementBar_timer.style_2>div { 177 position: relative; 178 } 179 180 .announcementBar_timer.style_2>div:not(:last-child):after { 181 position: absolute; 182 content: ':'; 183 top: 0; 184 right: 0; 185 margin-right: -7px; 186 } 187 188 .announcementBar_timer.style_3 { 189 display: -webkit-inline-flex; 190 display: -moz-inline-flex; 191 display: -ms-inline-flex; 192 display: -o-inline-flex; 193 display: inline-flex; 194 align-items: center; 195 border-radius: 5px; 196 padding: 5px 5px; 197 margin-left: 10px; 198 font-weight: bold; 199 } 200 201 .announcementBar_timer.style_3>div { 202 margin: 0 3px; 203 line-height: 1; 204 } 205 206 .announcementBar_timer.style_3 small { 207 font-weight: bold; 208 line-height: 1.1; 209 font-size: 65%; 210 } 211 212 .announcement-close-button { 213 position: relative; 214 width: 24px; 215 height: 24px; 216 opacity: 1; 217 background: #000; 218 border-radius: 50%; 219 cursor: pointer; 220 top: 30%; 221 } 222 223 .announcement-close-button:hover { 224 opacity: 0.6; 225 } 226 227 .announcement-close-button:before, 228 .announcement-close-button:after { 229 position: absolute; 230 left: 11px; 231 top: 5px; 232 content: ' '; 233 height: 14px; 234 width: 2px; 235 background-color: #fff; 236 border-radius: 3px; 237 } 238 239 .announcement-close-button:before { 240 transform: rotate(45deg); 241 } 242 243 .announcement-close-button:after { 244 transform: rotate(-45deg); 245 } 246 247 .announcement-bar-button { 248 padding: 4px 8px; 249 vertical-align: baseline; 250 border-radius: 5px; 251 margin-left: 10px; 252 opacity: 1; 253 line-height: 1.1; 254 text-decoration: none; 255 display: inline-block; 256 font-weight: 500; 257 } 258 259 .announcement-bar-button:hover { 260 opacity: 0.8; 261 } 262 263 .announcement-bar-direction-button { 264 cursor: pointer; 265 padding: 0px 8px; 266 } 267 268 .announcement-bar-direction-button:hover { 269 opacity: 0.8; 270 } 271 272 slider-announcement-bar-app { 273 display: block; 274 text-align: center; 275 margin: 0px; 276 padding: 6px 10px; 277 border-top: 1px dashed transparent; 278 border-bottom: 1px dashed transparent; 279 width: 100%; 280 } 281 282 slider-announcement-bar-app ul::-webkit-scrollbar {
283 height: 0.4rem; 284 width: 0.4rem; 285 display: none; 286 } 287 288 slider-announcement-bar-app ul { 289 position: relative; 290 display: flex; 291 overflow-x: auto; 292 -ms-overflow-style: none; 293 /* IE 11 */ 294 scrollbar-width: none; 295 /* Firefox 64 */ 296 scroll-snap-type: x mandatory; 297 scroll-behavior: smooth; 298 /* scroll-padding-left: 1rem; */ 299 -webkit-overflow-scrolling: touch; 300 margin-bottom: 0; 301 padding: 0; 302 list-style: none; 303 align-items: center; 304 } 305 306 slider-announcement-bar-app ul li { 307 width: 100vw; 308 scroll-snap-align: start; 309 flex-shrink: 0; 310 } 311</style> 312 313<input type="hidden" id="total_announcement_left" name="total_announcement_left" value="1"> 314<input type="hidden" id="locale" name="locale" value="en_US"> 315<div id="announcement-bar-top"> </div> 316 317<slider-announcement-bar-app id="announcement-bar" name="announcement-bar" style=" 318 border: unset; 319 padding: 4px 0px; 320 margin: 0px; 321 z-index:2999; 322 background:rgb(233, 69, 69); 323 color:rgb(255, 255, 255); 324 position: fixed; top: 0; 325"> 326 327 <div style=" 328 display: flex; 329 justify-content: space-between; 330 align-items: center; 331 padding: 0px 0px; 332 border-top: 0px; 333 border-bottom: 0px; 334 border-color: transparent; 335 "> 336 <div style="height: 24px;"> 337 </div> 338 339 <ul style="flex-grow: 2; margin: 0px;"> 340 <li id="announcement_bar_0" style=" 341 display: flex; 342 justify-content: center; 343 align-items: center; 344 flex-wrap: wrap; 345 margin: 0px; 346 width: 100%; 347 word-wrap: break-word; 348 "> 349 For long pipe, sanitary wares, water tank or septic tank order kindly chat with us and let us know your quantity and location at 0129838831 before place order to determine shipping fees 350 <input type="hidden" id="expired_at_0" name="expired_at_0" value=""> 351 </li> 352 </ul> 353 354 <div style="display: flex;"> 355 <span id="announcement-close-button" name="announcement-close-button" class="announcement-close-button" style="margin-right: 10px;"></span> 356 </div> 357 </div> 358 359 360</slider-announcement-bar-app> 361
362<script> 363 (function() { 364 365 const loadScript = function(url, callback) { 366 const script = document.createElement("script"); 367 script.type = "text/javascript"; 368 // If the browser is Internet Explorer. 369 if (script.readyState) { 370 script.onreadystatechange = function() { 371 if (script.readyState == "loaded" || script.readyState == "complete") { 372 script.onreadystatechange = null; 373 callback(); 374 } 375 }; 376 // For any other browser. 377 } else { 378 script.onload = function() { 379 callback(); 380 }; 381 } 382 script.src = url; 383 document.getElementsByTagName("head")[0].appendChild(script); 384 }; 385 386 const announcementBarAppJS = function($) { 387 388 $('#announcement-bar-top').css('height', $('#announcement-bar').outerHeight()); 389 390 $('#announcement-close-button').on('click', function() { 391 $('#announcement-bar, #announcement-bar-top').animate({ 392 marginTop: '-=' + $('#announcement-bar').outerHeight() + 'px' 393 }, 200, 'linear'); 394 }); 395 } 396 397 if (typeof jQuery === 'undefined') { 398 loadScript('//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js', function() { 399 jQuery = jQuery.noConflict(true); 400 announcementBarAppJS(jQuery); 401 }); 402 } else { 403 announcementBarAppJS(jQuery); 404 } 405 406 })(); 407 408 setInterval(function() { 409 const total_announcement = "1" 410 var total_announcement_left = $("#total_announcement_left").val(); 411 412 for (let i = 0; i <= total_announcement; i++) { 413 const startDate = new Date(); 414 const endDateStr = $("#expired_at_" + i).val(); 415 const endDate = endDateStr == undefined ? new Date() : new Date(endDateStr.replace(/-/g, "/")); 416 const seconds = (endDate.getTime() - startDate.getTime()) / 1000; 417 418 const days = parseInt(seconds / 86400); 419 const hours = parseInt((seconds % 86400) / 3600); 420 const mins = parseInt((seconds % 86400 % 3600) / 60); 421 const secs = parseInt((seconds % 86400 % 3600) % 60); 422 423 // use to translate countdown unit 424 // (translate based on the preferred language when save announcement bar setting) 425 String.prototype.translate = function() { 426 try { 427 if ($("#locale").val() == "zh_TW") { 428 if (this.toString() === 'day') { 429 return "天"; 430 } 431 if (this.toString() === 'hour') { 432 return "å°æ"; 433 } 434 if (this.toString() === 'min') { 435 return "åé"; 436 } 437 if (this.toString() === 'sec') { 438 return "ç§"; 439 } 440 } else { 441 if (this.toString() === 'day') { 442 if (days > 0) { 443 return "Days"; 444 } else { 445 return "Day"; 446 } 447 } else if (this.toString() === 'hour') { 448 if (hours > 0) { 449 return "Hours"; 450 } else { 451 return "Hour"; 452 } 453 } else if (this.toString() === 'min') { 454 if (mins > 0) { 455 return "Mins"; 456 } else { 457 return "Min"; 458 } 459 } else if (this.toString() === 'sec') { 460 if (secs > 0) { 461 return "Secs"; 462 } else { 463 return "Sec"; 464 } 465 } 466 } 467 } catch (error) { 468 console.log("Some errors heres", error); 469 } 470 }; 471 472 const announcementBar_countdown = document.getElementById("announcementBar_countdown_" + i); 473 if (announcementBar_countdown && seconds > 0) { 474 475 $(announcementBar_countdown).show() 476 announcementBar_countdown.innerHTML = ` 477 <div> 478 ${days} <small>${'day'.translate()}</small> 479 </div> 480 <div> 481 ${hours} <small>${'hour'.translate()}</small> 482 </div> 483 <div> 484 ${mins} <small>${'min'.translate()}</small> 485 </div> 486 <div> 487 ${secs} <small>${'sec'.translate()}</small> 488 </div> 489 `; 490 $('#announcement-bar-top').css('height', $('#announcement-bar').outerHeight()); 491 492 493 } else if (announcementBar_countdown && seconds <= 0) { 494 $("#announcement_bar_" + i).remove(); 495 total_announcement_left = total_announcement_left - 1; 496 $("#total_announcement_left").val(total_announcement_left); 497 } 498 } 499 500 showOrHide(total_announcement_left); 501 }, 1000); 502 503 504 function showOrHide(total_announcement_left) { 505 if (total_announcement_left <= 1) { 506 $("#previous-announcement-bar-button,#next-announcement-bar-button").hide(); 507 } else { 508 $("#previous-announcement-bar-button,#next-announcement-bar-button").show(); 509 } 510 511 if (total_announcement_left == 0) { 512 $("#announcement-close-button").hide(); 513 $("#announcement-bar").hide(); 514 $('#announcement-bar-top').hide(); 515 } 516 }; 517 518 let annoucementBarAutoMoveInterval = ''; 519 class AnnouncementBarAppSlider extends HTMLElement { 520 constructor() { 521 super(); 522 this.slider = this.querySelector('ul'); 523 this.sliderItems = this.querySelectorAll('li'); 524 this.prevButton = this.querySelector('a[name="previous"]'); 525 this.nextButton = this.querySelector('a[name="next"]'); 526 527 if (!this.slider || !this.nextButton) return; 528 529 const resizeObserver = new ResizeObserver(entries => this.initPages()); 530 resizeObserver.observe(this.slider); 531 532 this.slider.addEventListener('scroll', this.update.bind(this)); 533 this.prevButton.addEventListener('click', this.onButtonClick.bind(this)); 534 this.nextButton.addEventListener('click', this.onButtonClick.bind(this)); 535 536 537 } 538 539 initPages() { 540 const sliderItemsToShow = Array.from(this.sliderItems).filter(element => element.clientWidth > 0); 541 this.sliderLastItem = sliderItemsToShow[sliderItemsToShow.length - 1]; 542 if (sliderItemsToShow.length === 0) return; 543 this.slidesPerPage = Math.floor(this.slider.clientWidth / sliderItemsToShow[0].clientWidth); 544 this.totalPages = sliderItemsToShow.length - this.slidesPerPage + 1; 545 this.update(); 546 let self = this 547 var total_announcement_left = $("#total_announcement_left").val(); 548 annoucementBarAutoMoveInterval = setInterval(function() { 549 if (total_announcement_left > 1) { 550 self.moveSlide('next') 551 } 552 }, 5000) 553 } 554 555 update() { 556 this.currentPage = Math.round(this.slider.scrollLeft / this.sliderLastItem.clientWidth) + 1; 557 } 558 559 onButtonClick(event) { 560 event.preventDefault(); 561 let self = this; 562 self.moveSlide(event.currentTarget.name); 563 } 564 565 566 moveSlide(move_to) { 567 568 clearInterval(annoucementBarAutoMoveInterval); 569 let self = this; 570 annoucementBarAutoMoveInterval = setInterval(function() { 571 self.moveSlide('next'); 572 }, 5000) 573 574 if (move_to === 'previous' && this.currentPage === 1) { 575 this.slider.scrollTo({ 576 left: this.sliderLastItem.clientWidth * (this.totalPages - 1) 577 }); 578 } else if (move_to === 'next' && this.currentPage === this.totalPages) { 579 this.slider.scrollTo({ 580 left: 0 581 }); 582 } else { 583 const slideScrollPosition = move_to === 'next' ? this.slider.scrollLeft + this.sliderLastItem 584 .clientWidth : this.slider.scrollLeft - this.sliderLastItem.clientWidth; 585 this.slider.scrollTo({ 586 left: slideScrollPosition 587 }); 588 } 589 } 590 591 } 592 593 customElements.define('slider-announcement-bar-app', AnnouncementBarAppSlider); 594</script>
594 595 596<!-- /Snippet --> 597 598<!-- Snippet:global/body_start: Console Extension --> 599<div id='es_console' style='display: none;'>39481</div> 600<!-- /Snippet --> 601 602 603 <style> 604 .search-modal__form{ 605 position: relative; 606 } 607 608 .dropdown-wrapper { 609 position: relative; 610 } 611 612 .dropdown-wrapper:hover .dropdown { 613 display: block; 614 } 615 616 .dropdown { 617 display: none; 618 position: absolute; 619 top: 100%; 620 left: -20px; 621 width: 100%; 622 padding: 8px 0; 623 margin: 0; 624 background-color: #fff; 625 z-index: 1000; 626 border-radius: 5px; 627 border: 1px solid rgba(var(--color-foreground), 1); 628 border-top: none; 629 overflow-x: hidden; 630 overflow-y: auto; 631 max-height: 350px; 632 min-width: 150px; 633 box-shadow: 0 0 5px rgba(0,0,0,0.1); 634 } 635 636 .dropdown li,.dropdown a{ 637 display: block; 638 } 639 .dropdown a{ 640 text-decoration: none; 641 padding: 2px 10px; 642 color: #000 !important; 643 } 644 645 .dropdown-item { 646 padding: 0.4rem 2rem; 647 cursor: pointer; 648 line-height: 1.4; 649 overflow: hidden; 650 text-overflow: ellipsis; 651 white-space: nowrap; 652 } 653 654 .dropdown-item:hover{ 655 background-color: #f3f3f3; 656 } 657 658 .search__input.is-focus{ 659 border-radius: 15px; 660 border-bottom-left-radius: 0; 661 border-bottom-right-radius: 0; 662 border: 1px solid rgba(var(--color-foreground), 1); 663 border-bottom: none; 664 box-shadow: none; 665 } 666 667 .clear-all { 668 text-align: right; 669 padding: 0 2rem 0.2rem; 670 line-height: 1; 671 font-size: 70%; 672 margin-bottom: -2px; 673 } 674 675 .clear-all:hover{ 676 background-color: #fff; 677 } 678 679 .referral-notification { 680 position: absolute; 681 top: 100%; 682 right: -12px; 683 background: white; 684 border: 1px solid #e0e0e0; 685 border-radius: 8px; 686 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 687 width: 280px; 688 z-index: 1000; 689 padding: 16px; 690 margin-top: 8px; 691 font-size: 14px; 692 line-height: 1.4; 693 } 694 695 .referral-notification::before { 696 content: ''; 697 position: absolute; 698 top: -8px; 699 right: 20px; 700 width: 0; 701 height: 0; 702 border-left: 8px solid transparent; 703 border-right: 8px solid transparent; 704 border-bottom: 8px solid white; 705 } 706 707 .referral-notification::after { 708 content: ''; 709 position: absolute; 710 top: -9px; 711 right: 20px; 712 width: 0; 713 height: 0; 714 border-left: 8px solid transparent; 715 border-right: 8px solid transparent; 716 border-bottom: 8px solid #e0e0e0; 717 } 718 719 .referral-notification h4 { 720 margin: 0 0 8px 0; 721 color: #2196F3; 722 font-size: 16px; 723 font-weight: 600; 724 } 725 726 .referral-notification p { 727 margin: 0 0 12px 0; 728 color: #666; 729 } 730 731 .referral-notification .referral-code { 732 background: #f5f5f5; 733 padding: 8px; 734 border-radius: 4px; 735 font-family: monospace; 736 font-size: 13px; 737 text-align: center; 738 margin: 8px 0; 739 border: 1px solid #ddd; 740 } 741 742 .referral-notification .close-btn { 743 position: absolute; 744 top: 8px; 745 right: 8px; 746 background: none; 747 border: none; 748 font-size: 18px; 749 color: #999; 750 cursor: pointer; 751 padding: 0; 752 width: 20px; 753 height: 20px; 754 line-height: 1; 755 } 756 757 .referral-notification .close-btn:hover { 758 color: #333; 759 } 760 761 .referral-notification .action-btn { 762 background: #2196F3; 763 color: white; 764 border: none; 765 padding: 8px 16px; 766 border-radius: 4px; 767 cursor: pointer; 768 font-size: 12px; 769 margin-right: 8px; 770 margin-top: 8px; 771 } 772 773 .referral-notification .action-btn:hover { 774 background: #1976D2; 775 } 776 777 .referral-notification .dismiss-btn { 778 background: transparent; 779 color: #666; 780 border: 1px solid #ddd; 781 padding: 8px 16px; 782 border-radius: 4px; 783 cursor: pointer; 784 font-size: 12px; 785 margin-top: 8px; 786 } 787 788 .referral-notification .dismiss-btn:hover { 789 background: #f5f5f5; 790 } 791 .referral-modal__dialog { 792 transform: translate(-50%, 0); 793 transition: transform var(--duration-default) ease, visibility 0s; 794 z-index: 10000; 795 position: fixed; 796 top: 20vh; 797 left: 50%; 798 width: 100%; 799 max-width: 350px; 800 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 801 border-radius: 12px; 802 background-color: #fff; 803 color: #000; 804 padding: 20px; 805 text-align: center; 806 } 807 .modal-overlay.referral-modal-overlay { 808 position: fixed; 809 top: 0; 810 left: 0; 811 width: 100%; 812 height: 100%; 813 background-color: rgba(0, 0, 0, 0.7); 814 z-index: 2001; 815 display: block; 816 opacity: 1;
817 transition: opacity var(--duration-default) ease, visibility 0s; 818 } 819</style> 820 821<div class="header-bar"> 822 <div class="wrapper medium-down--hide"> 823 <div class="large--display-table"> 824 825 <div class="header-bar__left large--display-table-cell"> 826 827 828 829 830 831 832 833 <div class="header-bar__module header-bar__search"> 834 835 <form action="/search" method="get" class="header-bar__search-form clearfix" role="search"> 836 837 <button type="submit" class="btn icon-fallback-text header-bar__search-submit"> 838 <span class="icon icon-search" aria-hidden="true"></span> 839 <span class="fallback-text">Search</span> 840 </button> 841 <input type="search" name="q" value="" aria-label="Search" class="header-bar__search-input" placeholder="Search" autocomplete="off"> 842 <div class="dropdown" id="searchDropdown"></div> 843 <input type="hidden" name="search_history" id="search_history"> 844 </form> 845 846
847<script> 848 var _search_history_listings = "" 849 if(_search_history_listings != "") localStorage.setItem('searchHistory', JSON.stringify(_search_history_listings)); 850 851 function clearAll() { 852 localStorage.removeItem('searchHistory'); 853 854 var customer = ""; 855 if(customer) { 856 // Your logic to clear all data or call an endpoint 857 fetch('/account/search_histories', { 858 method: 'DELETE', 859 headers: { 860 "Content-Type": "application/json", 861 "X-Requested-With": "XMLHttpRequest" 862 }, 863 body: JSON.stringify({ 864 _token: "CfeJEOxOvhUxEgvKeM0ruK5x6bNBCUp3YcMNjaAT" 865 }), 866 }) 867 .then(response => response.json()) 868 } 869 } 870 // End - store view history in local storage 871 872 // search history dropdown 873 var searchInputs = document.querySelectorAll('input[type=search]'); 874 875 if(searchInputs.length > 0) { 876 877 searchInputs.forEach((searchInput)=>{ 878 879 searchInput.addEventListener('focus', function() { 880 var searchDropdown = searchInput.closest('form').querySelector('#searchDropdown'); 881 searchDropdown.innerHTML = ''; // Clear existing items 882 883 // retrieve data from local storage 884 var searchHistoryData = JSON.parse(localStorage.getItem('searchHistory')) || []; 885 886 // Convert JSON to string 887 var search_history_json = JSON.stringify(searchHistoryData); 888 889 // Set the value of the hidden input 890 searchInput.closest('form').querySelector("#search_history").value = search_history_json; 891 892 // create dropdown items 893 var clearAllItem = document.createElement('div'); 894 clearAllItem.className = 'dropdown-item clear-all'; 895 clearAllItem.textContent = 'Clear'; 896 clearAllItem.addEventListener('click', function() { 897 clearAll(); 898 searchDropdown.style.display = 'none'; 899 searchInput.classList.remove('is-focus'); 900 }); 901 searchDropdown.appendChild(clearAllItem); 902 903 // create dropdown items 904 searchHistoryData.forEach(function(item) { 905 var dropdownItem = document.createElement('div'); 906 dropdownItem.className = 'dropdown-item'; 907 dropdownItem.textContent = item.term; 908 dropdownItem.addEventListener('click', function() { 909 searchInput.value = item.term; 910 searchDropdown.style.display = 'none'; 911 }); 912 searchDropdown.appendChild(dropdownItem); 913 }); 914 915 if(searchHistoryData.length > 0){ 916 // display the dropdown 917 searchInput.classList.add('is-focus'); 918 document.body.classList.add('search-input-focus'); 919 searchDropdown.style.display = 'block'; 920 } 921 }); 922 923 searchInput.addEventListener("focusout", (event) => { 924 var searchDropdown = searchInput.closest('form').querySelector('#searchDropdown'); 925 searchInput.classList.remove('is-focus'); 926 searchDropdown.classList.add('transparent'); 927 setTimeout(()=>{ 928 searchDropdown.style.display = 'none'; 929 searchDropdown.classList.remove('transparent'); 930 document.body.classList.remove('search-input-focus'); 931 }, 400); 932 }); 933 }) 934 } 935 // End - search history dropdown 936</script>
936 937 </div> 938 939 940 941 </div> 942 943 <div class="header-bar__right large--display-table-cell"> 944 945 <ul class="header-bar__module header-bar__module--list" style="position: relative;"> 946 947 948 949 <li> 950 <a href="/account/login">Log in</a> 951 </li><li>OR</li> 952 953 954 955 <li> 956 <a href="/account/register">Create account</a> 957 </li> 958 959 960 961 <!-- Referral Notification Popup for Desktop --> 962 <div id="referralNotification" class="referral-notification" style="display: none;"> 963 <button class="close-btn" onclick="dismissReferralNotification()">×</button> 964 <div style="text-align: center; margin-bottom: 16px;"> 965 <div style="width: 100px; height: 100px; margin: 0 auto;"> 966 <img src="https://resources.easystore.co/storefront/icons/channels/vector_referral_program.svg"> 967 </div> 968 </div> 969 <p id="referralSignupMessage" style="text-align: center; font-size: 14px; color: #666; margin: 0 0 20px 0;"> 970 Your friend gifted you credit! Sign up now to redeem. 971 </p> 972 <div style="text-align: center;"> 973 <button class="btn" onclick="goToSignupPage()" style="width: 100%; color: #fff;"> 974 Sign Up 975 </button> 976 </div> 977 </div> 978 979 </ul> 980 981 982 983 984 985 <div class="header-bar__module"> 986 <span class="header-bar__sep" aria-hidden="true"></span> 987 <a href="/cart" class="cart-page-link"> 988 <span class="icon icon-cart header-bar__cart-icon" aria-hidden="true"></span> 989 </a> 990 </div> 991 992 <div class="header-bar__module"> 993 994 <a href="/cart" class="cart-page-link"> 995 Cart<span class="hidden-count">:</span> 996 <span class="cart-count header-bar__cart-count hidden-count">0</span> 997 </a> 998 </div> 999 1000 1001 1002 1003 1004 </div> 1005 </div> 1006 </div> 1007 <div class="wrapper large--hide" style="padding: 5px 30px 0;"> 1008 <button type="button" class="mobile-nav-trigger" id="MobileNavTrigger"> 1009 <span class="icon icon-hamburger" aria-hidden="true"></span> 1010 Menu 1011 </button> 1012 1013 1014 1015 <!-- Referral Notification Modal for Mobile --> 1016 <details-modal class="referral-modal" id="referralMobileModal"> 1017 <details> 1018 <summary class="referral-modal__toggle" aria-haspopup="dialog" aria-label="Open referral modal" style="display: none;"> 1019 </summary> 1020 <div class="modal-overlay referral-modal-overlay"></div> 1021 <div class="referral-modal__dialog" role="dialog" aria-modal="true" aria-label="Referral notification"> 1022 <div class="referral-modal__content"> 1023 <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;"> 1024 × 1025 </button> 1026 1027 <div style="margin-bottom: 24px;"> 1028 <div style="width: 120px; height: 120px; margin: 0 auto 20px;"> 1029 <img src="https://resources.easystore.co/storefront/icons/channels/vector_referral_program.svg" style="width: 100%; height: 100%;"> 1030 </div> 1031 </div> 1032 1033 <p id="referralSignupMessageMobile" style="font-size: 16px; color: #333; margin: 0 0 30px 0; line-height: 1.5;"> 1034 Your friend gifted you credit! Sign up now to redeem. 1035 </p> 1036 1037 <button type="button" class="btn" onclick="goToSignupPageFromMobile()" style="width: 100%; color: #fff;"> 1038 Sign Up 1039 </button> 1040 </div> 1041 </div> 1042 </details> 1043 </details-modal> 1044 1045 1046 1047 1048 <a href="/cart" class="cart-page-link mobile-cart-page-link">
1049 <span class="icon icon-cart header-bar__cart-icon" aria-hidden="true"></span> 1050 Cart 1051 <span class="cart-count hidden-count">0</span> 1052 </a> 1053 <div class="mobile-cart-page-link" style="margin-right: 20px;"> 1054 1055 1056 1057 1058 </div> 1059 </div> 1060 <ul id="MobileNav" class="mobile-nav large--hide"> 1061 <li class="mobile-nav__link"> 1062 1063 <div class="header-bar__module header-bar__search"> 1064 1065 <form action="/search" method="get" class="header-bar__search-form clearfix" role="search"> 1066 1067 <button type="submit" class="btn icon-fallback-text header-bar__search-submit"> 1068 <span class="icon icon-search" aria-hidden="true"></span> 1069 <span class="fallback-text">Search</span> 1070 </button> 1071 <input type="search" name="q" value="" aria-label="Search" class="header-bar__search-input" placeholder="Search" autocomplete="off"> 1072 <div class="dropdown" id="searchDropdown"></div> 1073 <input type="hidden" name="search_history" id="search_history"> 1074 </form> 1075 1076
1077<script> 1078 var _search_history_listings = "" 1079 if(_search_history_listings != "") localStorage.setItem('searchHistory', JSON.stringify(_search_history_listings)); 1080 1081 function clearAll() { 1082 localStorage.removeItem('searchHistory'); 1083 1084 var customer = ""; 1085 if(customer) { 1086 // Your logic to clear all data or call an endpoint 1087 fetch('/account/search_histories', { 1088 method: 'DELETE', 1089 headers: { 1090 "Content-Type": "application/json", 1091 "X-Requested-With": "XMLHttpRequest" 1092 }, 1093 body: JSON.stringify({ 1094 _token: "CfeJEOxOvhUxEgvKeM0ruK5x6bNBCUp3YcMNjaAT" 1095 }), 1096 }) 1097 .then(response => response.json()) 1098 } 1099 } 1100 // End - store view history in local storage 1101 1102 // search history dropdown 1103 var searchInputs = document.querySelectorAll('input[type=search]'); 1104 1105 if(searchInputs.length > 0) { 1106 1107 searchInputs.forEach((searchInput)=>{ 1108 1109 searchInput.addEventListener('focus', function() { 1110 var searchDropdown = searchInput.closest('form').querySelector('#searchDropdown'); 1111 searchDropdown.innerHTML = ''; // Clear existing items 1112 1113 // retrieve data from local storage 1114 var searchHistoryData = JSON.parse(localStorage.getItem('searchHistory')) || []; 1115 1116 // Convert JSON to string 1117 var search_history_json = JSON.stringify(searchHistoryData); 1118 1119 // Set the value of the hidden input 1120 searchInput.closest('form').querySelector("#search_history").value = search_history_json; 1121 1122 // create dropdown items 1123 var clearAllItem = document.createElement('div'); 1124 clearAllItem.className = 'dropdown-item clear-all'; 1125 clearAllItem.textContent = 'Clear'; 1126 clearAllItem.addEventListener('click', function() { 1127 clearAll(); 1128 searchDropdown.style.display = 'none'; 1129 searchInput.classList.remove('is-focus'); 1130 }); 1131 searchDropdown.appendChild(clearAllItem); 1132 1133 // create dropdown items 1134 searchHistoryData.forEach(function(item) { 1135 var dropdownItem = document.createElement('div'); 1136 dropdownItem.className = 'dropdown-item'; 1137 dropdownItem.textContent = item.term; 1138 dropdownItem.addEventListener('click', function() { 1139 searchInput.value = item.term; 1140 searchDropdown.style.display = 'none'; 1141 }); 1142 searchDropdown.appendChild(dropdownItem); 1143 }); 1144 1145 if(searchHistoryData.length > 0){ 1146 // display the dropdown 1147 searchInput.classList.add('is-focus'); 1148 document.body.classList.add('search-input-focus'); 1149 searchDropdown.style.display = 'block'; 1150 } 1151 }); 1152 1153 searchInput.addEventListener("focusout", (event) => { 1154 var searchDropdown = searchInput.closest('form').querySelector('#searchDropdown'); 1155 searchInput.classList.remove('is-focus'); 1156 searchDropdown.classList.add('transparent'); 1157 setTimeout(()=>{ 1158 searchDropdown.style.display = 'none'; 1159 searchDropdown.classList.remove('transparent'); 1160 document.body.classList.remove('search-input-focus'); 1161 }, 400); 1162 }); 1163 }) 1164 } 1165 // End - search history dropdown 1166</script>
1166 1167 </div> 1168 1169 </li> 1170 1171 1172 1173 <li class="mobile-nav__link" aria-haspopup="true"> 1174 1175 <a href="/" class="mobile-nav"> 1176 Home 1177 </a> 1178 1179 </li> 1180 1181 1182 <li class="mobile-nav__link" aria-haspopup="true"> 1183 1184 1185 <a href="/collections/all" class="mobile-nav__sublist-trigger"> 1186 Products 1187 <span class="icon-fallback-text mobile-nav__sublist-expand"> 1188 <span class="icon icon-plus" aria-hidden="true"></span> 1189 <span class="fallback-text">+</span> 1190</span> 1191<span class="icon-fallback-text mobile-nav__sublist-contract"> 1192 <span class="icon icon-minus" aria-hidden="true"></span> 1193 <span class="fallback-text">-</span> 1194</span> 1195 </a> 1196 <ul class="mobile-nav__sublist"> 1197 <li class="mobile-nav__sublist-link"> 1198 <a href="/collections/all" >Products</a> 1199 </li> 1200 1201 <li class="mobile-nav__sublist-link"> 1202 <a href="/collections/water-pump" >Water Pump</a> 1203 </li> 1204 1205 <li class="mobile-nav__sublist-link"> 1206 <a href="/collections/sanitary-wares" >Sanitary Wares</a> 1207 </li> 1208 1209 <li class="mobile-nav__sublist-link"> 1210 <a href="/collections/plumbing-valve" >Plumbing Valve</a> 1211 </li> 1212 1213 <li class="mobile-nav__sublist-link"> 1214 <a href="/collections/water-tank---sewerage-tank" >Water Tank & Sewerage Tank</a> 1215 </li> 1216 1217 <li class="mobile-nav__sublist-link"> 1218 <a href="/collections/water-heater" >Water Heater</a> 1219 </li> 1220 1221 <li class="mobile-nav__sublist-link"> 1222 <a href="/collections/water-pipes-and-fittings" >Water Pipes and Fittings</a> 1223 </li> 1224 1225 <li class="mobile-nav__sublist-link"> 1226 <a href="/collections/wholesales-price" >Wholesales Price</a> 1227 </li> 1228 1229 </ul> 1230 1231 </li> 1232 1233 1234 <li class="mobile-nav__link" aria-haspopup="true"> 1235 1236 <a href="/pages/about-us" class="mobile-nav"> 1237 About Us 1238 </a> 1239 1240 </li> 1241 1242 1243 <li class="mobile-nav__link" aria-haspopup="true"> 1244 1245 <a href="/pages/catalogue" class="mobile-nav"> 1246 Catalogue 1247 </a> 1248 1249 </li> 1250 1251 1252 1253 1254 1255 1256 1257 <li class="mobile-nav__link"> 1258 <a href="/account/login">Log in</a> 1259 </li> 1260 1261 <li class="mobile-nav__link"> 1262 <a href="/account/register">Create account</a> 1263 </li> 1264 1265 1266 1267 1268</ul> 1269</div> 1270
1271<script> 1272 // referral notification popup 1273 const referralMessageTemplate = 'Your friend gifted you __CURRENCY_CODE__ __CREDIT_AMOUNT__ credit! Sign up now to redeem.'; 1274 const shopPrefix = 'RM '; 1275 1276 function getCookie(name) { 1277 const value = `; ${document.cookie}`; 1278 const parts = value.split(`; ${name}=`); 1279 if (parts.length === 2) return parts.pop().split(';').shift(); 1280 return null; 1281 } 1282 1283 function removeCookie(name) { 1284 document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`; 1285 } 1286 1287 function showReferralNotification() { 1288 const referralCode = getCookie('customer_referral_code'); 1289 1290 1291 const customerReferralCode = null; 1292 1293 1294 const activeReferralCode = referralCode || customerReferralCode; 1295 const referralData = JSON.parse(localStorage.getItem('referral_notification_data')) || {}; 1296 1297 if (referralData.timestamp && referralData.expirationDays) { 1298 const expirationTime = referralData.timestamp + (referralData.expirationDays * 24 * 60 * 60 * 1000); 1299 if (Date.now() > expirationTime) { 1300 localStorage.removeItem('referral_notification_data'); 1301 return; 1302 } 1303 } 1304 1305 if (referralData.dismissed) return; 1306 1307 if (activeReferralCode) { 1308 if (referralData.creditAmount) { 1309 displayReferralNotification(referralData.creditAmount); 1310 } else { 1311 fetchReferralCampaignData(activeReferralCode); 1312 } 1313 } 1314 } 1315 1316 function fetchReferralCampaignData(referralCode) { 1317 fetch(`/customer/referral_program/campaigns/${referralCode}`) 1318 .then(response => { 1319 if (!response.ok) { 1320 throw new Error('Network response was not ok'); 1321 } 1322 return response.json(); 1323 }) 1324 .then(data => { 1325 let refereeCreditAmount = null; 1326 if (data.data.campaign && data.data.campaign.referral_rules) { 1327 const refereeRule = data.data.campaign.referral_rules.find(rule => 1328 rule.target_type === 'referee' && 1329 rule.event_name === 'customer/create' && 1330 rule.entitlement && 1331 rule.entitlement.type === 'credit' 1332 ); 1333 1334 if (refereeRule) { 1335 refereeCreditAmount = refereeRule.entitlement.amount; 1336 } 1337 } 1338 1339 const referralData = JSON.parse(localStorage.getItem('referral_notification_data')) || {}; 1340 referralData.creditAmount = refereeCreditAmount; 1341 referralData.dismissed = referralData.dismissed || false; 1342 referralData.timestamp = Date.now(); 1343 referralData.expirationDays = 3; 1344 localStorage.setItem('referral_notification_data', JSON.stringify(referralData)); 1345 1346 displayReferralNotification(refereeCreditAmount); 1347 }) 1348 .catch(error => { 1349 removeCookie('customer_referral_code'); 1350 }); 1351 } 1352 1353 function displayReferralNotification(creditAmount = null) { 1354 const isMobile = innerWidth <= 749; 1355 1356 if (isMobile) { 1357 const mobileModal = document.querySelector('#referralMobileModal details'); 1358 const mobileMessageElement = document.getElementById('referralSignupMessageMobile'); 1359 1360 if (mobileModal && mobileMessageElement) { 1361 if (creditAmount) { 1362 const finalMessage = referralMessageTemplate 1363 .replace('__CREDIT_AMOUNT__', creditAmount) 1364 .replace('__CURRENCY_CODE__', shopPrefix); 1365 1366 mobileMessageElement.textContent = finalMessage; 1367 } 1368 1369 mobileModal.setAttribute('open', ''); 1370 } 1371 } else { 1372 const notification = document.getElementById('referralNotification'); 1373 1374 if (notification) { 1375 if (creditAmount) { 1376 const messageElement = document.getElementById('referralSignupMessage'); 1377 if (messageElement) { 1378 const finalMessage = referralMessageTemplate 1379 .replace('__CREDIT_AMOUNT__', creditAmount) 1380 .replace('__CURRENCY_CODE__', shopPrefix); 1381 1382 messageElement.textContent = finalMessage; 1383 } 1384 } 1385 1386 notification.style.display = 'block'; 1387 } 1388 } 1389 } 1390 1391 showReferralNotification(); 1392 1393 addEventListener('beforeunload', function() { 1394 const referralData = JSON.parse(localStorage.getItem('referral_notification_data')) || {}; 1395 1396 if (referralData.creditAmount !== undefined) { 1397 referralData.creditAmount = null; 1398 localStorage.setItem('referral_notification_data', JSON.stringify(referralData)); 1399 } 1400 }); 1401 1402 function closeReferralNotification() { 1403 const notification = document.getElementById('referralNotification'); 1404 if (notification) { 1405 notification.style.display = 'none'; 1406 } 1407 } 1408 1409 function dismissReferralNotification() { 1410 const referralData = JSON.parse(localStorage.getItem('referral_notification_data')) || {}; 1411 1412 referralData.dismissed = true; 1413 referralData.timestamp = referralData.timestamp || Date.now(); 1414 referralData.expirationDays = referralData.expirationDays || 3; 1415 1416 localStorage.setItem('referral_notification_data', JSON.stringify(referralData)); 1417 1418 closeReferralNotification(); 1419 } 1420 1421 function goToSignupPage() { 1422 location.href = '/account/register'; 1423 dismissReferralNotification(); 1424 } 1425 1426 function closeMobileReferralModal() { 1427 const mobileModal = document.querySelector('#referralMobileModal details'); 1428 if (mobileModal) { 1429 mobileModal.removeAttribute('open'); 1430 } 1431 1432 const referralData = JSON.parse(localStorage.getItem('referral_notification_data')) || {}; 1433 referralData.dismissed = true; 1434 referralData.timestamp = referralData.timestamp || Date.now(); 1435 referralData.expirationDays = referralData.expirationDays || 3; 1436 localStorage.setItem('referral_notification_data', JSON.stringify(referralData)); 1437 } 1438 1439 function goToSignupPageFromMobile() { 1440 location.href = '/account/register'; 1441 closeMobileReferralModal(); 1442 } 1443 // End - referral notification popup 1444</script>
1444 1445 1446 <header class="site-header" role="banner"> 1447 1448 <div class="wrapper"> 1449 1450 1451 1452 <div class="grid--full"> 1453 <div class="grid__item"> 1454 1455 <div class="h1 site-header__logo" itemscope itemtype="http://schema.org/Organization"> 1456 1457 1458 <a href="/" class="logo-wrapper" itemprop="url"> 1459 <img src="https://cdn.store-assets.com/s/39481/f/1112531.png?width=350&format=webp" alt="TS Bathroom Mall - Your One Stop Bathroom Solutions Need" itemprop="logo"> 1460 </a> 1461 1462 1463 </div> 1464 1465 1466 <p class="header-message large--hide"> 1467 <small></small> 1468 </p> 1469 1470 </div> 1471 </div> 1472 <div class="grid--full medium-down--hide"> 1473 <div class="grid__item"> 1474 1475<ul class="site-nav" id="AccessibleNav"> 1476 1477 1478 1479 <li > 1480 <a href="/" class="site-nav__link">Home</a> 1481 </li> 1482 1483 1484 1485 1486 1487 <li class="site-nav--has-dropdown" aria-haspopup="true"> 1488 <a href="/collections/all" class="site-nav__link"> 1489 Products 1490 <span class="icon-fallback-text"> 1491 <span class="icon icon-arrow-down" aria-hidden="true"></span> 1492 </span> 1493 </a> 1494 <ul class="site-nav__dropdown"> 1495 1496 1497 1498 1499 <li class="site-nav--has-dropdown"> 1500 <a href="/collections/water-pump" class="site-nav__link"> 1501 Water Pump 1502 <span class="icon-fallback-text"> 1503 <span class="icon icon-arrow-right" aria-hidden="true"></span> 1504 </span> 1505 </a> 1506 <ul class="site-nav__dropdown"> 1507 1508 1509 1510 <li > 1511 <a href="/collections/cnp" class="site-nav__link">CNP</a> 1512 </li> 1513 1514 1515 </ul> 1516 </li> 1517 1518 1519 1520 1521 1522 <li class="site-nav--has-dropdown"> 1523 <a href="/collections/sanitary-wares" class="site-nav__link"> 1524 Sanitary Wares 1525 <span class="icon-fallback-text"> 1526 <span class="icon icon-arrow-right" aria-hidden="true"></span> 1527 </span> 1528 </a> 1529 <ul class="site-nav__dropdown"> 1530 1531 1532 1533 1534 <li class="site-nav--has-dropdown"> 1535 <a href="/collections/sericite" class="site-nav__link"> 1536 Sericite 1537 <span class="icon-fallback-text"> 1538 <span class="icon icon-arrow-right" aria-hidden="true"></span> 1539 </span> 1540 </a> 1541 <ul class="site-nav__dropdown"> 1542 1543 <li> 1544 <a href="/collections/sericite-urinal-bowl" class="site-nav__link">Sericite Urinal Bowl</a> 1545 </li> 1546 1547 <li> 1548 <a href="/collections/sericite-wc" class="site-nav__link">Sericite WC</a> 1549 </li> 1550 1551 <li> 1552 <a href="/collections/sericite-basin" class="site-nav__link">Sericite Basin</a> 1553 </li> 1554 1555 </ul> 1556 </li> 1557 1558 1559 1560 1561 1562 <li class="site-nav--has-dropdown"> 1563 <a href="/collections/inno" class="site-nav__link"> 1564 Inno
1565 <span class="icon-fallback-text"> 1566 <span class="icon icon-arrow-right" aria-hidden="true"></span> 1567 </span> 1568 </a> 1569 <ul class="site-nav__dropdown"> 1570 1571 <li> 1572 <a href="/collections/inno-squatting-pan" class="site-nav__link">Inno Squatting Pan</a> 1573 </li> 1574 1575 <li> 1576 <a href="/collections/inno-urinal-bowl" class="site-nav__link">Inno Urinal Bowl</a> 1577 </li> 1578 1579 <li> 1580 <a href="/collections/inno-wc" class="site-nav__link">Inno WC</a> 1581 </li> 1582 1583 <li> 1584 <a href="/collections/inno-basin" class="site-nav__link">Inno Basin</a> 1585 </li> 1586 1587 </ul> 1588 </li> 1589 1590 1591 </ul> 1592 </li> 1593 1594 1595 1596 1597 1598 <li class="site-nav--has-dropdown"> 1599 <a href="/collections/plumbing-valve" class="site-nav__link"> 1600 Plumbing Valve 1601 <span class="icon-fallback-text"> 1602 <span class="icon icon-arrow-right" aria-hidden="true"></span> 1603 </span> 1604 </a> 1605 <ul class="site-nav__dropdown"> 1606 1607 1608 1609 <li > 1610 <a href="/collections/gkm" class="site-nav__link">GKM</a> 1611 </li> 1612 1613 1614 1615 1616 <li > 1617 <a href="/collections/city-valve" class="site-nav__link">City Valve</a> 1618 </li> 1619 1620 1621 1622 1623 <li > 1624 <a href="/collections/g-kent-valve" class="site-nav__link">g-Kent Valve</a> 1625 </li> 1626 1627 1628 1629 1630 <li > 1631 <a href="/collections/york-check-valve" class="site-nav__link">YORK Check Valve</a> 1632 </li> 1633 1634 1635 1636 1637 <li > 1638 <a href="/collections/lye" class="site-nav__link">LYE</a> 1639 </li> 1640 1641 1642 1643 1644 <li > 1645 <a href="/collections/water-meter" class="site-nav__link">Water Meter</a> 1646 </li> 1647 1648 1649 1650 1651 <li > 1652 <a href="/collections/pipe-repair-clamp" class="site-nav__link">Pipe Repair Clamp</a> 1653 </li> 1654 1655 1656 1657 1658 <li > 1659 <a href="/collections/others" class="site-nav__link">Others</a> 1660 </li> 1661 1662 1663 </ul> 1664 </li> 1665 1666 1667 1668 1669 1670 <li class="site-nav--has-dropdown"> 1671 <a href="/collections/water-tank---sewerage-tank" class="site-nav__link"> 1672 Water Tank & Sewerage Tank 1673 <span class="icon-fallback-text"> 1674 <span class="icon icon-arrow-right" aria-hidden="true"></span> 1675 </span> 1676 </a> 1677 <ul class="site-nav__dropdown"> 1678 1679 1680 1681 1682 <li class="site-nav--has-dropdown"> 1683 <a href="/collections/polytank" class="site-nav__link"> 1684 POLYTANK
1685 <span class="icon-fallback-text"> 1686 <span class="icon icon-arrow-right" aria-hidden="true"></span> 1687 </span> 1688 </a> 1689 <ul class="site-nav__dropdown"> 1690 1691 <li> 1692 <a href="/collections/camel" class="site-nav__link">CAMEL</a> 1693 </li> 1694 1695 <li> 1696 <a href="/collections/buffalo" class="site-nav__link">BUFFALO</a> 1697 </li> 1698 1699 <li> 1700 <a href="/collections/king-kong" class="site-nav__link">KING KONG</a> 1701 </li> 1702 1703 <li> 1704 <a href="/collections/hercules" class="site-nav__link">HERCULES</a> 1705 </li> 1706 1707 <li> 1708 <a href="/collections/compact" class="site-nav__link">COMPACT</a> 1709 </li> 1710 1711 </ul> 1712 </li> 1713 1714 1715 1716 1717 1718 <li class="site-nav--has-dropdown"> 1719 <a href="/collections/kossan-water-tank" class="site-nav__link"> 1720 Kossan Water Tank 1721 <span class="icon-fallback-text"> 1722 <span class="icon icon-arrow-right" aria-hidden="true"></span> 1723 </span> 1724 </a> 1725 <ul class="site-nav__dropdown"> 1726 1727 <li> 1728 <a href="/collections/kossan-square-tank" class="site-nav__link">Kossan Square Tank</a> 1729 </li> 1730 1731 <li> 1732 <a href="/collections/kossan-round-tank" class="site-nav__link">Kossan Round Tank</a> 1733 </li> 1734 1735 <li> 1736 <a href="/collections/kossan-rectangular-tank" class="site-nav__link">Kossan Rectangular Tank</a> 1737 </li> 1738 1739 </ul> 1740 </li> 1741 1742 1743 1744 1745 1746 <li class="site-nav--has-dropdown"> 1747 <a href="/collections/king-kong-stainless-steel-tank" class="site-nav__link"> 1748 King Kong Stainless Steel Tank 1749 <span class="icon-fallback-text"> 1750 <span class="icon icon-arrow-right" aria-hidden="true"></span> 1751 </span> 1752 </a> 1753 <ul class="site-nav__dropdown"> 1754 1755 <li> 1756 <a href="/collections/king-kong-horizontal-with-stand" class="site-nav__link">King Kong Horizontal With Stand</a> 1757 </li> 1758 1759 <li> 1760 <a href="/collections/king-kong-vertical-round-bottom-with-stand" class="site-nav__link">King Kong Vertical Round Bottom With Stand</a> 1761 </li> 1762 1763 <li> 1764 <a href="/collections/king-kong-vertical-flat-bottom-without-stand" class="site-nav__link">King Kong Vertical Flat Bottom Without Stand</a> 1765 </li> 1766 1767 </ul> 1768 </li> 1769 1770 1771 1772 1773 <li > 1774 <a href="/collections/septic-tank" class="site-nav__link">Septic Tank</a> 1775 </li> 1776 1777 1778 </ul> 1779 </li> 1780 1781 1782 1783 1784 1785 <li class="site-nav--has-dropdown"> 1786 <a href="/collections/water-heater" class="site-nav__link"> 1787 Water Heater
1788 <span class="icon-fallback-text"> 1789 <span class="icon icon-arrow-right" aria-hidden="true"></span> 1790 </span> 1791 </a> 1792 <ul class="site-nav__dropdown"> 1793 1794 1795 1796 <li > 1797 <a href="/collections/solar-heater" class="site-nav__link">Solar Heater</a> 1798 </li> 1799 1800 1801 1802 1803 <li > 1804 <a href="/collections/instant-water-heater" class="site-nav__link">Instant Water Heater</a> 1805 </li> 1806 1807 1808 1809 1810 <li > 1811 <a href="/collections/heater-storage-tank" class="site-nav__link">Heater Storage Tank</a> 1812 </li> 1813 1814 1815 </ul> 1816 </li> 1817 1818 1819 1820 1821 1822 <li class="site-nav--has-dropdown"> 1823 <a href="/collections/water-pipes-and-fittings" class="site-nav__link"> 1824 Water Pipes and Fittings 1825 <span class="icon-fallback-text"> 1826 <span class="icon icon-arrow-right" aria-hidden="true"></span> 1827 </span> 1828 </a> 1829 <ul class="site-nav__dropdown"> 1830 1831 1832 1833 <li > 1834 <a href="/collections/pvc-pipes--sirim-" class="site-nav__link">PVC Pipes (Sirim)</a> 1835 </li> 1836 1837 1838 1839 1840 <li > 1841 <a href="/collections/pvc-pipes--non-sirim-" class="site-nav__link">PVC Pipes (Non Sirim)</a> 1842 </li> 1843 1844 1845 1846 1847 <li > 1848 <a href="/collections/pvc-fittings" class="site-nav__link">PVC Fittings</a> 1849 </li> 1850 1851 1852 1853 1854 <li > 1855 <a href="/collections/upvc-underground-pipe--sirim-" class="site-nav__link">Underground Pipe (Sirim)</a> 1856 </li> 1857 1858 1859 1860 1861 <li > 1862 <a href="/collections/upvc-pipes--sirim-" class="site-nav__link">UPVC Pipes (Sirim)</a> 1863 </li> 1864 1865 1866 1867 1868 <li > 1869 <a href="/collections/upvc-pipes--non-sirim-" class="site-nav__link">UPVC Pipes (Non Sirim)</a> 1870 </li> 1871 1872 1873 1874 1875 <li > 1876 <a href="/collections/upvc-underground-fittings" class="site-nav__link">Underground Fittings</a> 1877 </li> 1878 1879 1880 1881 1882 <li > 1883 <a href="/collections/upvc-swv-fittings" class="site-nav__link">UPVC SWV Fittings</a> 1884 </li> 1885 1886 1887 1888 1889 <li > 1890 <a href="/collections/buteline-pipes---fittings" class="site-nav__link">Buteline Pipes & Fittings</a> 1891 </li> 1892 1893 1894 1895 1896 <li > 1897 <a href="/collections/hansen-fittings" class="site-nav__link">Hansen Fittings</a> 1898 </li> 1899 1900 1901 1902 1903 <li > 1904 <a href="/collections/gf-ppr-pipes---fittings" class="site-nav__link">GF PPR Pipes & Fittings</a> 1905 </li> 1906 1907 1908 1909 1910 <li > 1911 <a href="/collections/kembla-copper-pipes" class="site-nav__link">Kembla Copper Pipes</a> 1912 </li> 1913 1914 1915 1916 1917 <li > 1918 <a href="/collections/hubless-pipes-and-fittings" class="site-nav__link">Hubless Pipes and Fittings</a> 1919 </li> 1920 1921 1922 1923 1924 <li > 1925 <a href="/collections/conex-copper-fittings" class="site-nav__link">Conex Copper Fittings</a> 1926 </li> 1927 1928 1929 1930 1931 <li > 1932 <a href="/collections/delcop-copper-welding-fittings" class="site-nav__link">DELCOP Copper Welding Fittings</a> 1933 </li> 1934 1935 1936 1937 1938 <li > 1939 <a href="/collections/gi-fittings" class="site-nav__link">GI Fittings</a> 1940 </li> 1941 1942 1943 1944 1945 <li > 1946 <a href="/collections/gi-pipes" class="site-nav__link">GI Pipes</a> 1947 </li> 1948 1949 1950 1951 1952 <li > 1953 <a href="/collections/gi-aa-pipes" class="site-nav__link">GI AA Pipes</a> 1954 </li> 1955 1956 1957 1958 1959 <li > 1960 <a href="/collections/stainless-steel-pipe" class="site-nav__link">Stainless Steel Pipe</a> 1961 </li> 1962 1963 1964 1965 1966 <li > 1967 <a href="/collections/stainless-steel-fittings" class="site-nav__link">Stainless Steel Fittings</a> 1968 </li> 1969 1970 1971 1972 1973 <li > 1974 <a href="/collections/techplas" class="site-nav__link">Techplas</a> 1975 </li> 1976 1977 1978 1979 1980 <li > 1981 <a href="/collections/others-1" class="site-nav__link">Others</a> 1982 </li> 1983 1984 1985 </ul> 1986 </li> 1987 1988 1989 1990 1991 1992 <li class="site-nav--has-dropdown"> 1993 <a href="/collections/wholesales-price" class="site-nav__link"> 1994 Wholesales Price
1995 <span class="icon-fallback-text"> 1996 <span class="icon icon-arrow-right" aria-hidden="true"></span> 1997 </span> 1998 </a> 1999 <ul class="site-nav__dropdown"> 2000 2001 2002 2003 <li > 2004 <a href="/collections/wholesale-upvc-swv-fittings" class="site-nav__link">Wholesale UPVC SWV Fittings</a> 2005 </li> 2006 2007 2008 2009 2010 <li > 2011 <a href="/collections/wholesale-upvc-pipes--sirim-" class="site-nav__link">Wholesale UPVC Pipes (Sirim)</a> 2012 </li> 2013 2014 2015 2016 2017 <li > 2018 <a href="/collections/wholesale-buteline-fittings" class="site-nav__link">Wholesale Buteline Fittings</a> 2019 </li> 2020 2021 2022 </ul> 2023 </li> 2024 2025 2026 </ul> 2027 </li> 2028 2029 2030 2031 2032 <li > 2033 <a href="/pages/about-us" class="site-nav__link">About Us</a> 2034 </li> 2035 2036 2037 2038 2039 <li > 2040 <a href="/pages/catalogue" class="site-nav__link">Catalogue</a> 2041 </li> 2042 2043 2044 2045</ul> 2046 </div> 2047 </div> 2048 2049 2050 2051 </div> 2052 </header> 2053 2054 <main class="wrapper main-content" role="main"> 2055 <div class="grid"> 2056 2057 <div class="grid__item medium-down--hide large--one-quarter"> 2058 <hr class="hr--small hr--clear"> 2059 <nav> 2060 <font size="5"> <hr><b>Category<br></b></font> 2061 2062 <a href="/collections/water-pump">Water Pump</a><br> 2063 2064 <a href="/collections/sanitary-wares">Sanitary Wares</a><br> 2065 2066 <a href="/collections/plumbing-valve">Plumbing Valve</a><br> 2067 2068 <a href="/collections/water-tank---sewerage-tank">Water Tank & Sewerage Tank</a><br> 2069 2070 <a href="/collections/water-heater">Water Heater</a><br> 2071 2072 <a href="/collections/water-pipes-and-fittings">Water Pipes and Fittings</a><br> 2073 2074 <a href="/collections/wholesales-price">Wholesales Price</a><br> 2075 2076 <hr> 2077 </nav> 2078 </div> 2079 2080 <div class="grid__item large--three-quarters">
vendor: 94 bytes, lines 2080-2084
2080 2081 2082 2083 2084<script src="https://www.recaptcha.net/recaptcha/enterprise.js?render=
20846LdMNdIqAAAAALegJp7y4weVcCIlnwmIN8TWseEF
vendor: 12 bytes, line 2084
2084"></script>
2085<link rel="preload" href="https://store-themes.easystore.co/39481/themes/336/assets/customer.css?t=1787072412" as="style" onload="this.onload=null;this.rel='stylesheet'"> 2086<style> 2087 main.main-content .field > label{ 2088 display: none; 2089 } 2090 .h3{ 2091 font-size: 18px; 2092 } 2093 .button--full-width{ 2094 width: 100%; 2095 } 2096 .tab-wrapper{ 2097 display: inline-flex; 2098 justify-content: center; 2099 align-items: center; 2100 margin: 10px auto 20px; 2101 background-color: #eaeaea; 2102 border-radius: 200px; 2103 padding: 5px; 2104 gap: 10px; 2105 } 2106 .tab-wrapper .tab-label { 2107 display: inline-block; 2108 min-width: 130px; 2109 padding: 8px 15px; 2110 border-radius: 100px; 2111 cursor: pointer; 2112 font-size: 17px; 2113 line-height: 1.1; 2114 user-select: none; 2115 margin: 0; 2116 color: #000; 2117 } 2118 #register-tos a { 2119 display: inline; 2120 } 2121 @media screen and (max-width: 400px){ 2122 .tab-wrapper .tab-label { 2123 min-width: 110px; 2124 } 2125 } 2126 .tab-wrapper input[type='radio']:checked + .tab-label { 2127 background-color: #fff; 2128 box-shadow: 2px 2px 25px -10px #333; 2129 } 2130 2131 @media screen and (min-width: 500px){ 2132 .large--five-twelfths{ 2133 min-width: 420px; 2134 } 2135 } 2136</style> 2137 2138 2139 2140 2141<div class="customer login section-width-control small-width large--five-twelfths text-center wrapper"> 2142 <div class="card"> 2143 <h1 id="login" tabindex="-1" class="mb-0"> 2144 Welcome 2145 </h1> 2146 <p class="h3 mt-0"> 2147 2148 Sign up or log in to continue 2149 2150 </p> 2151 <div> 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 <div class="tab-wrapper"> 2170 <div> 2171 <input type="radio" name="type" id="tab-register" class="hide hidden" checked> 2172 <label for="tab-register" class="tab-label"> 2173 Sign up 2174 </label> 2175 </div> 2176 <div> 2177 <input type="radio" name="type" id="tab-login" class="hide hidden"> 2178 <label for="tab-login" class="tab-label"> 2179 Login 2180 </label> 2181 </div> 2182 </div> 2183 2184 2185 <form id="form-login" action="/account/auth" method="post" class="mt-2"> 2186 <input type="hidden" name="_token" value="CfeJEOxOvhUxEgvKeM0ruK5x6bNBCUp3YcMNjaAT"> 2187 <input type="hidden" name="location_id" value=""> 2188 <input type="hidden" name="staff_id" value=""> 2189 <input type="hidden" id="recaptchaResponse" name="g-recaptcha-response"> 2190 2191 2192 <div id="error-content"></div> 2193 2194 <div class="field"> 2195 <input 2196 type="text" 2197 name="customer[email_or_phone]" 2198 id="CustomerDetail" 2199 autocorrect="off" 2200 autocapitalize="off" 2201 placeholder="Email" 2202 required 2203 > 2204 <label for="CustomerDetail"> 2205 Email 2206 </label> 2207 </div> 2208 2209 <button 2210 type="submit" 2211 form="form-login" 2212 class="btn button--full-width g-recaptcha"> 2213 Continue 2214 </button> 2215 2216 <div class="button-snippet"> 2217 2218 </div> 2219 2220 2221 2222 <div id="register-tos"> 2223 By signing up, I agree to the TS Bathroom Mall - Your One Stop Bathroom Solutions Need's <a target="_blank" href="/legal/terms-of-service">Terms of Service</a>, <a target="_blank" href="/legal/privacy-policy">Privacy Policy</a>, <a target="_blank" href="/legal/refund-policy">Refund Policy</a> and <a target="_blank" href="/legal/delivery">Delivery</a>. 2224 </div> 2225 2226 </form> 2227 </div> 2228 </div> 2229 2230</div> 2231 2232 2233
2234<script> 2235 const register_tab = document.querySelector('#tab-register'); 2236 const login_tab = document.querySelector('#tab-login'); 2237 const register_tos = document.querySelector('#register-tos'); 2238 const isRegistrationEnabled = "1"; 2239 2240 if (register_tab) { 2241 register_tab.addEventListener('click', function() { 2242 if (register_tos) { 2243 register_tos.style.display = 'block'; 2244 } 2245 }); 2246 } 2247 if (login_tab) { 2248 login_tab.addEventListener('click', function() { 2249 if (register_tos) { 2250 register_tos.style.display = 'none'; 2251 } 2252 }); 2253 } 2254 2255 const methodSwitchBtn = document.getElementById('phone-login-btn'); 2256 if (methodSwitchBtn) { 2257 methodSwitchBtn.onclick = function () { 2258 var type = (register_tab && register_tab.checked) ? 'register' : 'login'; 2259 window.location.href = '/account/login?method=phone&type=' + type; 2260 }; 2261 } 2262 2263 let lastPathSegment = window.location.pathname.split('/').filter(segment => segment.length > 0).pop(); 2264 2265 // Redirect to login if trying to access register when self registration is disabled 2266 if (!isRegistrationEnabled) { 2267 if (lastPathSegment === 'register') { 2268 window.location.href = '/account/login'; 2269 } else if (lastPathSegment === 'login' || lastPathSegment === 'auth') { 2270 if (register_tos) { 2271 register_tos.style.display = 'none'; 2272 } 2273 } 2274 } else { 2275 // Only proceed with tab logic if not redirecting 2276 if(lastPathSegment === 'login') { 2277 if (login_tab) { 2278 login_tab.checked = true; 2279 } 2280 if (register_tos) { 2281 register_tos.style.display = 'none'; 2282 } 2283 } else { 2284 if (register_tos) { 2285 register_tos.style.display = 'block'; 2286 } 2287 } 2288 } 2289 2290 const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; 2291 const phonePattern = /^(?:\+?\d{1,3}[\s-]?)?(?:\d{1,14}[\s-]?){1,5}$/; 2292 2293 let form = document.querySelector('#form-login'), 2294 input = document.querySelector('#CustomerDetail'), 2295 error_content = document.querySelector('#error-content'); 2296 2297 2298 // One attempt at a time: the reCAPTCHA wait below is async, so without 2299 // this a double-click starts two continuations and posts the form twice. 2300 var submitting = false; 2301 2302 form.addEventListener('submit', async function (event) { 2303 // preventDefault FIRST: a re-entrant submit must still be cancelled, or the 2304 // browser performs a native post with no captcha token. 2305 event.preventDefault(); 2306 if (submitting) return; 2307 2308 let input_value = input.value 2309 error_content.innerHTML = ''; 2310 if (!input_value.match(emailPattern) && !input_value.match(phonePattern)) { 2311 error_content.innerHTML = `<div class="errors note">Invalid Email or Phone</div>`; 2312 return false; 2313 } 2314 2315 submitting = true; 2316 document.querySelector('#form-login .btn').classList.add('btn--loading','loading'); 2317 2318 // reCAPTCHA is unreachable from some regions (google.com is blocked in 2319 // mainland China). Fail open the same way checkout and forgot-password do: 2320 // submit without a token rather than leave the button spinning forever. 2321 try { 2322 if (window.grecaptcha && typeof window.grecaptcha.enterprise === 'object') { 2323 // The loader defines a stub {ready} synchronously and fetches the real 2324 // implementation asynchronously, so execute() may not exist yet. 2325 // ready() queues until it does; the timeout stops a bundle that never 2326 // arrives from hanging the button. 2327 await new Promise(function (resolve) { 2328 setTimeout(resolve, 3000); 2329 try { grecaptcha.enterprise.ready(resolve); } catch (e) { resolve(); } 2330 }); 2331 2332 const token = await grecaptcha.enterprise.execute('6LdMNdIqAAAAALegJp7y4weVcCIlnwmIN8TWseEF', { action: 'LOGIN' }); 2333 document.getElementById('recaptchaResponse').value = token; 2334 } 2335 } catch (e) { /* fail open */ } 2336 2337 document.getElementById('form-login').submit(); 2338 }); 2339 2340 2341</script>
2341 2342 2343 2344 2345 </div> 2346 </div> 2347 2348 </main> 2349 2350 <footer class="site-footer small--text-center" role="contentinfo"> 2351 2352 <div class="wrapper"> 2353 2354 <div class="grid-uniform"> 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 <div class="grid__item large--one-half medium--one-half"> 2377 2378 <h4>Quick Links</h4> 2379 2380 <ul class="site-footer__links"> 2381 2382 <li><a href="http://cp.easystore.my/" >Back to Control Panel</a></li> 2383 2384 2385 </ul> 2386 </div> 2387 2388 2389 2390 <div class="grid__item large--one-half medium--one-half"> 2391 <h4>Follow Us</h4> 2392 2393 <ul class="inline-list social-icons"> 2394 2395 <li> 2396 <a class="icon-fallback-text" href="https://twitter.com/" title="TS Bathroom Mall - Your One Stop Bathroom Solutions Need on Twitter" target="_blank"> 2397 <span class="icon icon-twitter" aria-hidden="true"></span> 2398 <span class="fallback-text">Twitter</span> 2399 </a> 2400 </li> 2401 2402 2403 <li> 2404 <a class="icon-fallback-text" href="https://www.facebook.com/" title="TS Bathroom Mall - Your One Stop Bathroom Solutions Need on Facebook" target="_blank"> 2405 <span class="icon icon-facebook" aria-hidden="true"></span> 2406 <span class="fallback-text">Facebook</span> 2407 </a> 2408 </li> 2409 2410 2411 <li> 2412 <a class="icon-fallback-text" href="https://www.pinterest.com/" title="TS Bathroom Mall - Your One Stop Bathroom Solutions Need on Pinterest" target="_blank"> 2413 <span class="icon icon-pinterest" aria-hidden="true"></span> 2414 <span class="fallback-text">Pinterest</span> 2415 </a> 2416 </li> 2417 2418 2419 <li> 2420 <a class="icon-fallback-text" href="https://plus.google.com/" title="TS Bathroom Mall - Your One Stop Bathroom Solutions Need on Google Plus" rel="publisher" target="_blank"> 2421 <span class="icon icon-google_plus" aria-hidden="true"></span> 2422 <span class="fallback-text">Google</span> 2423 </a> 2424 </li> 2425 2426 2427 <li> 2428 <a class="icon-fallback-text" href="https://instagram.com/" title="TS Bathroom Mall - Your One Stop Bathroom Solutions Need on Instagram" target="_blank"> 2429 <span class="icon icon-instagram" aria-hidden="true"></span> 2430 <span class="fallback-text">Instagram</span> 2431 </a> 2432 </li> 2433 2434 2435 <li> 2436 <a class="icon-fallback-text" href="https://www.tumblr.com/" title="TS Bathroom Mall - Your One Stop Bathroom Solutions Need on Tumblr" target="_blank"> 2437 <span class="icon icon-tumblr" aria-hidden="true"></span> 2438 <span class="fallback-text">Tumblr</span> 2439 </a> 2440 </li> 2441 2442 2443 <li> 2444 <a class="icon-fallback-text" href="https://www.youtube.com/" title="TS Bathroom Mall - Your One Stop Bathroom Solutions Need on YouTube" target="_blank"> 2445 <span class="icon icon-youtube" aria-hidden="true"></span> 2446 <span class="fallback-text">YouTube</span> 2447 </a> 2448 </li> 2449 2450 2451 <li> 2452 <a class="icon-fallback-text" href="https://vimeo.com/" title="TS Bathroom Mall - Your One Stop Bathroom Solutions Need on Vimeo" target="_blank"> 2453 <span class="icon icon-vimeo" aria-hidden="true"></span> 2454 <span class="fallback-text">Vimeo</span> 2455 </a> 2456 </li> 2457 2458 2459 2460</ul> 2461 2462 </div> 2463 2464 </div> 2465 2466 <hr class="hr--small hr--clear"> 2467 2468 2469 <div class="grid"> 2470 2471 <div class="grid__item text-center"> 2472 <ul class="inline-list payment-icons"> 2473 2474 <li> 2475 <span class="icon-fallback-text"> 2476 <span class="icon icon-visa" aria-hidden="true"></span> 2477 <span class="fallback-text">Visa</span> 2478 </span> 2479 </li> 2480 2481 2482 <li> 2483 <span class="icon-fallback-text">
2484 <span class="icon icon-master" aria-hidden="true"></span> 2485 <span class="fallback-text">Master</span> 2486 </span> 2487 </li> 2488 2489 2490 2491 2492 2493 </ul> 2494 </div> 2495 2496 2497 <div class="grid__item text-center"> 2498 <ul class="inline-list payment-icons"> 2499 2500 <li> 2501 <img src="https://s3.dualstack.ap-southeast-1.amazonaws.com/eesb.public/images/payment/payments_bank-transfer.svg" alt="payment_bank-transfer"> 2502 </li> 2503 2504 <li> 2505 <img src="https://s3.dualstack.ap-southeast-1.amazonaws.com/eesb.public/images/payment/payments_ipay88.svg" alt="payment_ipay88"> 2506 </li> 2507 2508 <li> 2509 <img src="https://s3.dualstack.ap-southeast-1.amazonaws.com/eesb.public/images/payment/payments_maybankqrpay.svg" alt="payment_maybankqrpay"> 2510 </li> 2511 2512 </ul> 2513 </div> 2514 2515 2516 </div> 2517 2518 2519 <div class="grid"> 2520 <div class="grid__item text-center"> 2521 <p class="site-footer__links">Copyright © 2018 TS Bathroom Mall - Your One Stop Bathroom Solutions Need. E-commerce Powered by <a href="http://www.easystore.co/?utm_source=storefront&utm_medium=sf_ref_footer&utm_campaign=sf_ref">EasyStore</a></p> 2522 2523 <p class="site-footer__links"> 2524 2525 2526 2527 <a href="/legal/terms-of-service">Terms of Service</a> 2528 2529 2530 2531 2532 | <a href="/legal/privacy-policy">Privacy Policy</a> 2533 2534 2535 2536 2537 | <a href="/legal/refund-policy">Refund Policy</a> 2538 2539 2540 2541 2542 | <a href="/legal/delivery">Delivery</a> 2543 2544 2545 2546 </p> 2547 2548 </div> 2549 </div> 2550 2551 </div> 2552 </footer> 2553 2554 2555 2556
2556<script src="https://store-themes.easystore.co/39481/themes/336/assets/fastclick.min.js?t=1787072412" type="text/javascript" ></script>
2556 2557
2557<script src="https://store-themes.easystore.co/39481/themes/336/assets/timber.js?t=1787072412" type="text/javascript" ></script>
2557 2558
2558<script src="https://store-themes.easystore.co/39481/themes/336/assets/theme.js?t=1787072412" type="text/javascript" ></script>
2558 2559 2560 2561 2562 2563 2564 2565
2566<script src="/assets/currencies.js" type="text/javascript" ></script>
vendor: 2 bytes, line 2566
2566
2567<script src="/assets/jquery.currencies.min.js" type="text/javascript" ></script>
vendor: 2 bytes, line 2567
2567
2568<script src="/assets/currencies_format.js" type="text/javascript" ></script>
2568 2569
2570<script> 2571Currency.format = "money_with_currency_format"; 2572var shopCurrency = 'MYR'; 2573var selectedCurrency = ''; 2574 2575 2576 Currency.convert = function(amount, from, to) { 2577 return amount; 2578 }; 2579 2580 2581if( $.cookie("currency") == null ){ 2582 2583 if( selectedCurrency != false ) { 2584 2585 Currency.convertAll(shopCurrency, selectedCurrency); 2586 jQuery('.selected-currency').text(Currency.currentCurrency); 2587 2588 } 2589 2590} 2591 2592/* Sometimes merchants change their shop currency, let's tell our JavaScript file */ 2593// Currency.moneyFormats[shopCurrency].money_with_currency_format = ""; 2594// Currency.moneyFormats[shopCurrency].money_format = "RM {{amount}} "; 2595 2596/* Default currency */ 2597var defaultCurrency = 'MYR'; 2598 2599/* Cookie currency */ 2600var cookieCurrency = Currency.cookie.read(); 2601 2602 cookieCurrency = shopCurrency; 2603 2604$("input[name=current_currency]").val(cookieCurrency); 2605if(cookieCurrency == null){ 2606 cookieCurrency = shopCurrency; 2607 Currency.cookie.write(shopCurrency); 2608} 2609/* Fix for customer account pages */ 2610jQuery('span.money span.money').each(function() { 2611 jQuery(this).parents('span.money').removeClass('money'); 2612}); 2613/* Saving the current price */ 2614jQuery('span.money').each(function() { 2615 jQuery(this).attr('data-currency-MYR', jQuery(this).html()); 2616}); 2617// If there's no cookie. 2618if (cookieCurrency == null) { 2619 if (shopCurrency !== defaultCurrency) { 2620 Currency.convertAll(shopCurrency, defaultCurrency); 2621 } 2622 else { 2623 Currency.currentCurrency = defaultCurrency; 2624 } 2625} 2626// If the cookie value does not correspond to any value in the currency dropdown. 2627else if (jQuery('[name=currencies]').size() && jQuery('[name=currencies] option[value=' + cookieCurrency + ']').size() === 0) { 2628 Currency.currentCurrency = shopCurrency; 2629 Currency.cookie.write(shopCurrency); 2630} 2631else if (cookieCurrency === shopCurrency) { 2632 Currency.currentCurrency = shopCurrency; 2633 Currency.convertAll(shopCurrency, cookieCurrency); 2634} 2635else { 2636 Currency.convertAll(shopCurrency, cookieCurrency); 2637} 2638jQuery('[name=currencies]').val(Currency.currentCurrency).change(function() { 2639 var newCurrency = jQuery(this).val(); 2640 2641 2642 Currency.cookie.write(newCurrency) 2643 if ('customers' == 'cart') { 2644 const params = new URLSearchParams(window.location.search); 2645 params.set('delete_cache', 1); 2646 window.location.search = params; 2647 } else { 2648 window.location.reload(); 2649 } 2650 2651}); 2652var original_selectCallback = window.selectCallback; 2653var selectCallback = function(variant, selector) { 2654 var currencies = null; 2655 original_selectCallback(variant, selector); 2656 if(typeof jQuery('[name=currencies]').val() == "undefined"){ 2657 currencies = shopCurrency; 2658 } 2659 else{ 2660 currencies = jQuery('[name=currencies]').val(); 2661 } 2662 Currency.convertAll(shopCurrency, currencies); 2663 jQuery('.selected-currency').text(Currency.currentCurrency); 2664}; 2665$('body').on('ajaxCart.afterCartLoad', function(cart) { 2666 Currency.convertAll(shopCurrency, jQuery('[name=currencies]').val()); 2667 jQuery('.selected-currency').text(Currency.currentCurrency); 2668}); 2669jQuery('.selected-currency').text(Currency.currentCurrency); 2670</script>
2670 2671 2672 2673 2674<!-- Snippet:global/body_end: Product Label -->
2675<script> 2676 $(".product-label-badge").parent().find(".badge--sale").remove(); 2677 </script>
2677 2678 2679 <style> 2680 .product-label-badge-text { 2681 display: table-cell; 2682 vertical-align: middle; 2683 padding: 2px 8px 0; 2684 border-radius: 50%; 2685 } 2686 .product-label-badge{ 2687 display: table; 2688 position: absolute; 2689 width: 60px; 2690 height: 60px; 2691 border-radius: 50%; 2692 font-weight: bold; 2693 text-align: center; 2694 font-size: .75em; 2695 line-height: 1.1; 2696 z-index: 10; 2697 } 2698 2699 .product-label-position{ 2700 top: -12px; 2701 right: -12px; 2702 } 2703 </style> 2704<!-- /Snippet -->
2705<script> 2706 2707 /* 2708 2709 ------ 2710 Events 2711 ------ 2712 2713 pages/viewed 2714 2715 customers/signup 2716 2717 customers/login 2718 2719 products/searched 2720 2721 collections/viewed 2722 2723 products/viewed 2724 2725 products/shared 2726 2727 wishlists/item_added 2728 2729 carts/items_added 2730 2731 carts/viewed 2732 2733 carts/item_removed 2734 2735 checkouts/initiated 2736 2737 checkouts/shipping_info_added 2738 2739 checkouts/payment_info_added 2740 2741 checkouts/completed 2742 2743 orders/placed 2744 2745 orders/purchased 2746 2747 payments/captured 2748 2749 payments/failed 2750 2751 */ 2752 2753 window.__latest_cart = window.__latest_cart || null; 2754 2755 const _selector = document.querySelector.bind(document), 2756 _selectorAll = document.querySelectorAll.bind(document) 2757 2758 document.addEventListener('DOMContentLoaded', (event) => { 2759 2760 const default_currency = getCookie("currency") 2761 const customer_id = window.__st.cid 2762 const template = window.__st.p 2763 2764 onPageView() 2765 2766 //-----------------------// 2767 // Configs // 2768 //-----------------------// 2769 2770 window.dataLayer = window.dataLayer || [] 2771 2772 Array.prototype.last = Array.prototype.last || function() { 2773 return this[this.length - 1] || null 2774 } 2775 2776 Array.prototype.first = Array.prototype.first || function() { 2777 return this[0] || null 2778 } 2779 2780 //----------------------// 2781 // Triggers // 2782 //----------------------// 2783 2784 const XHR = window.XMLHttpRequest 2785 2786 function xhr() { 2787 2788 const xhr = new XHR() 2789 2790 xhr.addEventListener("readystatechange", function() { 2791 2792 if(xhr.readyState != 4) return 2793 2794 try { 2795 2796 const response_url = xhr.responseURL 2797 2798 if (response_url.includes("cart/add")) { 2799 2800 window.__latest_cart = JSON.parse(xhr.response) 2801 2802 onCartItemsAdded() // ok 2803 2804 } 2805 2806 if (response_url.includes("cart/remove_item_quantity")) { 2807 2808 window.__latest_cart = JSON.parse(xhr.response) 2809 2810 onCartItemRemoved() // ok 2811 2812 } 2813 2814 if (response_url.includes("new_cart?retrieve=true")) { 2815 2816 result = JSON.parse(xhr.response) 2817 2818 if (result.cart) { 2819 window.__latest_cart = result.cart 2820 } 2821 2822 } 2823 2824 } catch(e) { 2825 2826 console.error(e) 2827 2828 } 2829 2830 }, false); 2831 2832 return xhr 2833 2834 } 2835 2836 window.XMLHttpRequest = xhr 2837 2838 switch(template) { 2839 2840 case 'cart': onCartView() // ok 2841 break 2842 case 'product': onProductView() // ok 2843 break 2844 case 'collection': onCollectionView() // ok 2845 break 2846 case 'blog': onBlogView() 2847 break 2848 case 'article': onArticleView() 2849 break 2850 case 'payment_completed': onOrderPlace() 2851 break 2852 case 'payment_fail': onPaymentFail() 2853 break 2854 2855 } 2856 2857 // _selector('form[action="/checkout/payments"]').addEventListener("submit", onPaymentInfoAdded(_selector('form[action="/checkout/payments"]'))) 2858 if (_selector('form[action="/checkout/detail"]')) _selector('form[action="/checkout/detail"]').addEventListener("submit", ()=>{onShippingInfoAdded(_selector('form[action="/checkout/detail"]'))}) // ok 2859 if (_selector('form[action="/checkout/shipping"]')) _selector('form[action="/checkout/shipping"]').addEventListener("submit", ()=>{onShippingInfoAdded(_selector('form[action="/checkout/shipping"]'))}) // ok 2860 if (_selector('form[action="/account/register"]')) _selector('form[action="/account/register"]').addEventListener("submit", onSignUp) // ok 2861 if (_selector('form[action="/account/login"]')) _selector('form[action="/account/login"]').addEventListener("submit", onLogin) // ok 2862 if (_selector('form[action="/search"]')) _selector('form[action="/search"]').addEventListener("submit", onProductSearch()) // ok 2863 if (_selector('#line-login-btn')) _selector('#line-login-btn').addEventListener('click', onLineLogin) // ok 2864 if (_selector('#PlaceOrder')) _selector('#PlaceOrder').addEventListener("click", ()=>{onCheckoutComplete(_selector('form[action="/checkout/payments"]'))}); // ok 2865 if (_selector('#add_wishlist')) _selector('#add_wishlist').addEventListener("click", onWishlistItemAdded) // ok 2866 if (_selectorAll('a[href="/account/logout"]')) _selectorAll('a[href="/account/logout"]').forEach(logoutButton => logoutButton.addEventListener("click", onLogout)) // ok 2867 if (_selector('.CartDrawerTrigger.cart-page-link')) _selector('.CartDrawerTrigger.cart-page-link').addEventListener("click", onCartView) // ok 2868 if (_selector('.CartDrawerTrigger.cart-page-link.mobile-cart-page-link')) _selector('.CartDrawerTrigger.cart-page-link.mobile-cart-page-link').addEventListener("click", onCartView) // ok 2869 if (_selectorAll('a[class^="share-"]')) _selectorAll('a[class^="share-"]').forEach(shareButton => shareButton.addEventListener("click", ()=>{onProductShare(shareButton)}) ) // ok 2870 if (_selectorAll('[name$="checkout"]')) _selectorAll('[name$="checkout"]').forEach(checkoutButton => checkoutButton.addEventListener("click", onCheckoutInitiate)) 2871 2872 // For append elements 2873 const bodyMutationObserver = new MutationObserver(() => { 2874 if (_selector('#form__spc #PlaceOrder')) _selector('#form__spc #PlaceOrder').addEventListener("click", onSinglePageCheckout) // ok 2875 if (_selectorAll('[name$="checkout"]')) _selectorAll('[name$="checkout"]').forEach(checkoutButton => checkoutButton.addEventListener("click", onCheckoutInitiate)) 2876 }); 2877 bodyMutationObserver.observe(_selector("body"), {subtree: true, childList: true}); 2878 2879 //------------------------------// 2880 // Event handlers // 2881 //------------------------------// 2882 2883 function onPageView() { 2884 2885 EasyStore.Event.dispatch('pages/viewed', { 2886 page: { 2887 type: template, 2888 title: document.title, 2889 description: _selector('meta[name=description]') ? _selector('meta[name=description]').getAttribute('content') : null, 2890 url: location.href, 2891 } 2892 }) 2893 2894 } 2895 2896 function onSignUp() { 2897 2898 const email_regex = new RegExp(/^[a-zA-Z0-9.!#$%&"*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/) 2899 const phone_regex = new RegExp(/^(\+?6?01)[0-46-9]-*[0-9]{7,8}$/) 2900 2901 const email_or_phone = _selector(`input[name="customer[email_or_phone]"]`).value.replace("+", "").replace(/\s+/, "") 2902 2903 let data = {} 2904 2905 if (email_regex.test(email_or_phone)) { 2906 data.method = 'email' 2907 data.email = email_or_phone 2908 } 2909 2910 if (phone_regex.test(email_or_phone)) { 2911 data.method = "phone" 2912 data.phone = email_or_phone 2913 } 2914 2915 if (!data.method) return 2916 2917 EasyStore.Event.dispatch('customers/signup', data) 2918 2919 } 2920 2921 function onLogin() { 2922 2923 const email_regex = new RegExp(/^[a-zA-Z0-9.!#$%&"*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/) 2924 const phone_regex = new RegExp(/^(\+?6?01)[0-46-9]-*[0-9]{7,8}$/) 2925 2926 const email_or_phone = _selector(`input[name="customer[email_or_phone]"]`).value.replace("+", "").replace(/\s+/, "") 2927 2928 let data = {} 2929 2930 if (email_regex.test(email_or_phone)) { 2931 data.method = 'email' 2932 data.email = email_or_phone 2933 } 2934 2935 if (phone_regex.test(email_or_phone)) { 2936 data.method = "phone" 2937 data.phone = email_or_phone 2938 } 2939 2940 if (!data.method) return 2941 2942 EasyStore.Event.dispatch('customers/login', data) 2943 2944 } 2945 2946 function onLineLogin() { 2947 2948 EasyStore.Event.dispatch('customers/login', { method: 'line' }) 2949 2950 } 2951 2952 function onLogout() { 2953 2954 EasyStore.Event.dispatch('customers/logout', { customer_id }) 2955 2956 } 2957 2958 function onBlogView() { 2959 2960 const blog = "" 2961 2962 EasyStore.Event.dispatch('blogs/viewed', { blog }) 2963 2964 } 2965 2966 function onArticleView() { 2967 2968 const article = "" 2969 2970 EasyStore.Event.dispatch('articles/viewed', { article }) 2971 2972 } 2973 2974 function onProductSearch() { 2975 2976 const query = _selector('input[name=q]').value 2977 2978 if (!query) return 2979 2980 EasyStore.Event.dispatch('products/searched', { query }) 2981 2982 } 2983 2984 function onCollectionView() { 2985 2986 const collection = "" 2987 2988 EasyStore.Event.dispatch('collections/viewed', { collection }) 2989 2990 } 2991 2992 function onProductView() { 2993 2994 const product = "" 2995 2996 EasyStore.Event.dispatch("products/viewed", { product }) 2997 2998 } 2999 3000 function onProductShare(el) { 3001 3002 const product = "" 3003 3004 const channel = el.getAttribute("class").split("-").last() 3005 3006 EasyStore.Event.dispatch('products/shared', { product, channel }) 3007 3008 } 3009 3010 function onWishlistItemAdded() { 3011 3012 let product = "" 3013 const quantity = _selector("#Quantity").value 3014 3015 EasyStore.Event.dispatch('wishlists/item_added', { product }) 3016 3017 } 3018 3019 async function onCartItemsAdded() { 3020 3021 const cart = await getCart() 3022 3023 if(cart && cart.items != undefined && cart.items.length > 0) { 3024 3025 EasyStore.Event.dispatch('carts/item_added', { cart }) 3026 3027 } 3028 3029 } 3030 3031 async function onCartView() { 3032 3033 const cart = await getCart() 3034 3035 EasyStore.Event.dispatch('carts/viewed', { cart }) 3036 3037 } 3038 3039 async function onCartItemRemoved() { 3040 3041 const cart = await getCart() 3042 3043 cart.items = [] 3044 3045 if(cart) { 3046 3047 EasyStore.Event.dispatch('carts/item_removed', { cart }) 3048 3049 } 3050 3051 } 3052 3053 async function onCheckoutInitiate() { 3054 3055 const cart = await getCart() 3056 3057 EasyStore.Event.dispatch('checkouts/initiated', { cart }) 3058 3059 } 3060 3061 async function onShippingInfoAdded(form) { 3062 let checkout = getCheckout() 3063 3064 let form_data = {} 3065 new FormData(form).forEach((value, key) => form_data[key] = value) 3066 3067 let shipping_tier = null 3068 3069 if (form_data.type && form_data.type.startsWith("r_pickup")) { 3070 shipping_tier = `Pickup - ${form_data.pick_location}` 3071 } 3072 3073 if (form_data.s_id) {
3074 shipping_tier = _selector(`input[id='`+form_data.s_id+`']`).getAttribute("data-shipping-name") 3075 } 3076 3077 if (!shipping_tier) return 3078 3079 EasyStore.Event.dispatch('checkouts/shipping_info_added', { checkout, shipping_tier }) 3080 3081 } 3082 3083 async function onPaymentInfoAdded(form) { 3084 3085 let checkout = getCheckout() 3086 3087 let payment_type = new FormData(form).get('payment_method') 3088 3089 EasyStore.Event.dispatch("checkouts/payment_info_added", { checkout, payment_type }) 3090 3091 } 3092 3093 async function onOrderPlace() { 3094 3095 let cart_token = getCookie("cart_js") 3096 let previous_cart_token = getCookie("previous_cart_ga4_js") 3097 3098 if (previous_cart_token && previous_cart_token == cart_token) { 3099 // Prevent duplicate purchase tracking 3100 return 3101 } 3102 3103 let order = getOrder() 3104 3105 EasyStore.Event.dispatch('orders/placed', { order }) 3106 3107 if(order.is_manual_payment) { 3108 3109 EasyStore.Event.dispatch('orders/purchased', { order }) 3110 3111 } 3112 3113 const last_transaction = order.transactions.last() 3114 3115 if (last_transaction.status) { 3116 3117 EasyStore.Event.dispatch('orders/purchased', { order }) 3118 EasyStore.Event.dispatch('payments/captured', { order }) 3119 3120 } 3121 3122 } 3123 3124 async function onPaymentFail() { 3125 3126 const order = getOrder() 3127 3128 EasyStore.Event.dispatch('payments/failed', { order }) 3129 3130 } 3131 3132 async function onCheckoutComplete(form) { 3133 3134 onPaymentInfoAdded(form) 3135 3136 const checkout = getCheckout() 3137 3138 EasyStore.Event.dispatch('checkouts/completed', { checkout }) 3139 3140 } 3141 3142 async function onSinglePageCheckout() { 3143 3144 // let checkout = $("[data-app-checkout]").data("app-checkout") 3145 3146 let checkout = getCheckout() 3147 3148 // const payment_type = _selector("#app_spc_payment_method").find(`[class*="label-content"]`).first().find("b").first().text() 3149 const payment_type = _selector("#app_spc_payment_method").getElementsByClassName('label-content')[0].getElementsByTagName('b')[0].innerHTML 3150 3151 const shipping_method = _selector("#delivery_method").value 3152 3153 let shipping_tier = null 3154 let app_spc_customer_info_label = _selector("#app_spc_customer_info").getElementsByClassName('label-content') 3155 if (shipping_method == "shipping") { 3156 shipping_tier = app_spc_customer_info_label[app_spc_customer_info_label.length - 1].getElementsByTagName('b')[0].innerHTML 3157 } 3158 3159 if (shipping_method == "pickup") { 3160 shipping_tier = app_spc_customer_info_label[0].innerHTML 3161 } 3162 3163 if (shipping_tier) { 3164 3165 EasyStore.Event.dispatch('checkouts/shipping_info_added', { 3166 checkout, 3167 shipping_tier, 3168 }) 3169 3170 } 3171 3172 if (payment_type) { 3173 3174 EasyStore.Event.dispatch('checkouts/payment_info_added', { 3175 checkout, 3176 payment_type, 3177 }) 3178 3179 } 3180 3181 EasyStore.Event.dispatch('checkouts/completed', { checkout }) 3182 3183 } 3184 3185 }) 3186 3187 3188 //-----------------------// 3189 // Functions // 3190 //-----------------------// 3191 3192 function getCookie(name) { 3193 3194 name += "=" 3195 3196 decodedCookie = decodeURIComponent(document.cookie) 3197 3198 ca = decodedCookie.split(";") 3199 3200 for(i = 0; i < ca.length; i++) { 3201 c = ca[i] 3202 while (c.charAt(0) == " ") { 3203 c = c.substring(1) 3204 } 3205 if (c.indexOf(name) == 0) { 3206 return c.substring(name.length, c.length) 3207 } 3208 } 3209 3210 return "" 3211 3212 } 3213 3214 const parsePrice = price => { 3215 if(typeof price === 'string') { 3216 return parseFloat(price.split(',').join('')) 3217 } 3218 3219 return price 3220 } 3221 3222 const requestCart = async (method, data) => { 3223 3224 let response = await fetch('/cart.json', {method: 'GET',headers: {'Content-Type': 'application/json'}}) 3225 result = await response.json() 3226 3227 return result.cart 3228 3229 } 3230 const getCart = async (latest = false) => { 3231 3232 let cart = window.__latest_cart 3233 3234 const cart_invalid = !cart || !cart.items || (cart.total_price > 0 && !cart.items.length) 3235 3236 if(latest || cart_invalid) { 3237 cart = await requestCart() 3238 } 3239 3240 cart.items = cart.items || [] 3241 3242 return mapCart(cart) 3243 3244 } 3245 3246 const getCheckout = async () => { 3247 mapCheckout(window.__latest_cart || await requestCart()) 3248 } 3249 3250 const getOrder = async () => { 3251 mapOrder(window.__latest_cart || await requestCart()) 3252 } 3253 3254 //-----------------------// 3255 // Mappers // 3256 //-----------------------// 3257 3258 const fallbackAttribute = (object, attribute) => { 3259 3260 if(!Array.isArray(attribute)) { 3261 attribute = [attribute] 3262 } 3263 3264 let final_value = null 3265 3266 do { 3267 3268 final_value = object[attribute.shift()] 3269 3270 } while(!final_value && attribute.length) 3271 3272 return final_value 3273 3274 } 3275 3276 const map = (object, mapper) => { 3277 3278 const newObject = {} 3279 3280 for(const key in mapper) { 3281 3282 // Handle different keys between new and old format
3283 newObject[key] = fallbackAttribute(object, mapper[key]) 3284 3285 // Cast price to float 3286 if(newObject[key] && ['price', 'amount', 'discount', 'discounts'].some(x => key.endsWith(x))) { 3287 newObject[key] = parsePrice(newObject[key]) 3288 } 3289 3290 if(newObject[key] === undefined) { 3291 delete newObject[key] 3292 } 3293 3294 } 3295 3296 return newObject 3297 3298 } 3299 3300 const mapCart = cart => { 3301 3302 const oldCart = cart 3303 3304 const mapper = { 3305 id: 'id', 3306 currency: 'currency', 3307 item_count: 'item_count', 3308 items: 'items', 3309 total_price: 'total_price', 3310 latest_items: 'latest_items' 3311 } 3312 3313 cart = map(cart, mapper) 3314 3315 const discounts = (oldCart.storewide_discounts || []).concat((oldCart.voucher_discounts || [])) 3316 3317 cart.discount_applications = discounts.map(discount => ({ 3318 title: discount.voucher_code || null, 3319 value: parsePrice(discount.amount), 3320 })) 3321 3322 cart.original_total_price = cart.items.length 3323 ? cart.items 3324 .map(item => item.original_price) 3325 .reduce((sum, price) => sum + price) 3326 : 0 3327 3328 cart.total_discount = cart.discount_applications.length 3329 ? cart.discount_applications 3330 .map(discount => parsePrice(discount.value)) 3331 .reduce((sum, value) => sum + value) 3332 : 0 3333 3334 cart.items = cart.items.map(item => mapLineItem(item)) 3335 3336 return cart 3337 3338 } 3339 3340 const mapCheckout = checkout => { 3341 3342 const mapper = { 3343 note: 'note', 3344 attributes: 'note_attributes', 3345 billing_address: 'billing_address', 3346 currency: 'currency', 3347 customer_id: 'customer_id', 3348 discounts_amount: 'total_discount', 3349 id: 'id', 3350 line_items: 'order_item', 3351 order_number: 'order_number', 3352 shipping_address: 'shipping_address', 3353 shipping_price: 'shipping_tax', 3354 shipping_method: 'shipping_method_name', 3355 tax_price: 'total_tax', 3356 } 3357 3358 checkout = map(checkout, mapper) 3359 3360 checkout.requires_shipping = checkout.line_items.some(item => item.shipping_required) 3361 3362 checkout.line_items = checkout.line_items.map(item => mapLineItem(item)) 3363 3364 return checkout 3365 3366 } 3367 3368 const mapOrder = order => { 3369 3370 const mapper = { 3371 attributes: 'note_attributes', 3372 billing_address: 'billing_address', 3373 cancelled: 'is_cancelled', 3374 cancelled_at: 'cancelled_at', 3375 created_at: 'created_at', 3376 customer_id: 'customer_id', 3377 // discount_applications: 'discount_applications', 3378 email: 'email', 3379 financial_status: 'financial_status', 3380 fulfillment_status: 'fulfillment_status', 3381 line_items: 'order_item', 3382 note: 'note', 3383 order_number: 'order_number', 3384 phone: 'phone', 3385 shipping_address: 'shipping_address', 3386 shipping_methods: 'shipping_methods', 3387 shipping_price: 'total_shipping', 3388 subtotal_price: 'subtotal_price', 3389 // tax_lines: 'tax_lines', 3390 tax_price: 'total_tax', 3391 total_discounts: 'total_discount', 3392 total_net_amount: 'total_amount_include_transaction', 3393 total_price: 'total_price', 3394 transactions: 'transaction_records', 3395 is_manual_payment: 'is_manual_payment', 3396 } 3397 3398 order.email = order.billing_address.email 3399 order.phone = order.billing_address.phone 3400 order.shipping_method = order.shipping_method_name 3401 3402 order = map(order, mapper) 3403 3404 order.line_items = order.line_items.map(item => mapLineItem(item)) 3405 order.transactions = order.transactions.map(transaction => mapTransaction(transaction)) 3406 3407 return order 3408 3409 } 3410 3411 const mapLineItem = line_item => { 3412 3413 const old_line_item = line_item 3414 3415 const mapper = { 3416 final_price: 'price', 3417 image: 'img_url', 3418 message: 'message', 3419 original_line_price: 'original_line_price', 3420 original_price: 'original_price', 3421 properties: 'properties', 3422 quantity: 'quantity', 3423 requires_shipping: 'shipping_required', 3424 sku: 'sku', 3425 taxable: 'taxable', 3426 title: 'product_name', 3427 product_name: 'product_name', 3428 url: 'url', 3429 product_id: 'product_id', 3430 variant_id: 'variant_id', 3431 id: ['id', 'i_id'], 3432 } 3433 3434 line_item = map(line_item, mapper) 3435 3436 line_item.product_id = line_item.product_id || (old_line_item.product && old_line_item.product.id) || null 3437 line_item.variant_id = line_item.variant_id || (old_line_item.variant && old_line_item.variant.id) || null 3438 line_item.image = line_item.image || (old_line_item.image && old_line_item.image.url) || null 3439 3440 return line_item 3441 3442 } 3443 3444 const mapTransaction = transaction => { 3445 3446 const mapper = { 3447 amount: 'amount', 3448 created_at: 'created_at', 3449 gateway: 'gateway_type', 3450 id: 'id', 3451 status: 'status', 3452 } 3453 3454 transaction = map(transaction, mapper) 3455 3456 return transaction 3457 3458 } 3459 3460 3461</script>
3461 3462 3463</body> 3464</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.