Guest User

Untitled

a guest
May 29th, 2024
73
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 80.08 KB | None | 0 0
  1. // https://minify-js.com/
  2. import { $4, $$4, $id4, isRTL, formatMoney, debounce, getterAdd, getterGet, getterRunFn, btnTooltip, Drawer, VariantChangeBase } from 'global';
  3.  
  4. // WISHLIST
  5. var $wishlist_list = $id4('hdt_wishlist_list'),
  6. $compare_list = $id4('hdt_compare_list'),
  7. nameCachedWishlist = 'theme4:wishlist:id',
  8. nameCachedCompare = 'theme4:compare:id';
  9. // if exit $wishlist_list is use app wishlist the4
  10. if ($wishlist_list) {
  11. var arr_wishlist_list = $wishlist_list.textContent ? $wishlist_list.textContent.split(' ') : [];
  12. }
  13. else {
  14. var arr_wishlist_list = (!localStorage.getItem(nameCachedWishlist)) ? [] : localStorage.getItem(nameCachedWishlist).split(','); // remove id: and conver array
  15. }
  16. if ($compare_list){
  17. var arr_compare_list = $compare_list.textContent ? $compare_list.textContent.split(' ') : [];
  18. }
  19. else {
  20. var arr_compare_list = (!localStorage.getItem(nameCachedCompare)) ? [] : localStorage.getItem(nameCachedCompare).split(','); // remove id: and conver array
  21. }
  22. // arr_wishlist_list = [1234, 5678, 9011]
  23. // arr_compare_list = [1234, 5678, 9011]
  24.  
  25. var linkWishlistApp = '/apps/ecomrise/wishlist',
  26. linkCompareApp = '/apps/ecomrise/compare',
  27. actionAfterWishlistAdded = themeHDN.extras.AddedWishlistRemove ? 'remove' : 'added',
  28. actionAfterCompareAdded = themeHDN.extras.AddedCompareRemove ? 'remove' : 'added',
  29. limitWishlist = $wishlist_list ? 100 : 50,
  30. limitCompare = 6,
  31. conver_to_link_fn = function (prefix = this.textFn, array = this.array) {
  32. const x = themeHDN.routes.search_url + `/?view=${prefix}`,
  33. y = x + '&type=product&options[unavailable_products]=last&q=';
  34. return (array.length) ? y + encodeURI(`id:${array.join(' OR id:')}`) : x;
  35. };
  36. // Reset if limit change
  37. if (arr_wishlist_list.length > limitWishlist) {
  38. arr_wishlist_list.splice(limitWishlist - 1, arr_wishlist_list.length - 1);
  39. localStorage.setItem(nameCachedWishlist, arr_wishlist_list.toString());
  40. }
  41. // Check is page has item but not show reload page
  42. if (window.isPageWishlist) {
  43. if (arr_wishlist_list.length && !window.isWishlistPerformed) {
  44. window.location.href = conver_to_link_fn('wishlist', arr_wishlist_list)
  45. } else {
  46. window.history.replaceState({}, document.title, conver_to_link_fn('wishlist', []));
  47. }
  48. }
  49.  
  50. // Reset if limit change
  51. if (arr_compare_list.length > limitCompare) {
  52. arr_compare_list.splice(limitCompare - 1, arr_compare_list.length - 1);
  53. localStorage.setItem(nameCachedCompare, arr_compare_list.toString());
  54. }
  55. // Check is page has item but not show reload page
  56. if (window.isPageCompare) {
  57. if (arr_compare_list.length && !window.isComparePerformed) {
  58. window.location.href = conver_to_link_fn('compare', arr_compare_list)
  59. } else {
  60. window.history.replaceState({}, document.title, conver_to_link_fn('compare', []));
  61. }
  62. }
  63. var _wishlist_id, _wishlist_handle,
  64. _update_wis_text, update_wis_text_fn,
  65. _update_wis_btns, update_wis_btns_fn,
  66. _click_wis, click_wis_fn,
  67. _add_wis, add_wis_fn,
  68. _remove_wis, remove_wis_fn,
  69. _action_after_remove_add, action_after_remove_add_fn,
  70. _show_popup_compare, show_popup_compare_fn,
  71. _conver_to_link;
  72. class Wishlist extends btnTooltip {
  73. constructor() {
  74. super();
  75. getterAdd(_wishlist_id, this, this.dataset.id);
  76. getterAdd(_wishlist_handle, this, this.dataset.handle);
  77. getterAdd(_update_wis_text, this);
  78. getterAdd(_update_wis_btns, this);
  79. getterAdd(_click_wis, this);
  80. getterAdd(_add_wis, this);
  81. getterAdd(_remove_wis, this);
  82. getterAdd(_action_after_remove_add, this);
  83. getterAdd(_show_popup_compare, this);
  84. getterAdd(_conver_to_link, this);
  85.  
  86. this.addEventListener("click", () => getterRunFn(_click_wis, this, click_wis_fn).call(this));
  87. }
  88. static get observedAttributes() {
  89. return ["action"];
  90. }
  91. get isFnWishlist() {
  92. return true;
  93. }
  94. get textFn() {
  95. return 'wishlist';
  96. }
  97. get isPageWishlistorCompare() {
  98. //add code check page
  99. return this.isFnWishlist ? window.isPageWishlist : window.isPageCompare;
  100. }
  101. get limit() {
  102. return this.isFnWishlist ? limitWishlist : limitCompare;
  103. }
  104. get nameCached() {
  105. return this.isFnWishlist ? nameCachedWishlist : nameCachedCompare;
  106. }
  107. get array() {
  108. return this.isFnWishlist ? arr_wishlist_list : arr_compare_list;
  109. }
  110. get actionAfterAdded() {
  111. return this.isFnWishlist ? actionAfterWishlistAdded : actionAfterCompareAdded;
  112. }
  113. get action() {
  114. return this.getAttribute('action');
  115. }
  116. attributeChangedCallback(name, oldValue, newValue) {
  117. if (oldValue === newValue || (oldValue === null && newValue === 'add')) return;
  118. getterRunFn(_update_wis_text, this, update_wis_text_fn).call(this, newValue);
  119. super.attributeChangedCallback(name, oldValue, newValue);
  120. }
  121. connectedCallback() {
  122. super.connectedCallback();
  123. if (this.array.indexOf(getterGet(_wishlist_id, this)) > -1) {
  124. if (this.hasAttribute('remove-on-page')) {
  125. return
  126. }
  127. this.setAttribute('action', this.actionAfterAdded);
  128. }
  129. }
  130. };
  131. _wishlist_id = new WeakMap();
  132. _wishlist_handle = new WeakMap();
  133. _update_wis_text = new WeakSet();
  134. _update_wis_btns = new WeakSet();
  135. _click_wis = new WeakSet();
  136. _add_wis = new WeakSet();
  137. _remove_wis = new WeakSet();
  138. _action_after_remove_add = new WeakSet();
  139. _conver_to_link = new WeakSet();
  140. _show_popup_compare = new WeakSet();
  141. update_wis_text_fn = function (value) {
  142. if (this.hasAttribute('remove-on-page')) return;
  143. const text = themeHDN.extras[`text_${this.isFnWishlist ? 'wis' : 'cp'}_${value}`],
  144. icon = themeHDN.extras[`icon_${this.isFnWishlist ? 'wis' : 'cp'}_${value}`];
  145. if (icon) this.firstElementChild.replaceWith(document.createRange().createContextualFragment(icon));
  146. if (text) this.lastElementChild.textContent = text;
  147. };
  148. update_wis_btns_fn = function (_action) {
  149. // Update Button
  150. $$4(`[is="hdt-${this.textFn}"][data-id="${getterGet(_wishlist_id, this)}"]`).forEach(btn => {
  151. btn.setAttribute('action', _action);
  152. });
  153. // Update Count and link
  154. document.dispatchEvent(new CustomEvent(`theme4:${this.textFn}:update`, {
  155. bubbles: true,
  156. detail: 'the4' // arr_wishlist_list
  157. }));
  158. };
  159. click_wis_fn = function (e) {
  160.  
  161. if (this.action == 'add') {
  162. // ADD
  163. getterRunFn(_add_wis, this, add_wis_fn).call(this);
  164. } else if (this.action === 'added') {
  165. // ADDED: go to page wishlist
  166. if (!this.isFnWishlist && themeHDN.extras.enableComparePopup) {
  167. getterRunFn(_show_popup_compare, this, show_popup_compare_fn).call(this);
  168. } else {
  169. window.location.href = getterRunFn(_conver_to_link, this, conver_to_link_fn).call(this);
  170. }
  171. } else {
  172. // REMOVE
  173. getterRunFn(_remove_wis, this, remove_wis_fn).call(this);
  174. }
  175. };
  176. add_wis_fn = function (e) {
  177. if ($wishlist_list && this.isFnWishlist || $compare_list && !this.isFnWishlist) {
  178. // app wishlist
  179. this.setAttribute('aria-busy', true);
  180. fetch(this.isFnWishlist ? linkWishlistApp : linkCompareApp, {
  181. method: 'POST',
  182. headers: {
  183. 'Content-Type': 'application/json'
  184. },
  185. body: JSON.stringify({
  186. product_id: getterGet(_wishlist_id, this),
  187. product_handle: getterGet(_wishlist_handle, this),
  188. action: 'add'
  189. })
  190. })
  191. .then((response) => {
  192. return response.json()
  193. })
  194. .then((data) => {
  195. if (data.status != 'success') {
  196. console.error(data.message || 'Unknow error');
  197. return;
  198. }
  199. if(this.isFnWishlist) {
  200. arr_wishlist_list = JSON.parse(data.response.metafield.value).ecomrise_ids;
  201. if (!Array.isArray(arr_wishlist_list)) {
  202. arr_wishlist_list = arr_wishlist_list.split(',');
  203. }
  204. }
  205. if (!this.isFnWishlist) {
  206. arr_compare_list = JSON.parse(data.response.metafield.value).ecomrise_ids;
  207. if (!Array.isArray(arr_compare_list)) {
  208. arr_compare_list = arr_compare_list.split(',');
  209. }
  210. getterRunFn(_show_popup_compare, this, show_popup_compare_fn).call(this);
  211. }
  212. getterRunFn(_action_after_remove_add, this, action_after_remove_add_fn).call(this, this.actionAfterAdded);
  213. })
  214. .catch(function (error) {
  215. console.log('Error: ' + error);
  216. })
  217. .finally(() => {
  218. this.setAttribute('aria-busy', false);
  219. });
  220. }
  221. else {
  222. // local wishlist
  223. // adds the specified elements to the beginning of an array
  224. this.array.unshift(getterGet(_wishlist_id, this));
  225. //console.log(this.array, getterGet(_wishlist_id, this))
  226. if (this.array.length > this.limit) {
  227. // if over limit remove element on last array
  228. let arraySplice = this.array.splice(this.limit, 1);
  229. // arraySplice: allway has 1 element
  230. $$4(`[is="hdt-${this.textFn}"][data-id="${arraySplice[0]}"]`).forEach(btn => {
  231. btn.setAttribute('action', 'add');
  232. });
  233. }
  234. if (!this.isFnWishlist) getterRunFn(_show_popup_compare, this, show_popup_compare_fn).call(this);
  235. getterRunFn(_action_after_remove_add, this, action_after_remove_add_fn).call(this, this.actionAfterAdded);
  236. localStorage.setItem(this.nameCached, this.array.toString());
  237. }
  238. };
  239. remove_wis_fn = function (e) {
  240. // REMOVE
  241. if ($wishlist_list && this.isFnWishlist || $compare_list && !this.isFnWishlist) {
  242. // app wishlist
  243. this.setAttribute('aria-busy', true);
  244. fetch(this.isFnWishlist ? linkWishlistApp : linkCompareApp, {
  245. method: 'DELETE',
  246. headers: {
  247. 'Content-Type': 'application/json'
  248. },
  249. body: JSON.stringify({
  250. product_id: getterGet(_wishlist_id, this),
  251. product_handle: getterGet(_wishlist_handle, this),
  252. action: 'add',
  253. _method: 'DELETE'
  254. })
  255. })
  256. .then((response) => {
  257. return response.json()
  258. })
  259. .then((data) => {
  260. if (data.status != 'success') {
  261. console.error(data.message || 'Unknow error');
  262. return;
  263. }
  264. arr_wishlist_list = JSON.parse(data.response.metafield.value).ecomrise_ids;
  265. if (!Array.isArray(arr_wishlist_list)) {
  266. arr_wishlist_list = arr_wishlist_list.split(',');
  267. }
  268. getterRunFn(_action_after_remove_add, this, action_after_remove_add_fn).call(this, 'add');
  269. })
  270. .catch(function (error) {
  271. console.log('Error: ' + error);
  272. })
  273. .finally(() => {
  274. this.setAttribute('aria-busy', false);
  275. });
  276. } else {
  277. // local wishlist
  278. this.array.splice(this.array.indexOf(getterGet(_wishlist_id, this)), 1);
  279. localStorage.setItem(this.nameCached, this.array.toString());
  280.  
  281. getterRunFn(_action_after_remove_add, this, action_after_remove_add_fn).call(this, 'add');
  282. }
  283. };
  284. action_after_remove_add_fn = function (action) {
  285.  
  286. if (this.getAttribute('action') == 'remove' && this.hasAttribute('remove-on-page')) {
  287. if (this.isFnWishlist) {
  288. this.closest('.hdt-card-product').remove();
  289. } else {
  290. $$4(`.hdt-compare-item-${getterGet(_wishlist_id, this)}`).forEach(item => {
  291. item.remove();
  292. });
  293. }
  294. getterRunFn(_update_wis_btns, this, update_wis_btns_fn).call(this, action);
  295. if (this.array.length == 0) {
  296. $id4(`drawerCompare`)?.removeAttribute('open');
  297. if (this.isPageWishlistorCompare) window.location.href = getterRunFn(_conver_to_link, this, conver_to_link_fn).call(this);
  298. }
  299. //this.setAttribute('action', action);
  300. } else {
  301. getterRunFn(_update_wis_btns, this, update_wis_btns_fn).call(this, action);
  302. // is page wishlist or compare reload page
  303. if (this.isPageWishlistorCompare) window.location.href = getterRunFn(_conver_to_link, this, conver_to_link_fn).call(this);
  304. }
  305. };
  306. show_popup_compare_fn = function (event) {
  307. if (!themeHDN.extras.enableComparePopup || this.isPageWishlistorCompare) return;
  308. // add code if want show popup compare
  309. $id4(`drawerCompare`).setAttribute('open', '');
  310. fetch(conver_to_link_fn('compare', arr_compare_list) + "&section_id=compare-offcanvas")
  311. .then((response) => response.text())
  312. .then((text) => {
  313. const html = document.createElement('div');
  314. html.innerHTML = text;
  315. const prds = html.querySelector('offcanvas-compare');
  316.  
  317. if (prds && prds.innerHTML.trim().length) {
  318.  
  319. $4("offcanvas-compare").innerHTML = prds.innerHTML;
  320. }
  321. })
  322. .catch((e) => {
  323. console.error(e);
  324. });
  325. };
  326. var _clear_all, clear_all_fn;
  327. class ClearAll extends HTMLButtonElement {
  328. constructor() {
  329. super();
  330. getterAdd(_clear_all, this);
  331. this.addEventListener("click", getterRunFn(_clear_all, this, clear_all_fn));
  332. }
  333. get ofFn() {
  334. return this.getAttribute('of-fn'); //'compare', 'wishlist'
  335. }
  336. get nameCached() {
  337. return this.ofFn == 'compare' ? nameCachedCompare : nameCachedWishlist;
  338. }
  339. set array(value) {
  340. this.ofFn == 'compare' ? arr_compare_list = value : arr_wishlist_list = value;
  341. }
  342. };
  343. _clear_all = new WeakSet();
  344. clear_all_fn = function () {
  345. localStorage.removeItem(this.nameCached);
  346. // Update Buttons
  347. $$4(`[is="hdt-${this.ofFn}"][action="added"]`).forEach(btn => {
  348. btn.setAttribute('action', 'add');
  349. });
  350. // Update Count and link
  351. document.dispatchEvent(new CustomEvent(`theme4:${this.ofFn}:update`, {
  352. bubbles: true,
  353. detail: 'the4'
  354. }));
  355. this.array = [];
  356. $id4(`drawerCompare`)?.removeAttribute('open');
  357. if ($4(`#drawerCompare offcanvas-compare`)){
  358. $4(`#drawerCompare offcanvas-compare`).innerHTML = "";
  359. }
  360. };
  361. customElements.define("hdt-clear-all", ClearAll, { extends: "button" });
  362. // eg: <button is="hdt-clear-all" of-fn="compare">Clear All </button>
  363.  
  364. // Update count
  365. class WishlistCount extends HTMLElement {
  366. constructor() {
  367. super();
  368. this.textContent = this.array.length;
  369. document.addEventListener(`theme4:${this.prefix}:update`, (e) => {
  370. //console.log(this.prefix + ' count update.');
  371. this.textContent = this.array.length;
  372. });
  373. }
  374. get isFnWishlist() {
  375. return true;
  376. }
  377. get array() {
  378. return this.isFnWishlist ? arr_wishlist_list : arr_compare_list;
  379. }
  380. get prefix() {
  381. return this.isFnWishlist ? 'wishlist' : 'compare';
  382. }
  383. }
  384. // Update link page
  385. class WishlistLink extends HTMLAnchorElement {
  386. constructor() {
  387. super();
  388. this.href = conver_to_link_fn(this.prefix, this.array);
  389. //console.log('WishlistLink', this.href)
  390. document.addEventListener(`theme4:${this.prefix}:update`, (e) => {
  391. this.href = conver_to_link_fn(this.prefix, this.array);
  392. });
  393. }
  394. get isFnWishlist() {
  395. return true;
  396. }
  397. get array() {
  398. return this.isFnWishlist ? arr_wishlist_list : arr_compare_list;
  399. }
  400. get prefix() {
  401. return this.isFnWishlist ? 'wishlist' : 'compare';
  402. }
  403. }
  404. customElements.define("hdt-wishlist", Wishlist, { extends: "button" });
  405. customElements.define("hdt-wishlist-count", WishlistCount);
  406. customElements.define("hdt-wishlist-a", WishlistLink, { extends: "a" });
  407.  
  408. // COMPARE
  409. class Compare extends Wishlist {
  410. get isFnWishlist() {
  411. return false;
  412. }
  413. get textFn() {
  414. return 'compare';
  415. }
  416. }
  417. // Update count
  418. class CompareCount extends WishlistCount {
  419. get isFnWishlist() {
  420. return false;
  421. }
  422. }
  423. // Update link page
  424. class CompareLink extends WishlistLink {
  425. get isFnWishlist() {
  426. return false;
  427. }
  428. }
  429. customElements.define("hdt-compare", Compare, { extends: "button" });
  430. customElements.define("hdt-compare-count", CompareCount);
  431. customElements.define("hdt-compare-a", CompareLink, { extends: "a" });
  432.  
  433. // --------------------------
  434. // Product Recommendations
  435. // --------------------------
  436. class ProductRecommendations extends HTMLElement {
  437. constructor() {
  438. super();
  439. }
  440.  
  441. connectedCallback() {
  442. const handleIntersection = (entries, observer) => {
  443. if (!entries[0].isIntersecting) return;
  444. observer.unobserve(this);
  445.  
  446. fetch(this.dataset.url)
  447. .then((response) => response.text())
  448. .then((text) => {
  449. const html = document.createElement('div');
  450. html.innerHTML = text;
  451. const recommendations = html.querySelector('product-recommendations');
  452.  
  453. if (recommendations && recommendations.innerHTML.trim().length) {
  454. this.innerHTML = recommendations.innerHTML;
  455. }
  456. document.dispatchEvent(new CustomEvent("currency:update"));
  457. })
  458. .catch((e) => {
  459. console.error(e);
  460. });
  461. };
  462.  
  463. new IntersectionObserver(handleIntersection.bind(this), { rootMargin: '0px 0px 400px 0px' }).observe(this);
  464. }
  465. }
  466.  
  467. customElements.define('product-recommendations', ProductRecommendations);
  468.  
  469.  
  470. // --------------------------
  471. // Product Recently
  472. // --------------------------
  473. class ProductRecently extends HTMLElement {
  474. constructor() {
  475. super();
  476. }
  477.  
  478. connectedCallback() {
  479. const handleIntersection = (entries, observer) => {
  480. if (!entries[0].isIntersecting) return;
  481. observer.unobserve(this);
  482. let handleProducts = localStorage.getItem("theme4:recently:id");
  483. let prdId = this.dataset.id;
  484.  
  485. if (handleProducts !== null) {
  486. let products = handleProducts.split(',');
  487. let url = this.dataset.url.replace('q=', 'q=id:' + products.join("%20OR%20id:"));
  488. fetch(url)
  489. .then((response) => response.text())
  490. .then((text) => {
  491. const html = document.createElement('div');
  492. html.innerHTML = text;
  493. const recently = html.querySelector('product-recently');
  494.  
  495. if (recently && recently.innerHTML.trim().length) {
  496. this.innerHTML = recently.innerHTML;
  497. }
  498. document.dispatchEvent(new CustomEvent("currency:update"));
  499. })
  500. .catch((e) => {
  501. console.error(e);
  502. });
  503.  
  504. }
  505. let arrayProducts = handleProducts !== null ? handleProducts.split(',') : new Array;
  506. if (!arrayProducts.includes(prdId + '')) {
  507. if (arrayProducts.length >= 10) {
  508. arrayProducts.pop();
  509. }
  510. function prepend(value, array) {
  511. var newArray = array.slice();
  512. newArray.unshift(value);
  513. return newArray;
  514. }
  515. arrayProducts = prepend(prdId, arrayProducts);
  516. arrayProducts = arrayProducts.toString();
  517. localStorage.setItem("theme4:recently:id", arrayProducts);
  518. }
  519. };
  520.  
  521. new IntersectionObserver(handleIntersection.bind(this), { rootMargin: '0px 0px 400px 0px' }).observe(this);
  522. }
  523. }
  524.  
  525. customElements.define('product-recently', ProductRecently);
  526.  
  527.  
  528. // --------------------------
  529. // Predictive Search
  530. // --------------------------
  531. class PredictiveSearch extends Drawer {
  532. constructor() {
  533. super();
  534. // console.log(!this.hasAttribute('enabled'))
  535. if (!this.hasAttribute('enabled')) return;
  536. this.cachedResults = {};
  537. this.predictiveSearchResults = $4('[data-results-search]', this);
  538. this.input = $4('input[type="search"]', this);
  539. this.sectionIdResults = this.getAttribute('data-section-id-results') || 'hdt_predictive-search';
  540. this.allPredictiveSearchInstances = $$4('predictive-search');
  541. this.skeleton = $4('[data-skeleton-search]', this);
  542. this.noResults = $4('.hdt-cart-no-results', this);
  543. this.hideWrapper = $4('.hdt-cart-hide-has-results', this);
  544. this.showWrapper = $4('.hdt-cart-show-has-results', this);
  545. this.btn_viewall = $4('.hdt-view_all', this);
  546. this.href = this.btn_viewall?.getAttribute("href");
  547. this.isOpen = false;
  548. this.abortController2 = new AbortController();
  549. this.searchTerm = '';
  550. if (this.input) {
  551. this.input.addEventListener(
  552. 'input',
  553. debounce((event) => {
  554. this.onChange(event);
  555. }, 300).bind(this)
  556. );
  557. }
  558.  
  559. const formData = new FormData(this.input.form);
  560. this.params = "";
  561. for (const [key, value] of formData) {
  562. if (key === 'q') break;
  563. this.params += `${key}=${value}&`;
  564. }
  565.  
  566. this.setupEventListeners();
  567. }
  568.  
  569. setupEventListeners() {
  570. this.input.form.addEventListener('submit', this.onFormSubmit.bind(this));
  571.  
  572. this.input.addEventListener('focus', this.onFocus.bind(this));
  573. this.addEventListener('focusout', this.onFocusOut.bind(this));
  574. this.addEventListener('keyup', this.onKeyup.bind(this));
  575. //this.addEventListener('keydown', this.onKeydown.bind(this));
  576. }
  577.  
  578. getQuery() {
  579. return this.input.value.trim();
  580. }
  581.  
  582. onChange() {
  583. const newSearchTerm = this.getQuery();
  584. if (!this.searchTerm || !newSearchTerm.startsWith(this.searchTerm)) {
  585. // Remove the results when they are no longer relevant for the new search term
  586. // so they don't show up when the dropdown opens again
  587. // this.querySelector('#predictive-search-results-groups-wrapper')?.remove();
  588. }
  589.  
  590. // Update the term asap, don't wait for the predictive search query to finish loading
  591. this.updateSearchForTerm(this.searchTerm, newSearchTerm);
  592.  
  593. this.searchTerm = newSearchTerm;
  594. this.btn_viewall?.setAttribute("href", this.href.replace(/\/search\?q=.*/g, "/search?q=" + this.searchTerm));
  595.  
  596. if (!this.searchTerm.length) {
  597. this.close(true);
  598. return;
  599. }
  600. this.getSearchResults(this.searchTerm);
  601. }
  602.  
  603. onFormSubmit(event) {
  604. if (!this.getQuery().length || this.querySelector('[aria-selected="true"] a')) event.preventDefault();
  605. }
  606.  
  607. onFocus() {
  608. const currentSearchTerm = this.getQuery();
  609. if (!currentSearchTerm.length) return;
  610. if (this.searchTerm !== currentSearchTerm) {
  611. // Search term was changed from other search input, treat it as a user change
  612. this.onChange();
  613. } else if (this.getAttribute('results') === 'true') {
  614. this.openSearch();
  615. } else {
  616. this.getSearchResults(this.searchTerm);
  617. }
  618. }
  619.  
  620. onFocusOut() {
  621. setTimeout(() => {
  622. if (!this.contains(document.activeElement)) this.close();
  623. });
  624. }
  625.  
  626. onKeyup(event) {
  627. if (!this.getQuery().length) this.close(true);
  628. event.preventDefault();
  629.  
  630. // switch (event.code) {
  631. // case 'ArrowUp':
  632. // this.switchOption('up');
  633. // break;
  634. // case 'ArrowDown':
  635. // this.switchOption('down');
  636. // break;
  637. // case 'Enter':
  638. // this.selectOption();
  639. // break;
  640. // }
  641. }
  642.  
  643. // onKeydown(event) {
  644. // // Prevent the cursor from moving in the input when using the up and down arrow keys
  645. // if (event.code === 'ArrowUp' || event.code === 'ArrowDown') {
  646. // event.preventDefault();
  647. // }
  648. // }
  649. updateSearchForTerm(previousTerm, newTerm) {
  650. const searchForTextElement = this.querySelector('[data-predictive-search-search-for-text]');
  651. const currentButtonText = searchForTextElement?.innerText;
  652. if (currentButtonText) {
  653. if (currentButtonText.match(new RegExp(previousTerm, 'g')).length > 1) {
  654. // The new term matches part of the button text and not just the search term, do not replace to avoid mistakes
  655. return;
  656. }
  657. const newButtonText = currentButtonText.replace(previousTerm, newTerm);
  658. searchForTextElement.innerText = newButtonText;
  659. }
  660. }
  661.  
  662. getSearchResults(searchTerm) {
  663. const queryKey = searchTerm.replace(' ', '-').toLowerCase();
  664. this.setLiveRegionLoadingState();
  665.  
  666. if (this.cachedResults[queryKey]) {
  667. this.renderSearchResults(this.cachedResults[queryKey]);
  668. return;
  669. }
  670.  
  671. fetch(`${themeHDN.routes.predictive_search_url}?q=${encodeURIComponent(searchTerm)}&${this.params}section_id=${this.sectionIdResults}`, {
  672. signal: this.abortController2.signal,
  673. })
  674. .then((response) => {
  675. if (!response.ok) {
  676. var error = new Error(response.status);
  677. this.close();
  678. throw error;
  679. }
  680.  
  681. return response.text();
  682. })
  683. .then((text) => {
  684. const resultsMarkup = new DOMParser()
  685. .parseFromString(text, 'text/html')
  686. .querySelector('#shopify-section-' + this.sectionIdResults).innerHTML;
  687. // Save bandwidth keeping the cache in all instances synced
  688. this.allPredictiveSearchInstances.forEach((predictiveSearchInstance) => {
  689. this.cachedResults[queryKey] = resultsMarkup;
  690. });
  691. this.renderSearchResults(resultsMarkup);
  692. })
  693. .catch((error) => {
  694. if (error?.code === 20) {
  695. // Code 20 means the call was aborted
  696. return;
  697. }
  698. this.close();
  699. throw error;
  700. });
  701. }
  702.  
  703. setLiveRegionLoadingState() {
  704. this.hideWrapper.style.cssText = "display: none;";
  705. this.showWrapper.style.cssText = "display: block;";
  706. this.skeleton.classList.remove("hdt-hidden");
  707.  
  708. this.setAttribute('loading', true);
  709. }
  710.  
  711. renderSearchResults(resultsMarkup) {
  712. this.predictiveSearchResults.innerHTML = resultsMarkup;
  713. this.setAttribute('results', true);
  714.  
  715. this.setLiveRegionResults();
  716. this.openSearch();
  717. document.dispatchEvent(new CustomEvent("currency:update"));
  718. }
  719.  
  720. setLiveRegionResults() {
  721. this.removeAttribute('loading');
  722. }
  723.  
  724. openSearch() {
  725. //this.setAttribute('open', true);
  726. this.input.setAttribute('aria-expanded', true);
  727. this.skeleton.classList.add("hdt-hidden");
  728. this.isOpen = true;
  729. }
  730.  
  731. close(clearSearchTerm = false) {
  732. this.closeResults(clearSearchTerm);
  733. this.isOpen = false;
  734. }
  735.  
  736. closeResults(clearSearchTerm = false) {
  737. if (clearSearchTerm) {
  738. this.input.value = '';
  739. this.removeAttribute('results');
  740. this.hideWrapper.style.cssText = "display: block;";
  741. this.showWrapper.style.cssText = "display: none;";
  742. this.predictiveSearchResults.innerHTML = "";
  743. }
  744. const selected = this.querySelector('[aria-selected="true"]');
  745.  
  746. if (selected) selected.setAttribute('aria-selected', false);
  747.  
  748. this.input.setAttribute('aria-activedescendant', '');
  749. this.removeAttribute('loading');
  750. //this.removeAttribute('open');
  751. this.input.setAttribute('aria-expanded', false);
  752. this.resultsMaxHeight = false;
  753. this.predictiveSearchResults.removeAttribute('style');
  754. }
  755. }
  756.  
  757. customElements.define('hdt-predictive-search', PredictiveSearch);
  758. // bundle product
  759.  
  760. const bundleProduct = () => {
  761. if (window.innerWidth < 768) {
  762. return;
  763. }
  764. let arrBundle = $$4("[data-bundle-pin]");
  765. if (!arrBundle) {
  766. return;
  767. }
  768. arrBundle.forEach((el) => {
  769. if (!el.dataset.trigger) {
  770. return;
  771. } else {
  772. el.onmouseover = () => {
  773. $4(el.dataset.trigger).classList.add("is--hover");
  774. $4(el.dataset.trigger)
  775. .closest("[data-bundle-parent]")
  776. .classList.add("has--hover");
  777. };
  778. el.onmouseleave = () => {
  779. $4(el.dataset.trigger).classList.remove("is--hover");
  780. $4(el.dataset.trigger)
  781. .closest("[data-bundle-parent]")
  782. .classList.remove("has--hover");
  783. };
  784. }
  785. });
  786. };
  787. bundleProduct();
  788.  
  789. // --------------------------
  790. // Back to top
  791. // --------------------------
  792. class BackToTop extends HTMLElement {
  793. constructor() {
  794. super();
  795. this.config = JSON.parse(this.getAttribute('config'));
  796. if (window.innerWidth < 768 && this.config.hiddenMobile) return;
  797. this.debounce_timer = 0;
  798. this.debounce_timer2 = 0;
  799. this.circlechart = this.querySelector('.hdt-circle-css');
  800. this.addEventListener("click", this.goTop.bind(this));
  801. window.addEventListener("scroll", this.backToTop.bind(this));
  802. }
  803. goTop() {
  804. document.body.scrollTop = 0; // For Safari
  805. document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera
  806. }
  807.  
  808. backToTop() {
  809. let self = this;
  810. if (this.debounce_timer) { clearTimeout(this.debounce_timer); }
  811. this.debounce_timer = setTimeout(function () {
  812. if (window.scrollY > self.config.scrollTop) {
  813. self.classList.add('is--show');
  814. } else {
  815. self.classList.remove('is--show');
  816. }
  817. }, 40);
  818.  
  819. if (!this.circlechart) return;
  820.  
  821. if (this.debounce_timer2) { clearTimeout(this.debounce_timer2); }
  822.  
  823. this.debounce_timer2 = setTimeout(function () {
  824. let scrollTop2 = window.scrollY,
  825. docHeight = document.body.offsetHeight,
  826. winHeight = window.innerHeight,
  827. scrollPercent = scrollTop2 / (docHeight - winHeight),
  828. degrees = scrollPercent * 360;
  829. self.circlechart.style.setProperty("--cricle-degrees", degrees + 'deg');
  830. }, 6);
  831. }
  832. }
  833.  
  834. customElements.define('back-to-top', BackToTop);
  835.  
  836.  
  837. // --------------------------
  838. // numberRandom
  839. // --------------------------
  840. class numberRandom extends HTMLElement {
  841. constructor() {
  842. super();
  843. let config = JSON.parse(this.getAttribute("config"));
  844. this.min = config.min;
  845. this.max = config.max;
  846. this.interval = config.interval;
  847. this.number = Math.floor(Math.random() * (this.max - this.min + 1)) + this.min;
  848. this.ofset = ["1", "2", "4", "3", "6", "10", "-1", "-3", "-2", "-4", "-6"];
  849. this.prioritize = ["10", "20", "15"];
  850. this.h = "";
  851. this.e = "";
  852. this.M = "";
  853. this.liveViewInt();
  854. setInterval(this.liveViewInt.bind(this), this.interval);
  855. }
  856.  
  857. liveViewInt() {
  858. if (this.h = Math.floor(Math.random() * this.ofset.length), this.e = this.ofset[this.h], this.number = parseInt(this.number) + parseInt(this.e), this.min >= this.number) {
  859. this.M = Math.floor(Math.random() * this.prioritize.length);
  860. var a = this.prioritize[this.M];
  861. this.number += a
  862. }
  863. if (this.number < this.min || this.number > this.max) {
  864. this.number = Math.floor(Math.random() * (this.max - this.min + 1)) + this.min;
  865. }
  866. this.innerHTML = parseInt(this.number);
  867. }
  868. }
  869.  
  870. customElements.define('number-random', numberRandom);
  871.  
  872. // --------------------------
  873. // Lookbook product carousel
  874. // --------------------------
  875.  
  876. class LookbookProductCarousel extends HTMLElement {
  877.  
  878. constructor() {
  879. super();
  880. if (window.innerWidth > 767) {
  881. return;
  882. }
  883. this.lookbookMobile();
  884. }
  885. lookbookMobile() {
  886. let self = this;
  887. const el = self.querySelector('[lookbook-product-wrap]');
  888. const controls = self.querySelector('[hdt-slider-controls]');
  889. const btn_close = self.querySelector('button.hdt-close');
  890. const isToolBar = document.querySelector('.hdt-toolbar-mobile');
  891. let toolBarH = 0;
  892. if (!el | !controls | !btn_close) return;
  893. if (isToolBar) {
  894. toolBarH += isToolBar.offsetHeight;
  895. }
  896.  
  897. self.querySelector('[lookbook-product-wrap]').style.bottom = `${toolBarH / 10}rem`;
  898. controls.addEventListener('click', () => {
  899. el.classList.add('hdt-open');
  900. })
  901. btn_close.addEventListener('click', () => {
  902. el.classList.remove('hdt-open');
  903. })
  904. }
  905. }
  906. customElements.define('lookbook-product-carousel', LookbookProductCarousel);
  907.  
  908. /**
  909. * Cookies
  910. * https://help.shopify.com/en/manual/your-account/privacy/cookies
  911. * https://shopify.dev/themes/trust-security/cookie-banner#create-a-snippet-to-host-the-banner
  912. * https://help.shopify.com/en/manual/your-account/privacy/privacy-preferences-manager?shpxid=965d0d2a-08E2-4654-4A13-D3F38646AC5D
  913. * https://shopify.dev/api/consent-tracking
  914. */
  915.  
  916. function convertNameCookies(name) {
  917. return name.replace(/=/g,'_');
  918. }
  919. function setCookie(name,value,days,hours) {
  920. var expires = "";
  921. if (days || hours) {
  922. var date = new Date();
  923. date.setTime(date.getTime() + (days ? days*24*60*60*1000 : hours*60*60*1000));
  924. expires = "; expires=" + date.toString();
  925. }
  926. document.cookie = convertNameCookies(name) + "=" + (value || "") + expires + "; path=/";
  927. }
  928. function getCookie(name) {
  929. var nameEQ = convertNameCookies(name) + "=";
  930. var ca = document.cookie.split(';');
  931. for(var i=0;i < ca.length;i++) {
  932. var c = ca[i];
  933. while (c.charAt(0)==' ') c = c.substring(1,c.length);
  934. if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
  935. }
  936. return null;
  937. }
  938. function eraseCookie(name) {
  939. document.cookie = convertNameCookies(name)+'=; Max-Age=-99999999;';
  940. }
  941.  
  942. class cookiesBar extends HTMLElement {
  943. constructor() {
  944. super();
  945. this.cookies = JSON.parse(this.getAttribute("configs"));
  946. this.cookies.cookiesName = "theme4:cookies";
  947. this.cookies.drawer = this.querySelector("#drawerCookies");
  948. this.isShowCookiesAll = (this.cookies.show == '1');
  949. this.acceptBtn = this.cookies.drawer.querySelector(".hdt-pp_cookies__accept-btn");
  950. this.declineBtn = this.cookies.drawer.querySelector(".hdt-pp_cookies__decline-btn");
  951. this.loadCookieBanner(this);
  952. let self = this;
  953. this.acceptBtn?.addEventListener("click", this.handleAccept.bind(this));
  954. this.declineBtn?.addEventListener("click", this.handleDecline.bind(this));
  955. if (Shopify.designMode) {
  956. document.addEventListener('shopify:section:select', (event) => {
  957. const cookiesSelect = event.target.classList.contains('sys-cookies');
  958. if (!cookiesSelect) return;
  959. self.showCookiesBanner();
  960. })
  961. document.addEventListener('shopify:section:deselect', (event) => {
  962. const cookiesSelect = event.target.classList.contains('sys-cookies');
  963. if (!cookiesSelect) return;
  964. self.hideCookiesBanner();
  965. })
  966. }
  967. }
  968. handleAccept() {
  969. if (this.isShowCookiesAll) {
  970. setCookie(this.cookies.cookiesName, 'accepted', parseInt(this.cookies.day_next));
  971. }
  972. window.Shopify.customerPrivacy.setTrackingConsent(true, this.hideCookiesBanner.bind(this));
  973. }
  974.  
  975. handleDecline() {
  976. if (this.isShowCookiesAll) {
  977. setCookie(this.cookies.cookiesName, 'accepted', parseInt(this.cookies.day_next));
  978. }
  979. window.Shopify.customerPrivacy.setTrackingConsent(false, this.hideCookiesBanner.bind(this));
  980. }
  981.  
  982. showCookiesBanner() {
  983. this.cookies.drawer.setAttribute('open', '');
  984. $4("html").classList.remove("no-scroll");
  985. }
  986.  
  987. hideCookiesBanner() {
  988. this.cookies.drawer.removeAttribute('open');
  989. }
  990.  
  991. initCookieBanner() {
  992. const userCanBeTracked = window.Shopify.customerPrivacy.userCanBeTracked();
  993. const userTrackingConsent = window.Shopify.customerPrivacy.getTrackingConsent();
  994.  
  995. if ((!userCanBeTracked && userTrackingConsent === 'no_interaction') || this.isShowCookiesAll) {
  996. if (!Shopify.designMode) {
  997. this.showCookiesBanner();
  998. }
  999. }
  1000. }
  1001.  
  1002. loadCookieBanner() {
  1003.  
  1004. let self = this;
  1005. if (this.cookies.drawer.length == 0) return;
  1006. this.cookies.day_next = this.cookies.day_next || 60;
  1007. this.isShowCookiesAll = (this.cookies.show == '1');
  1008. if (getCookie(this.cookies.cookiesName) == 'accepted' || this.cookies.drawer.getAttribute("open") !== null) return;
  1009.  
  1010. window.Shopify.loadFeatures([
  1011. {
  1012. name: 'consent-tracking-api',
  1013. version: '0.1',
  1014. }
  1015. ],
  1016. function (error) {
  1017. if (error) {
  1018. throw error;
  1019. }
  1020. self.initCookieBanner();
  1021. });
  1022. }
  1023. }
  1024.  
  1025. customElements.define('sys-cookies', cookiesBar);
  1026.  
  1027. // --------------------------
  1028. // Newsletter
  1029. // --------------------------
  1030.  
  1031. class newsletterModal extends HTMLElement {
  1032. constructor() {
  1033. super();
  1034. this.configs = JSON.parse(this.getAttribute('configs'));
  1035. if ( !Shopify.designMode && getCookie("theme4:newsletter:"+this.configs.id) == 'shown' ) return;
  1036. this.modal = this.querySelector("#modalNewsletter-"+this.configs.id);
  1037. this.action_close = this.querySelector('[action-close]');
  1038. this.time_delay = this.configs.time_delay;
  1039. this.day_next = this.configs.day_next;
  1040. if (Shopify.designMode) {
  1041. document.addEventListener('shopify:section:select', (event) => {
  1042. const newsletterSelect = event.target.classList.contains('sys-newsletter');
  1043. if (!newsletterSelect) return;
  1044. this.modal.setAttribute('open', '');
  1045. })
  1046. document.addEventListener('shopify:section:deselect', (event) => {
  1047. const newsletterSelect = event.target.classList.contains('sys-newsletter');
  1048. if (!newsletterSelect) return;
  1049. this.modal.removeAttribute('open');
  1050. })
  1051. }
  1052. else {
  1053. const fnShow = this.showModal.bind(this);
  1054. const fnShowScroll = this.showModalScroll.bind(this);
  1055. if(this.configs.after === 'time'){
  1056. let tm = setTimeout(fnShow,this.configs.time_delay * 1000);
  1057. this.modal.addEventListener("dialog:afterClose", () => {
  1058. clearTimeout(tm);
  1059. this.hideModal(false, true);
  1060. });
  1061. }else{
  1062. window.addEventListener("scroll",fnShowScroll);
  1063. this.modal.addEventListener("dialog:afterClose", () => {
  1064. window.removeEventListener("scroll", fnShowScroll);
  1065. this.hideModal(false, true);
  1066. });
  1067. }
  1068. this.actionClose();
  1069. }
  1070. }
  1071.  
  1072. showModal() {
  1073. this.modal.setAttribute('open', '');
  1074. }
  1075. showModalScroll(){
  1076. if (window.scrollY > this.configs.scroll_delay) {
  1077. this.modal.setAttribute('open', '');
  1078. }
  1079. }
  1080. hideModal(pause, off) {
  1081. if (pause) {
  1082.  
  1083. }
  1084. if (off) {
  1085.  
  1086. setCookie("theme4:newsletter:"+this.configs.id, "shown", this.day_next);
  1087. }
  1088. }
  1089. actionClose(){
  1090. let self= this;
  1091. if(self.action_close){
  1092. self.action_close.addEventListener('click',function(){
  1093. self.modal.removeAttribute('open', '');
  1094. self.hideModal(false, true);
  1095. })
  1096. }
  1097. }
  1098.  
  1099. }
  1100. customElements.define('sys-newsletter', newsletterModal);
  1101.  
  1102. // --------------------------
  1103. // Exit
  1104. // --------------------------
  1105.  
  1106. class exitModal extends HTMLElement {
  1107. constructor() {
  1108. super();
  1109. this.configs = JSON.parse(this.getAttribute('configs'));
  1110. if ( !Shopify.designMode && getCookie("theme4:exit:"+this.configs.id) == 'shown' ) return;
  1111. this.modal = this.querySelector("#modalExit-"+this.configs.id);
  1112. this.day_next = this.configs.day_next;
  1113. this.btn_copy = this.querySelector('button[is="discount_copy"]');
  1114. this.discount = this.querySelector('[is="discount"]');
  1115. if (Shopify.designMode) {
  1116. document.addEventListener('shopify:section:select', (event) => {
  1117. const exitSelect = event.target.classList.contains('sys-exit');
  1118. if (!exitSelect) return;
  1119. this.showModal();
  1120. })
  1121. document.addEventListener('shopify:section:deselect', (event) => {
  1122. const exitSelect = event.target.classList.contains('sys-exit');
  1123. if (!exitSelect) return;
  1124. this.modal.removeAttribute('open');
  1125. })
  1126. }
  1127. else {
  1128. if(this.configs.after === 'move_cursor'){
  1129. if(window.innerWidth > 1150){
  1130. this.moveCursor();
  1131. }else{
  1132. this.scrollPage();;
  1133. }
  1134. }else if(this.configs.after === 'scroll'){
  1135. this.scrollPage();
  1136. console.log();
  1137. }
  1138. else{
  1139. this.amountTime();
  1140. }
  1141. }
  1142. }
  1143. // Move cursor out screen
  1144. moveCursor(){
  1145. const fnShow = this.showModal.bind(this);
  1146. document.querySelector("body").addEventListener("mouseleave", fnShow);
  1147. this.modal.addEventListener("dialog:afterClose", () => {
  1148. document.querySelector("body").removeEventListener("mouseleave", fnShow);
  1149. this.hideModal(false, true);
  1150. });
  1151. }
  1152. // scroll page
  1153. scrollPage(){
  1154. const fnShow = this.showModal.bind(this);
  1155. let docHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight - 500;
  1156. let scroll= ()=>{
  1157. if (window.scrollY >= docHeight) {
  1158. fnShow();
  1159. }
  1160. }
  1161. window.addEventListener("scroll",scroll);
  1162. this.modal.addEventListener("dialog:afterClose", () => {
  1163. window.removeEventListener("scroll",scroll);
  1164. this.hideModal(false, true);
  1165. });
  1166. }
  1167. // countdown time to show exit popup
  1168. amountTime(){
  1169. const fnShow = this.showModal.bind(this);
  1170. let tm = setTimeout(fnShow,this.configs.time_delay * 1000);
  1171. this.modal.addEventListener("dialog:afterClose", () => {
  1172. clearTimeout(tm);
  1173. this.hideModal(false, true);
  1174. });
  1175. }
  1176. showModal() {
  1177. let self = this;
  1178. this.modal.setAttribute('open', '');
  1179. if(self.btn_copy && self.discount){
  1180. self.btn_copy.addEventListener('click',function(){
  1181. self.discount.select();
  1182. self.discount.setSelectionRange(0, 99999);
  1183. document.execCommand("copy");
  1184. self.btn_copy.querySelector('.hdt-tooltip-text').innerText = `${themeHDN.extras.exit_popup.copied}: ${self.discount.value}`
  1185. })
  1186. self.btn_copy.addEventListener('mouseleave',function(){
  1187. self.btn_copy.querySelector('.hdt-tooltip-text').innerText = themeHDN.extras.exit_popup.copy
  1188. })
  1189. }
  1190. }
  1191. hideModal(pause, off) {
  1192. if (pause) {
  1193.  
  1194. }
  1195. if (off) {
  1196. setCookie("theme4:exit:"+this.configs.id, "shown", this.day_next);
  1197. }
  1198. }
  1199. }
  1200. customElements.define('sys-exit', exitModal);
  1201.  
  1202. // --------------------------
  1203. // Countdown Cart
  1204. // --------------------------
  1205.  
  1206. class cartCountdown extends HTMLElement {
  1207. constructor() {
  1208. super();
  1209. let settMinutes = 60 * this.getAttribute("time"), time = parseInt(sessionStorage.getItem('cartTime')), display = this.querySelector('#timer_count');
  1210. this.config = JSON.parse(this.getAttribute("config"));
  1211. var self = this;
  1212. function startTimer(duration, display) {
  1213. let timer = duration, minutes, seconds;
  1214. let countdown = setInterval(function () {
  1215. minutes = parseInt(timer / 60, 10)
  1216. seconds = parseInt(timer % 60, 10);
  1217. minutes = minutes < 10 ? minutes : minutes;
  1218. seconds = seconds < 10 ? "0" + seconds : seconds;
  1219. display.textContent = minutes + self.config.labelm + " : " + seconds + self.config.labels;
  1220. let calc = 60 * parseInt(timer / 60, 10) + parseInt(timer % 60, 10);
  1221. if (!Shopify.designMode) {
  1222. sessionStorage.setItem('cartTime', calc);
  1223. }
  1224. if (--timer < 0) {
  1225. window.clearInterval(countdown);
  1226. display.textContent = 0 + self.config.labelm + " : " + 0 + self.config.labels;
  1227. fetch(Shopify.routes.root+"cart/clear.js")
  1228. .then((response) => response.text())
  1229. .then((text) => {
  1230. console.log("Clear cart")
  1231. window.location.href = `${Shopify.routes.root}cart`;
  1232. })
  1233. .catch((e) => {
  1234. console.error(e);
  1235. });
  1236. }
  1237. }, 1000);
  1238. };
  1239. if (time > 0) {startTimer(time, display);}
  1240. else {startTimer(settMinutes, display)}
  1241. }
  1242. }
  1243. customElements.define('cart-countdown', cartCountdown);
  1244.  
  1245. // --------------------------
  1246. // Form status
  1247. // --------------------------
  1248.  
  1249.  
  1250. const contactForm = $$4('form[action^="/contact"]');
  1251. contactForm.forEach(function(form) {
  1252. form.addEventListener("submit", function(event){
  1253. sessionStorage.setItem("the4:recentform", this.getAttribute("id"));
  1254. })
  1255. });
  1256. var recentform = sessionStorage.getItem("the4:recentform") || "";
  1257.  
  1258. if (location.href.indexOf('contact_posted=true') > 0 && recentform !== "" ) {
  1259. document.dispatchEvent( new CustomEvent('the4:recentform', { detail: { recentform: recentform }, bubbles: true, cancelable: true }) );
  1260. $id4(recentform)?.classList.add("on-live");
  1261. if ($4('.form-status-mirror-'+ recentform)) {
  1262. $4('.form-status-mirror-'+ recentform).style.cssText = "display: block;";
  1263. }
  1264. }
  1265.  
  1266. else if (location.href.indexOf('customer_posted=true') > 0 && recentform !== "" ) {
  1267. document.addEventListener('the4:recentform', (event)=>{
  1268. let modal_id = document.getElementById(event.detail.recentform).getAttribute('data-id');
  1269. document.getElementById(modal_id).setAttribute('open','')
  1270. })
  1271. document.dispatchEvent( new CustomEvent('the4:recentform', { detail: { recentform: recentform }, bubbles: true, cancelable: true }) );
  1272. $id4(recentform)?.classList.add("on-live");
  1273. }
  1274. else if (location.href.indexOf('contact_posted=true') > 0 && location.href.indexOf('#') > 0 ) {
  1275. var recentform = location.href.split("#")[1];
  1276. if ($id4(recentform).length > 0) {
  1277. sessionStorage.setItem("the4:recentform", recentform);
  1278. $id4(recentform).classList.add("on-live");
  1279. if ($4('.form-status-mirror-'+ recentform)) {
  1280. $4('.form-status-mirror-'+ recentform).style.cssText = "display: block;";
  1281. }
  1282. }
  1283. }
  1284.  
  1285. // --------------------------
  1286. // Total sold flash
  1287. // --------------------------
  1288. class flashSold extends VariantChangeBase {
  1289. constructor() {
  1290. super();
  1291. var self = this;
  1292. this.time = this.getAttribute("time");
  1293. this.config = JSON.parse(this.getAttribute("flash-sold"));
  1294. this.mins = this.config.mins;
  1295. this.maxs = this.config.maxs;
  1296. this.mint = this.config.mint;
  1297. this.maxt = this.config.maxt;
  1298. this.dataID = this.config.id;
  1299. this.getS = sessionStorage.getItem("soldS" + this.dataID) || this.getRandomInt(this.mins, this.maxs);
  1300. this.getT = sessionStorage.getItem("soldT" + this.dataID) || this.getRandomInt(this.mint, this.maxt);
  1301. this.numS = parseInt(this.getS);
  1302. this.numT = parseInt(this.getT);
  1303. this.intervalTime = parseInt(this.config.time);
  1304. this.$sold = this.querySelector('[data-sold]');
  1305. this.$hour = this.querySelector('[data-hour]');
  1306. this.limitMinMax();
  1307. this.updateSold(this.numS, this.numT);
  1308. setInterval(function () {
  1309. this.numS = this.numS + self.getRandomInt(1, 4);
  1310. this.numT = this.numT + (Math.random() * (0.8 - 0.1) + 0.1).toFixed(1) * 1;
  1311. self.limitMinMax();
  1312. self.updateSold(self.numS, self.numT);
  1313. }, this.intervalTime);
  1314. }
  1315.  
  1316. getRandomInt(min, max) {
  1317. return Math.floor(Math.random() * (max - min + 1)) + min;
  1318. }
  1319.  
  1320. updateSold(num1, num2) {
  1321. this.$sold.innerHTML = num1;
  1322. this.$hour.innerHTML = Math.floor(this.numT);
  1323. sessionStorage.setItem("soldS" + this.dataID, num1);
  1324. sessionStorage.setItem("soldT" + this.dataID, num2);
  1325. }
  1326. limitMinMax() {
  1327. if (this.numS > this.maxs) this.numS = self.getRandomInt(this.mins, this.maxs)
  1328. if (this.numT > this.maxt) this.numT = self.getRandomInt(this.mins, this.maxt)
  1329. }
  1330. #preVariantId;
  1331. onVariantChanged(event) {
  1332. const variant = event.detail.variant;
  1333. if (variant && this.#preVariantId != variant.id) {
  1334. if(variant.available){
  1335. this.closest(".hdt-product-info__item").style.display = "block"
  1336. }
  1337. else {
  1338. this.closest(".hdt-product-info__item").style.display = "none"
  1339. }
  1340. }
  1341. }
  1342. }
  1343. customElements.define('flash-sold', flashSold);
  1344.  
  1345. // --------------------------
  1346. // Multi brands
  1347. // --------------------------
  1348. var hdtNavLoading = true;
  1349. class multiBrands extends HTMLElement {
  1350. constructor() {
  1351. super();
  1352. if($id4("hdt-nav-ul").classList.value.includes("hdt-nav-loading") < 0){
  1353. return false;
  1354. }
  1355. this.item = $$4("[item-brand]", this);
  1356. // this.section_id = this.getAttribute("s_id");
  1357. // this.url = "";
  1358. if(this.item.length) {
  1359. for(var i=0; i < this.item.length; i++) {
  1360. if (this.item[i].parentElement.classList.value.includes("is--active")) {
  1361. this.url = this.item[i].getAttribute("href");
  1362. const lazyMb = $4('hdt-lazy-submenu#sub-hdt-nav-mb');
  1363. if (lazyMb) {
  1364. lazyMb.addEventListener('lazyhtml:added', () => $4(`#hdt-nav-ul-mb summary[data-url="${this.url}"]`)?.parentElement.setAttribute("open",""));
  1365. } else {
  1366. $4(`#hdt-nav-ul-mb summary[data-url="${this.url}"]`)?.parentElement.setAttribute("open","");
  1367. }
  1368. // localStorage.setItem("the4:brand:url", this.url);
  1369. break;
  1370. }
  1371. }
  1372. // if(this.url === "") {
  1373. // this.url = localStorage.getItem("the4:brand:url") || Shopify.routes.root;
  1374. // for(var i=0; i < this.item.length; i++) {
  1375. // if (this.item[i].getAttribute("href") === this.url) {
  1376. // this.item[i].parentElement.classList.add("is--active");
  1377. // $4("#hdt-nav-ul-mb summary[data-url=\""+ this.url +"\"]").parentElement.setAttribute("open","");
  1378. // if (i > 0) {
  1379. // this._init();
  1380. // }
  1381. // break;
  1382. // }
  1383. // }
  1384. // }
  1385. }
  1386. }
  1387. // _init(){
  1388. // var nameStorage = "the4:brand:nav" + this.url.replace(/\//g,'-');
  1389. // if(!Shopify.designMode && sessionStorage.getItem(nameStorage) !== null){
  1390. // $id4("hdt-nav-ul").innerHTML = sessionStorage.getItem(nameStorage);
  1391. // $id4("hdt-nav-ul").classList.remove("hdt-nav-loading");
  1392. // return;
  1393. // }
  1394. // if (!hdtNavLoading) {
  1395. // return;
  1396. // }
  1397. // fetch(this.url + "?section_id=" + this.section_id)
  1398. // .then((response) => response.text())
  1399. // .then((text) => {
  1400. // const html = document.createElement('div');
  1401. // html.innerHTML = text;
  1402. // const nav = html.querySelector('#hdt-nav-ul');
  1403. // if (nav && nav.innerHTML.trim().length) {
  1404. // $id4("hdt-nav-ul").innerHTML = nav.innerHTML;
  1405. // $id4("hdt-nav-ul").classList.remove("hdt-nav-loading");
  1406. // sessionStorage.setItem(nameStorage, nav.innerHTML.trim().replace(/^\s+|\s+$|\n/gm,''));
  1407. // }
  1408. // })
  1409. // .catch((e) => {
  1410. // console.error(e);
  1411. // });
  1412. // hdtNavLoading = false;
  1413. // }
  1414. }
  1415.  
  1416. customElements.define('multi-brands', multiBrands);
  1417.  
  1418. // footer accodion
  1419. function handleAccordionClick() {
  1420. let accordion = this.parentElement;
  1421. accordion.classList.toggle('open');
  1422. if (window.innerWidth < 768) {
  1423. let content = accordion.querySelector('.hdt-collapse-content');
  1424. if (accordion.classList.contains('open')) {
  1425. content.style.height = content.scrollHeight + 'px';
  1426. } else {
  1427. content.style.height = '0';
  1428. }
  1429. }
  1430. }
  1431. function updateMaxHeight() {
  1432. if (window.innerWidth >= 768) {
  1433. document.querySelectorAll('.hdt-footer-section .hdt-collapse-content').forEach(function(content) {
  1434. content.style.height = '';
  1435. });
  1436. }
  1437. }
  1438. document.querySelectorAll('.hdt-footer-section .hdt-heading-f').forEach(function(element) {
  1439. element.addEventListener('click', handleAccordionClick);
  1440. });
  1441. window.addEventListener('resize', function() {
  1442. if (window.innerWidth < 768) {
  1443. document.querySelectorAll('.hdt-footer-section .hdt-heading-f').forEach(function(element) {
  1444. element.removeEventListener('click', handleAccordionClick);
  1445. element.addEventListener('click', handleAccordionClick);
  1446. });
  1447. } else {
  1448. document.querySelectorAll('.hdt-footer-section .hdt-heading-f').forEach(function(element) {
  1449. element.removeEventListener('click', handleAccordionClick);
  1450. });
  1451. updateMaxHeight();
  1452. }
  1453. });
  1454.  
  1455. // --------------------------
  1456. // Delivery Time
  1457. // --------------------------
  1458. // https://unpkg.com/[email protected]/dayjs.min.js
  1459. import dayjs from 'dayjs';
  1460. dayjs.locale('en'); // use locale globally
  1461. class orderDelivery extends VariantChangeBase {
  1462. constructor() {
  1463. super();
  1464. this.config = JSON.parse(this.getAttribute('config'));
  1465. this.offDays = this.config.off_day.replace(/ /g,'').split(",");
  1466. this.nowDay = dayjs();
  1467. var format_day = this.config.format_day,
  1468. time = this.config.time.replace("24:00:00", "23:59:59"),
  1469. arrDayWeek = ["SUN","MON","TUE","WED","THU","FRI","SAT"],
  1470. dateStart = this.config.estimateStartDate || 0,
  1471. dateEnd = this.config.estimateEndDate || 0,
  1472. excludeDays = this.config.cut_day.replace(/ /g,'').split(","),
  1473. startDay = dayjs(),
  1474. i = 0,
  1475. endDay = dayjs(),
  1476. j = 0,
  1477. nowTime = this.nowDay.format('HHmmss'),
  1478. timeint = time.replace(/ /g,'').replace(/:/g,''),
  1479.  
  1480. arrDay = themeHDN.extras.order.dayNames.replace(/ /g,'').split(","),
  1481. arrMth = themeHDN.extras.order.monthNames.replace(/ /g,'').split(",");
  1482.  
  1483. /**
  1484. * Check Time, if nowTime >= timeint +1 day
  1485. */
  1486. if (parseInt(nowTime) >= parseInt(timeint)) {
  1487. this.nowDay = this.nowDay.add(1, 'day');
  1488. startDay = startDay.add(1, 'day');
  1489. endDay = endDay.add(1, 'day');
  1490. }
  1491.  
  1492. /**
  1493. * Mode: 2 - Shipping + delivery
  1494. * Mode: 1 - Only delivery
  1495. */
  1496.  
  1497. if ( this.config.mode == '2' ) {
  1498.  
  1499. // START DATE
  1500. // if ngay khach mua trung voi ngay loai tru tang 1 ngay
  1501. while (excludeDays.indexOf( arrDayWeek[startDay.format('d')] ) > -1 || this.offDays.indexOf( startDay.format('DD/MM/****') ) > -1 || this.offDays.indexOf( startDay.format('DD/MM/YYYY') ) > -1) {
  1502. startDay = startDay.add(1, 'day');
  1503. }
  1504. while (i < dateStart) {
  1505. i++;
  1506. startDay = startDay.add(1, 'day');
  1507. if (excludeDays.indexOf( arrDayWeek[startDay.format('d')] ) > -1 || this.offDays.indexOf( startDay.format('DD/MM/****') ) > -1 || this.offDays.indexOf( startDay.format('DD/MM/YYYY') ) > -1) {
  1508. i--;
  1509. }
  1510. }
  1511.  
  1512. // END DATE
  1513. // if ngay khach mua trung voi ngay loai tru tang 1 ngay
  1514. while (excludeDays.indexOf( arrDayWeek[endDay.format('d')] ) > -1 || this.offDays.indexOf( endDay.format('DD/MM/****') ) > -1 || this.offDays.indexOf( endDay.format('DD/MM/YYYY') ) > -1) {
  1515. endDay = endDay.add(1, 'day');
  1516. }
  1517.  
  1518. while (j < dateEnd) {
  1519. j++;
  1520. endDay = endDay.add(1, 'day');
  1521. if (excludeDays.indexOf( arrDayWeek[endDay.format('d')] ) > -1 || this.offDays.indexOf( endDay.format('DD/MM/****') ) > -1 || this.offDays.indexOf( endDay.format('DD/MM/YYYY') ) > -1) {
  1522. j--;
  1523. }
  1524. }
  1525.  
  1526. }
  1527. else {
  1528.  
  1529. // START DATE
  1530. startDay = startDay.add(dateStart, 'day');
  1531. while (excludeDays.indexOf( arrDayWeek[startDay.format('d')] ) > -1 || this.offDays.indexOf( startDay.format('DD/MM/****') ) > -1 || this.offDays.indexOf( startDay.format('DD/MM/YYYY') ) > -1) {
  1532. startDay = startDay.add(1, 'day');
  1533. }
  1534.  
  1535. // END DATE
  1536. endDay = endDay.add(dateEnd, 'day');
  1537. while (excludeDays.indexOf( arrDayWeek[endDay.format('d')] ) > -1 || this.offDays.indexOf( endDay.format('DD/MM/****') ) > -1 || this.offDays.indexOf( endDay.format('DD/MM/YYYY') ) > -1) {
  1538. endDay = endDay.add(1, 'day');
  1539. }
  1540. // endDay = endDay.add(this.offDaysf(endDay), 'day');
  1541.  
  1542. }
  1543.  
  1544. /**
  1545. * Translate day, month
  1546. * https://day.js.org/docs/en/display/format
  1547. */
  1548. arrDay = this.ArrUnique(arrDay);
  1549. arrMth = this.ArrUnique(arrMth);
  1550.  
  1551. var startDayDInt = parseInt(startDay.format('D')),
  1552. daystStart = startDayDInt + this.nth(startDayDInt),
  1553. MntStart = arrMth[ parseInt(startDay.format('M')) -1 ],
  1554. dayStart = arrDay[ parseInt(startDay.format('d')) ],
  1555.  
  1556. EndDayDInt = parseInt(endDay.format('D')),
  1557. daystEnd = EndDayDInt + this.nth( EndDayDInt ),
  1558. MntEnd = arrMth[ parseInt(endDay.format('M')) -1 ],
  1559. dayEnd = arrDay[ parseInt(endDay.format('d')) ];
  1560.  
  1561. //console.log( startDayDInt, EndDayDInt )
  1562. $4('[data-start-delivery]', this).innerHTML = startDay.format(format_day).replace('t44',dayStart).replace('t45',daystStart).replace('t46',MntStart);
  1563. $4('[data-end-delivery]', this).innerHTML = endDay.format(format_day).replace('t44',dayEnd).replace('t45',daystEnd).replace('t46',MntEnd);
  1564. }
  1565.  
  1566. ArrUnique(arr) {
  1567. var onlyUnique = function (value, index, self) {
  1568. return self.indexOf(value) === index;
  1569. };
  1570. return arr.filter( onlyUnique );
  1571. }
  1572.  
  1573. nth(d) {
  1574. if (d > 3 && d < 21) return 'th';
  1575. switch (d % 10) {
  1576. case 1: return "st";
  1577. case 2: return "nd";
  1578. case 3: return "rd";
  1579. default: return "th";
  1580. }
  1581. }
  1582. #preVariantId;
  1583. onVariantChanged(event) {
  1584. const variant = event.detail.variant;
  1585. if (variant && this.#preVariantId != variant.id) {
  1586. if(variant.available){
  1587. this.closest(".hdt-product-info__item").style.display = ""
  1588. }
  1589. else {
  1590. this.closest(".hdt-product-info__item").style.display = "none"
  1591. }
  1592. }
  1593. }
  1594. }
  1595. customElements.define('order-delivery', orderDelivery);
  1596. class countdownSimple extends HTMLElement {
  1597. x = 0;
  1598. constructor() {
  1599. super();
  1600.  
  1601. /** Countdown
  1602. * [days] [hours] [mins] [secs]
  1603. */
  1604.  
  1605. this.config = JSON.parse(this.getAttribute('config'));
  1606. const time = this.config.time.replace("24:00:00", "23:59:59")
  1607.  
  1608. if (time == '19041994') return;
  1609. this.textTemp = $4("template", this).innerHTML;
  1610. this.notHasDay = !this.textTemp.includes('[days]');
  1611. let today = dayjs();
  1612.  
  1613. const nowTime = today.format('HHmmss'),
  1614. configTime = time.replace(/ /g,'').replace(/:/g,'')
  1615.  
  1616. if (parseInt(nowTime) >= parseInt(configTime)) {
  1617. today = today.add(1, 'day');
  1618. }
  1619. // Set the date we're counting down to
  1620. // "2030-01-05 15:37:25"
  1621. this.countDownDate = new Date(`${today.format('YYYY-MM-DD')} ${time}`).getTime();
  1622.  
  1623. // Update the count down every 1 second
  1624. this.#update();
  1625. this.x = setInterval( this.#update.bind(this), this.textTemp.includes('[secs]') ? 1000 : 60000);
  1626. }
  1627. #update() {
  1628. // Get today's date and time
  1629. const now = new Date().getTime();
  1630.  
  1631. // Find the distance between now and the count down date
  1632. const distance = this.countDownDate - now;
  1633.  
  1634. // Time calculations for days, hours, minutes and seconds
  1635. const days = Math.floor(distance / (1000 * 60 * 60 * 24));
  1636. let hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
  1637. const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
  1638. const seconds = Math.floor((distance % (1000 * 60)) / 1000);
  1639. if (this.notHasDay) hours = days*24 + hours;
  1640.  
  1641. // Display the result in the element
  1642. this.innerHTML = this.textTemp.replace('[days]', String(days).padStart(2, '0')).replace('[hours]', String(hours).padStart(2, '0')).replace('[mins]', String(minutes).padStart(2, '0')).replace('[secs]', String(seconds).padStart(2, '0'));
  1643.  
  1644. // If the count down is finished, write some text
  1645. if (distance < 0) {
  1646. clearInterval(this.x);
  1647. // EXPIRED
  1648. this.hidden = true
  1649. }
  1650. }
  1651. disconnectedCallback() {
  1652. clearInterval(this.x);
  1653. }
  1654. }
  1655. customElements.define('hdt-countdown-simple', countdownSimple);
  1656.  
  1657. class countdownTimer extends VariantChangeBase {
  1658. constructor() {
  1659. super();
  1660. }
  1661. #preVariantId;
  1662. onVariantChanged(event) {
  1663. const variant = event.detail.variant;
  1664. if (variant && this.#preVariantId != variant.id) {
  1665. if(variant.available){
  1666. this.style.display = ""
  1667. }
  1668. else {
  1669. this.style.display = "none"
  1670. }
  1671. }
  1672. }
  1673. }
  1674. customElements.define('countdown-timer', countdownTimer);
  1675.  
  1676. // quick order list
  1677.  
  1678. var createCartPromise = ()=> {
  1679. return new Promise(async (resolve) => {
  1680. resolve(await (await fetch(`${Shopify.routes.root}cart.js`)).json());
  1681. });
  1682. };
  1683. var _upate_qty,
  1684. upate_qty_fn;
  1685. class QuickOrderList extends HTMLElement {
  1686. constructor() {
  1687. super();
  1688. const ON_CHANGE_DEBOUNCE_TIMER = 300;
  1689. this.actions = {
  1690. add: 'ADD',
  1691. update: 'UPDATE',
  1692. };
  1693. this.quickOrderListId = '#'+this.id;
  1694. this.sectionId = this.dataset.sectionId;
  1695. this.updateThink();
  1696.  
  1697. const debouncedOnChange = debounce((event) => {
  1698. this.onChange(event);
  1699. }, ON_CHANGE_DEBOUNCE_TIMER);
  1700.  
  1701. this.addEventListener('change', debouncedOnChange.bind(this));
  1702. getterAdd(_upate_qty, this);
  1703. document.addEventListener("cart:push:section.id", (event) => event.detail.sections.push(this.sectionId));
  1704. }
  1705. onSubmit(event) {
  1706. event.preventDefault();
  1707. }
  1708.  
  1709. connectedCallback() {
  1710. document.addEventListener($4('hdt-cart-drawer') ? 'cart:drawer:change' : 'variant:add', this.fetchQuickOrder.bind(this) );
  1711. }
  1712. //disconnectedCallback() {}
  1713.  
  1714. updateThink() {
  1715. this.items_in_cart = $4('[update_items_in_cart]', this);
  1716. this.items_price_in_cart = $4('[update_items_price_in_cart]', this);
  1717. $4('form#QuickOrderList', this).addEventListener('submit', this.onSubmit.bind(this));
  1718. }
  1719. fetchQuickOrder(event) {
  1720. const html = new DOMParser().parseFromString(event.detail.sections[this.sectionId], 'text/html');
  1721. const sourceQty = html.querySelector(this.quickOrderListId);
  1722. $4('form#QuickOrderList', this).removeEventListener('submit', this.onSubmit.bind(this));
  1723. this.innerHTML = sourceQty.innerHTML;
  1724. document.dispatchEvent(new CustomEvent("currency:update"));
  1725. this.updateThink();
  1726. // fetch(`${window.location.pathname}?section_id=${this.sectionId}`)
  1727. // .then((response) => response.text())
  1728. // .then((responseText) => {
  1729. // const html = new DOMParser().parseFromString(responseText, 'text/html');
  1730. // const sourceQty = html.querySelector(this.quickOrderListId);
  1731. // this.innerHTML = sourceQty.innerHTML;
  1732. // })
  1733. // .catch((e) => { console.error(e); });
  1734. }
  1735. onChange(event) {
  1736. const inputValue = parseInt(event.target.value);
  1737. const cartQuantity = parseInt(event.target.dataset.cartQuantity);
  1738. const index = event.target.dataset.index;
  1739. const quantity = inputValue - cartQuantity;
  1740.  
  1741. if (cartQuantity > 0) {
  1742. this.updateQuantity(index, inputValue, this.actions.update);
  1743. } else {
  1744. this.updateQuantity(index, quantity, this.actions.add);
  1745. }
  1746. }
  1747. onLoading(id){
  1748. $4(`#Variant-${id} [price_loading]`, this)?.setAttribute('aria-busy',true);
  1749. $$4('#hdt-quick-order-list-total .hdt-btn-loading__svg', this).forEach(el => el.setAttribute('aria-busy',true));
  1750. }
  1751. deLoading(id){
  1752. $4(`#Variant-${id} [price_loading]`, this)?.setAttribute('aria-busy',false);
  1753. $$4('#hdt-quick-order-list-total .hdt-btn-loading__svg', this).forEach(el => el.setAttribute('aria-busy',false));
  1754. }
  1755. removeButton(id,quantity){
  1756. quantity !== 0 ? $4(`#Variant-${id} hdt-quick-order-list-remove-button`, this).removeAttribute('hidden') : $4(`#Variant-${id} hdt-quick-order-list-remove-button`, this).setAttribute('hidden','');
  1757. }
  1758. updateInCart(id,variant_in_cart){
  1759. // label
  1760. $4(`#Variant-${id} .hdt-quantity__rules-cart`, this).removeAttribute('hidden');
  1761. let variant_qty_cart = $4(`#Variant-${id} .hdt-quantity-cart`, this);
  1762.  
  1763. // totals
  1764. let variant_total = $4(`#Variant-${id} [variant_update_price]`, this);
  1765.  
  1766. // update
  1767. if(variant_in_cart?.length !== 0 && variant_in_cart){
  1768. let variant_total_price = parseFloat(variant_in_cart.original_line_price)
  1769. variant_qty_cart.innerText = variant_in_cart.quantity
  1770. variant_total.innerText = formatMoney(variant_total_price,themeHDN.settings.currencyFormat);
  1771. variant_total.setAttribute('data-variant-price', variant_total_price);
  1772. } else {
  1773. variant_qty_cart.innerText = 0;
  1774. variant_total.innerText = formatMoney(0,themeHDN.settings.currencyFormat);
  1775. variant_total.setAttribute('data-variant-price', 0);
  1776. $4(`#Variant-${id} .hdt-quantity__rules-cart`, this).setAttribute('hidden','');
  1777. }
  1778. this.removeT4CrurrencyData(variant_total);
  1779. }
  1780. removeT4CrurrencyData(el, dataset = el.dataset) {
  1781. for (const [key, value] of Object.entries(dataset)) {
  1782. if(key.toLowerCase().indexOf("currency") >= 0) delete el.dataset[key];
  1783. }
  1784. }
  1785. updateTotalInCart(){
  1786. let items_total = 0;
  1787. let items_price = 0;
  1788.  
  1789. // update items_total
  1790. let items = $$4('[data-cart-quantity]', this);
  1791. items.forEach(item => {
  1792. return items_total += parseInt(item.getAttribute('data-cart-quantity'));
  1793. })
  1794. this.items_in_cart.innerText = items_total;
  1795.  
  1796. // update items_price
  1797. let prices = $$4('[data-variant-price]', this);
  1798. prices.forEach(price =>{
  1799. return items_price += parseFloat(price.getAttribute('data-variant-price'));
  1800. })
  1801. this.items_price_in_cart.innerText = formatMoney(items_price,themeHDN.settings.currencyFormat);
  1802. this.removeT4CrurrencyData(this.items_price_in_cart);
  1803. document.dispatchEvent(new CustomEvent("currency:update"));
  1804. }
  1805.  
  1806. fetchConfig(type = 'json') {
  1807. return {
  1808. method: 'POST',
  1809. headers: { 'Content-Type': 'application/json', Accept: `application/${type}` },
  1810. };
  1811. }
  1812. // update quantity product
  1813. updateQuantity(id, quantity, action) {
  1814. document.dispatchEvent(new CustomEvent("theme:loading:doing",{bubbles:!0}));
  1815. this.removeButton(id,quantity);
  1816. this.onLoading(id);
  1817. let bundleSections = [];
  1818. document.dispatchEvent(new CustomEvent("cart:push:section.id", { bubbles: true, detail: { sections: bundleSections } }));
  1819. let routeUrl = `${Shopify.routes.root}cart/change.js`;
  1820. let body = JSON.stringify({
  1821. quantity,
  1822. id,
  1823. sections_url: window.location.pathname,
  1824. sections: bundleSections.join(",")
  1825. });
  1826. let fetchConfigType;
  1827.  
  1828. if (action === this.actions.add) {
  1829. fetchConfigType = 'javascript';
  1830. routeUrl = `${Shopify.routes.root}cart/add.js`;
  1831. body = JSON.stringify({
  1832. items: [
  1833. {
  1834. quantity: parseInt(quantity),
  1835. id: parseInt(id),
  1836. },
  1837. ],
  1838. sections_url: window.location.pathname,
  1839. sections: bundleSections.join(",")
  1840. });
  1841. }
  1842.  
  1843. fetch(`${routeUrl}`, { ...this.fetchConfig(fetchConfigType), ...{ body } })
  1844. .then((response) => {
  1845. return response.text();
  1846. })
  1847. .then((state) => {
  1848. let cartContent = JSON.parse(state);
  1849. const quantityElement = document.getElementById(`Quantity-${id}`);
  1850.  
  1851. if(quantity === 0){
  1852. this.resetQuantityInput(id,quantityElement,true);
  1853. }
  1854. quantityElement.setAttribute('data-cart-quantity',quantity);
  1855. getterRunFn(_upate_qty, this, upate_qty_fn).call(this, cartContent, parseInt(id), action, bundleSections)
  1856. })
  1857. .catch((error) => {
  1858. console.log(error);
  1859. })
  1860. .finally(() => {
  1861. this.updateTotalInCart();
  1862. document.dispatchEvent(new CustomEvent("currency:update"));
  1863. document.dispatchEvent(new CustomEvent("theme:loading:done",{bubbles:!0}));
  1864. this.deLoading(id);
  1865. });
  1866. }
  1867. resetQuantityInput(id, quantityElement,resetValue) {
  1868. const input = quantityElement ?? document.getElementById(`Quantity-${id}`);
  1869. input.value = input.getAttribute('value');
  1870. resetValue ? input.value = 0 : input.value = input.getAttribute('value');
  1871. }
  1872. updateError(updatedValue, id) {
  1873. let message = '';
  1874. if (typeof updatedValue === 'undefined') {
  1875. message = window.cartStrings.error;
  1876. } else {
  1877. message = window.cartStrings.quantityError.replace('[quantity]', updatedValue);
  1878. }
  1879. this.updateLiveRegions(id, message);
  1880. }
  1881.  
  1882. updateLiveRegions(id, message) {
  1883. const variantItemError = this.getElementById('Quick-order-list-item-error');
  1884. if (variantItemError) {
  1885. $4('.hdt-variant-item__error-text', variantItemError).innerHTML = message;
  1886. variantItemError.removeAttribute('hidden');
  1887. }
  1888. }
  1889.  
  1890. }
  1891. _upate_qty = new WeakSet();
  1892. upate_qty_fn = async function(cartContent, id, action) {
  1893. const variant_in_cart = cartContent.items.find((item) => item.variant_id === id);
  1894. this.updateInCart(id,variant_in_cart);
  1895. this.dispatchEvent(new CustomEvent("quickOrder:change", { bubbles: false}));
  1896. //document.dispatchEvent(new CustomEvent("cart:refresh"));
  1897. let cart = cartContent;
  1898. if (action === this.actions.add) {
  1899. cart = await createCartPromise();
  1900. cart.sections = cartContent.sections;
  1901. }
  1902. document.dispatchEvent(new CustomEvent("cart:change", {
  1903. bubbles: true,
  1904. detail: {
  1905. baseEvent: "line-item:change",
  1906. cart,
  1907. disabledDrawerChange: true
  1908. }
  1909. }));
  1910. };
  1911.  
  1912. customElements.define('hdt-quick-order-list', QuickOrderList);
  1913.  
  1914. // remove variant in cart
  1915. class QuickOrderListRemoveButton extends HTMLElement {
  1916. constructor() {
  1917. super();
  1918. this.addEventListener('click',(event) => {
  1919. event.preventDefault();
  1920. const quickOrderList = this.closest('hdt-quick-order-list');
  1921. quickOrderList.updateQuantity(this.dataset.index, 0);
  1922. this.setAttribute('hidden','');
  1923. });
  1924. }
  1925. }
  1926.  
  1927. customElements.define('hdt-quick-order-list-remove-button', QuickOrderListRemoveButton);
  1928.  
  1929. // remove all variant in cart
  1930. class QuickOrderListRemoveAllButton extends HTMLElement {
  1931. constructor() {
  1932. super();
  1933. this.quickOrderList = this.closest('hdt-quick-order-list');
  1934. const allVariants = Array.from(this.quickOrderList.querySelectorAll('[data-variant-id]'));
  1935. const items = {};
  1936. const list_variants = []
  1937. let hasVariantsInCart = false;
  1938.  
  1939. allVariants.forEach((variant) => {
  1940. const cartQty = parseInt(variant.dataset.cartQty);
  1941. if (cartQty > 0) {
  1942. hasVariantsInCart = true;
  1943. items[parseInt(variant.dataset.variantId)] = 0;
  1944. list_variants.push(variant.dataset.variantId);
  1945. }
  1946. });
  1947.  
  1948. if (!hasVariantsInCart) {
  1949. this.setAttribute('hidden','')
  1950. }
  1951.  
  1952. this.addEventListener('click', (event) => {
  1953. event.preventDefault();
  1954. this.quickOrderList.updateMultipleQty(items,list_variants);
  1955. this.setAttribute('hidden','')
  1956. });
  1957. }
  1958. }
  1959.  
  1960. customElements.define('hdt-quick-order-list-remove-all-button', QuickOrderListRemoveAllButton);
  1961.  
  1962.  
  1963. // Begin instagram feed api
  1964. const dataInstagramCache = {};
  1965. class instagramFeedAPI extends HTMLElement {
  1966. constructor() {
  1967. super();
  1968. this.config = JSON.parse(this.getAttribute('config'));
  1969. if (Shopify.designMode) {
  1970. document.addEventListener('shopify:section:select', (event) => {
  1971. this.refresh_ins();
  1972. })
  1973. document.addEventListener('shopify:section:deselect', (event) => {
  1974. const {acc,id} = this.config;
  1975. var data = sessionStorage.getItem('hdt_ins'+acc+id);
  1976. if (data != null && data != '') {
  1977. sessionStorage.removeItem('hdt_ins'+acc+id);
  1978. }
  1979. })
  1980. }
  1981. }
  1982. connectedCallback() {
  1983. this.refresh_ins();
  1984. }
  1985. refresh_ins(){
  1986. const {acc,id} = this.config;
  1987.  
  1988. if (acc == '') return;
  1989.  
  1990. var data = sessionStorage.getItem('hdt_ins'+acc+id);
  1991.  
  1992. if (data != null && data != '') {
  1993. // calculate expiration time for content,
  1994. // to force periodic refresh after 30 minutes
  1995. var now = new Date(),
  1996. expiration = new Date(JSON.parse(data).timestamp);
  1997.  
  1998. expiration.setMinutes(expiration.getMinutes() + 30);
  1999.  
  2000. // ditch the content if too old
  2001. if (now.getTime() > expiration.getTime()) {
  2002. data = null;
  2003. sessionStorage.removeItem('hdt_ins'+acc+id);
  2004. }
  2005. }
  2006. if ( data != null && data != '' ) {
  2007. this.instagramHTML(JSON.parse(data).content,false);
  2008. }
  2009. else{
  2010. if (dataInstagramCache[acc]) {
  2011. $4('.hdt-slider__container', this).innerHTML = dataInstagramCache[acc];
  2012. $4('.hdt-ins-loading', this)?.remove();
  2013. const lazySlider = $4('hdt-slider-lazy', this);
  2014. if (lazySlider) {
  2015. lazySlider.hidden = false;
  2016. lazySlider.InitLazy();
  2017. }
  2018. return;
  2019. }
  2020.  
  2021. fetch('https://graph.instagram.com/me/media?fields=id,media_type,media_url,permalink,thumbnail_url,caption,children&access_token='+atob(acc))
  2022. .then((response) => {
  2023. if(!response.ok) { throw new Error("not ok"); }
  2024. return response.json()
  2025. })
  2026. //.then(function(res)
  2027. .then((res) => {
  2028. //console.log('Success:', res);
  2029. this.instagramHTML(res.data,true);
  2030. })
  2031. .catch(() => {
  2032. $4('.hdt-ins-loading', this)?.remove();
  2033. console.error("Instagram Feed:error fetch");
  2034. });
  2035. }
  2036. }
  2037. instagramHTML(data,saveSessionStorageIns) {
  2038. const arrIcons = ($4('.hdt-icons-ins-svg', this).innerHTML || '').split('[hdtplit]'),
  2039. icons = {
  2040. image : arrIcons[0],
  2041. video : arrIcons[1],
  2042. carousel_album: arrIcons[2]
  2043. },
  2044. {id, acc, target, limit} = this.config;
  2045. let html = '';
  2046.  
  2047. data.forEach( (el, index) => {
  2048. if (index >= limit) return 0;
  2049. const media_type = el.media_type.toLowerCase();
  2050. html += `<div class="hdt-slider__slide hdt-ins-type-${media_type}"><a calc-nav data-no-instant rel="nofollow" class="hdt-ins-inner hdt-block hdt-relative hdt-oh hdt-rounded" href="${el.permalink}" target="${target}"><div class="hdt-ratio"><img src="${el.thumbnail_url || el.media_url}" class="hdt-ins-img" loading="lazy"></div><div class="hdt-ins-icon">${icons[media_type]}</div></a></div>`
  2051. });
  2052. dataInstagramCache[acc] = html;
  2053. $4('.hdt-slider__container', this).innerHTML = html;
  2054. $4('.hdt-ins-loading', this)?.remove();
  2055. const lazySlider = $4('hdt-slider-lazy', this);
  2056. if (lazySlider) {
  2057. lazySlider.hidden = false;
  2058. lazySlider.InitLazy();
  2059. }
  2060.  
  2061. if(saveSessionStorageIns){
  2062. sessionStorage.setItem('hdt_ins'+acc+id, JSON.stringify({
  2063. timestamp: new Date(),
  2064. content: data
  2065. }));
  2066. }
  2067. }
  2068. }
  2069. customElements.define('ins-feed-api', instagramFeedAPI);
  2070.  
  2071. // End instagram feed api
  2072.  
  2073. // Tab main-brands //
  2074. class TabsBrands extends HTMLElement {
  2075. constructor() {
  2076. super();
  2077. this.links = this.querySelectorAll('.hdt-tab-link');
  2078. this.tabs = this.querySelectorAll('.hdt-tab-content');
  2079. this.addAllListeners();
  2080. }
  2081.  
  2082. addAllListeners() {
  2083. this.links.forEach(link => {
  2084. link.addEventListener('click', () => {
  2085. const tabName = link.getAttribute('data-filter');
  2086. if (tabName === 'all') {
  2087. this.openAllTabs();
  2088. } else {
  2089. this.openTab(tabName);
  2090. }
  2091. this.links.forEach(item => {
  2092. item.classList.remove('is--active');
  2093. });
  2094. if (!link.classList.contains('is--active')) {
  2095. link.classList.add('is--active');
  2096. }
  2097. });
  2098. });
  2099. }
  2100.  
  2101. openAllTabs() {
  2102. this.tabs.forEach(content => {
  2103. content.style.display = 'block';
  2104. content.style.height = 'auto';
  2105. });
  2106. this.querySelector('[data-filter="all"]').classList.add('is--active');
  2107. }
  2108.  
  2109. openTab(tabName) {
  2110. this.tabs.forEach(content => {
  2111. if (content.id === tabName) {
  2112. content.style.display = 'block';
  2113. content.classList.add('slideInRight');
  2114. } else {
  2115. content.style.display = 'none';
  2116. content.classList.remove('slideInRight');
  2117. }
  2118. });
  2119. }
  2120.  
  2121. connectedCallback() {
  2122. this.addAllListeners();
  2123. }
  2124. disconnectedCallback() {
  2125. }
  2126. }
  2127.  
  2128. customElements.define('tabs-brands', TabsBrands);
  2129.  
  2130. // //
  2131.  
  2132.  
  2133. class countryFilter extends HTMLElement {
  2134. constructor() {
  2135. super();
  2136. this.search = $4('[name=country_filter]', this);
  2137. this.reset = $4(".hdt-country_filter__reset", this);
  2138. if (this.search) {
  2139. this.search.addEventListener(
  2140. 'input',
  2141. debounce((event) => {
  2142. this.filterCountries();
  2143. }, 200).bind(this)
  2144. );
  2145. }
  2146. this.search.addEventListener('keydown', this.onSearchKeyDown.bind(this));
  2147.  
  2148. }
  2149. filterCountries(){
  2150. const searchValue = this.search.value.toLowerCase();
  2151. const allCountries = this.closest("hdt-popover").querySelectorAll('hdt-richlist button');
  2152. let visibleCountries = allCountries.length;
  2153. allCountries.forEach((item) => {
  2154. const countryName = item.getAttribute('data-name').toLowerCase();
  2155. if (countryName.indexOf(searchValue) > -1) {
  2156. item.classList.remove('hdt-d-none');
  2157. visibleCountries++;
  2158. } else {
  2159. item.classList.add('hdt-d-none');
  2160. visibleCountries--;
  2161. }
  2162. });
  2163. this.closest("hdt-popover").updatePos();
  2164. this.closest("hdt-popover").shadowRoot.querySelector('.hdt-current-scrollbar').scrollTop = 0;
  2165. }
  2166.  
  2167. onSearchKeyDown(event) {
  2168. if (event.code.toUpperCase() === 'ENTER') {
  2169. event.preventDefault();
  2170. }
  2171. }
  2172. }
  2173.  
  2174. customElements.define('country-filter', countryFilter);
  2175.  
  2176.  
  2177. // check purchase code
  2178. if(Shopify.designMode){
  2179.  
  2180. /**
  2181. * If it's a global variable then window[variableName] or in your case window["onlyVideo"] should do the trick.
  2182. * https://stackoverflow.com/questions/5613834/convert-string-to-variable-name-in-javascript
  2183. */
  2184. function isStorageSupported(type) {
  2185. // Return false if we are in an iframe without access to sessionStorage
  2186. // window.self !== window.top
  2187.  
  2188. var storage = (type === 'session') ? window.sessionStorage : window.localStorage;
  2189.  
  2190. try {
  2191. storage.setItem('t4s', 'test');
  2192. storage.removeItem('t4s');
  2193. return true;
  2194. } catch (error) {
  2195. return false;
  2196. }
  2197. };
  2198.  
  2199. var ThemeCode = atob(window[atob('Y0hWeVkyaGg=')]),
  2200. ThemeName_base64 = window[atob('VkdobGJXVk9ZVzFsVkRR')],
  2201. ThemeName = atob(ThemeName_base64),
  2202. CookieName = 'SXNBY3RpdmVUaGVtZQ=='+ThemeName_base64,
  2203. ShopEmail = atob(window[atob('VTJodmNFMWxiMVEw')]),
  2204. isTrueSet = (sessionStorage.getItem(CookieName) === 'true' ),
  2205. str_temp_active = atob('I3Q0cy10ZW1wLWtleS1hY3RpdmU='), // #t4s-temp-key-active
  2206. str_purchase = atob('cHVyY2hhc2VfY29kZXQ0'); // purchase_codet4;
  2207.  
  2208. // console.log(ThemeCode,ThemeName,ShopEmail,CookieName,str_temp_active,str_purchase)
  2209. function alert_active_html() {
  2210. return ``;
  2211. };
  2212.  
  2213. // console.log('ThemeCode', ThemeCode, isTrueSet)
  2214.  
  2215. if (ThemeCode == '') {
  2216. let dom1 = (new DOMParser).parseFromString(alert_active_html(), "text/html");
  2217. document.body.append(dom1.body.firstElementChild);
  2218. let dom2 = (new DOMParser).parseFromString();
  2219. document.body.prepend(dom2.body.firstElementChild);
  2220. sessionStorage.removeItem(CookieName);
  2221. localStorage.removeItem(CookieName);
  2222. }
  2223. else if ( !isTrueSet ) {
  2224.  
  2225. //console.log(ShopEmail, ThemeName, ThemeCode);
  2226.  
  2227. var domain = window.location.hostname,
  2228. mix = ['4','t','h','e','p','l','i','c','o','/','.',':','n','s'],
  2229. mix_domain = mix[2]+mix[1]+mix[1]+mix[4]+mix[13]+mix[11]+mix[9]+mix[9]+mix[5]+mix[6]+mix[7]+mix[10]+mix[1]+mix[2]+mix[3]+mix[0]+mix[10]+mix[7]+mix[8]+mix[9]+mix[5]+mix[6]+mix[7]+mix[3]+mix[12]+mix[13]+mix[3]+mix[9]+mix[7]+mix[2]+mix[3]+mix[7]+'k',
  2230. data = {
  2231. "shopify_domain": domain,
  2232. "email" : ShopEmail,
  2233. "theme" : ThemeName,
  2234. "purchase_code" : ThemeCode
  2235. };
  2236.  
  2237. fetch(mix_domain, {
  2238. "headers": {
  2239. "accept": "*/*",
  2240. "cache-control": "no-cache",
  2241. "x-requested-with": "XMLHttpRequest"
  2242. },
  2243. "body": btoa (encodeURIComponent(JSON.stringify(data))) ,
  2244. "method": "POST",
  2245. "mode": "cors"
  2246. })
  2247. .then(function(response) {
  2248. if(response.ok){
  2249. return response.json()
  2250. } throw ""
  2251. })
  2252. .then(function(response) {
  2253. let dom = (new DOMParser).parseFromString(alert_active_html(), "text/html");
  2254.  
  2255. if ( response.status == 1) {
  2256.  
  2257. dom.body.firstElementChild.innerHTML = "<p>ACTIVATED SUCCESSFULLY. Thanks for buying my theme!</p>";
  2258. document.body.append(dom.body.firstElementChild);
  2259.  
  2260. // Set a cookie to expire in 1 hour in Javascript
  2261. var isActived = localStorage.getItem(CookieName);
  2262. sessionStorage.setItem(CookieName, 'true')
  2263.  
  2264. if (isActived === 'true') {
  2265. $4(atob('I3B1cmNoYXNlX2NvZGV0NA==')).remove(); // #purchase_codet4
  2266. // $4(atob('I2x1ZmZ5YWJjMTk0'))?.remove(); //#luffyabc194
  2267. }
  2268. else {
  2269. localStorage.setItem(CookieName, "true");
  2270. setTimeout(function(){
  2271. $4(atob('I3B1cmNoYXNlX2NvZGV0NA==')).remove(); // #purchase_codet4
  2272. // $4(atob('I2x1ZmZ5YWJjMTk0'))?.remove(); //#luffyabc194
  2273. }, 1000);
  2274. }
  2275.  
  2276. }
  2277. else {
  2278.  
  2279. var mess = response.message;
  2280. if (mess == "No sale belonging to the current user found with that code") {
  2281.  
  2282. dom.body.firstElementChild.innerHTML = "<p>Purchase code error. It is a sales reversal or a refund. :(((</p>";
  2283.  
  2284. }
  2285. else if (mess.length == 58 || mess.length == 101) {
  2286. dom.body.firstElementChild.innerHTML = "<p>That license key doesn't appear to be valid. Please check your purchase code again!<br>Please open a ticket at <a href='https://support.the4.co' target='_blank'><span>support.the4.co</span></a> if you have any question.</p>";
  2287.  
  2288. }
  2289. else if (mess.length == 104) {
  2290. dom.body.firstElementChild.innerHTML = "<p>The license not match with current theme.!<br>Please open a ticket at <a href='https://support.the4.co' target='_blank'><span>support.the4.co</span></a> if you have any question.</p>";
  2291. }
  2292. else {
  2293. try {
  2294. var mess = mess.split('active domain `')[1].split('`. ')[0];
  2295. }
  2296. catch(err) {
  2297. //var mess = mess;
  2298. }
  2299. dom.body.firstElementChild.innerHTML = "<p>Your purchase code is invalided since it is being activated at another store "+mess+".<br> Please open a ticket at <a class='cg' href='https://support.the4.co' target='_blank'><span>support.the4.co</span></a> to get quick assistance.</p>";
  2300. }
  2301. document.body.append(dom.body.firstElementChild);
  2302.  
  2303. }
  2304.  
  2305. }).catch(function(e) {
  2306. //}).catch((e)=>{
  2307. console.error(e)
  2308. });
  2309.  
  2310. }
  2311. }
  2312. // end check purchase code
Add Comment
Please, Sign In to add comment