This commit is contained in:
2026-05-17 21:09:32 +08:00
commit 1870400c47
1564 changed files with 544713 additions and 0 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,64 @@
document.addEventListener('change', function (e) {
let element = e.target.closest('[name="quantity"]');
if (element) {
let product = element.closest("[data-v-cart-product]");
let key = product.dataset.key;
let quantity = element.value;
let updateElements = ['#cart-container [data-key="' + key + '"] .price', '#cart-container [data-key="' + key + '"] .total', ".cart-right-column", ".mini-cart"];
delay(() => VvvebTheme.Cart.update(key, {quantity}, element, updateElements), 1000);
}
});
/*
document.querySelectorAll('.cart-table .quantity').on('click', '.btn-plus', function (e) {
("input[type =number]", this.parentNode).val(function( index, value ) {
return ++value;
}).change();
});
document.querySelectorAll('.cart-table .quantity').on('click', '.btn-minus', function (e) {
("input[type =number]", this.parentNode).val(function( index, value ) {
return Math.max(--value, 1);
}).change();
});
*/
document.addEventListener('click', function (e) {
let element = e.target.closest('.btn-coupon, .btn-remove-coupon');
if (element) {
let updateElements = [".cart-right-column", ".mini-cart"];
if (element.classList.contains("btn-remove-coupon")) {
let coupon = element.parentNode.querySelector(".code").innerHTML;
let container = e.target.closest("[data-v-cart-coupon]");
VvvebTheme.Cart.removeCoupon({coupon}, element, updateElements);
container.remove();
} else {
let coupon = document.querySelector("[name='coupon']").value;
VvvebTheme.Cart.coupon({coupon}, element, updateElements);
}
e.preventDefault();
}
});
document.addEventListener('click', function (e) {
let element = e.target.closest('.btn-remove');
if (element) {
let product = element.closest("[data-v-cart-product]");
if (product) {
let key = product.dataset.key;
let quantity = element.value;
let updateElements = [".cart-right-column", ".mini-cart"];
product.classList.add("opacity-50");
VvvebTheme.Cart.remove(key, element, updateElements, () => {
product.remove();
//if cart empty refresh page
if (document.querySelectorAll("#cart-container [data-v-cart-product]").length == 0 ) {
location.reload();
}
});
e.preventDefault();
}
}
});

View File

@@ -0,0 +1,217 @@
function refreshCart(parameters, element, update = false) {
VvvebTheme.Cart.module = 'checkout/checkout';
VvvebTheme.Cart.component_id = 1;
//action, parameters, element, selector, callback
if (!update) {
update = ['.cart-summary', '.container > .notifications'];
}
VvvebTheme.Cart.ajax('', parameters, element, update);
//cart-summary
}
function toggleBillingAddress(element) {
let address = document.querySelector(".billing_address");
if (element.value == 0) {
address.querySelectorAll("input,select,textarea").forEach(e => e.removeAttribute("disabled"));
address.style.display = "";
} else {
address.querySelectorAll("input,select,textarea").forEach(e => e.setAttribute("disabled", "true"));
address.style.display = "none";
}
}
function toggleShippingAddress(element) {
let address = document.querySelector(".shipping_address");
if (element) {
if (element.checked == 0) {
address.querySelectorAll("input,select,textarea").forEach(e => e.setAttribute("disabled", "true"));
address.style.display = "none";
} else {
address.querySelectorAll("input,select,textarea").forEach(e => e.removeAttribute("disabled"));
address.style.display = "";
}
}
}
function toggleRegister(element) {
let register = document.querySelector(".register-account");
if (element.value == 'false') {
register.querySelectorAll("input,select,textarea").forEach(e => e.setAttribute("disabled", "true"));
register.style.display = "none";
} else {
register.querySelectorAll("input,select,textarea").forEach(e => e.removeAttribute("disabled"));
register.style.display = "";
}
}
window.addEventListener('DOMContentLoaded', (e) => {
//show billing address form if no address is selected
document.addEventListener("change", e => {
let element = e.target.closest("[name=billing_address_id]");
if (element) {
toggleBillingAddress(element);
}
});
if (!document.getElementById("billing_address_new")?.checked && !document.querySelector("[name=billing_address_id]:checked")?.value) {
//if new address is not checked and no address is selected
document.querySelector("[name=billing_address_id]")?.dispatchEvent(new MouseEvent('click'));//select first address
}
let billing_address = document.querySelector("[name=billing_address_id]:checked");
//if an address is selected hide billing address form
if (billing_address && billing_address.value != false) {
document.querySelector(".billing_address").style.display = "none";
}
//hide shipping address form if same as billing checkbox is checked
toggleShippingAddress(document.getElementById("shipping-form-check"));
//if login form is filled show form
if (document.querySelector("#checkout-login-form [name=password]")?.value) {
document.getElementById('checkout-login-container').style.display = "";
document.getElementById('login-form-check').checked = true;
}
//if shipping or payment method is selected collapse the accordion
document.querySelector('.accordion-item input[type="radio"]:checked')?.closest("label").dispatchEvent(new MouseEvent('click'));
document.addEventListener("click", function(e) {
let element = e.target.closest(".accordion-header label");
if (element) {
//e.stopPropagation();
let item = element.closest(".accordion-item");
let parent = item.closest(".accordion");
let collapse = item.querySelector(":scope > .collapse");
parent.querySelectorAll(".collapse.show").forEach(e => bootstrap.Collapse.getOrCreateInstance(e)?.hide());
parent.querySelectorAll(".accordion-button").forEach(e => e.classList.add("collapsed"));
item.querySelector(".accordion-button").classList.remove("collapsed");
bootstrap.Collapse.getOrCreateInstance(collapse)?.show();
//disable inputs for non selected payment and shipping methods to avoid form validation issues
parent.querySelectorAll(".accordion-body input, .accordion-body select, .accordion-body textarea").forEach(e => e.setAttribute("disabled", "true"));
//enable only for selected method
item.querySelectorAll(".accordion-body input, .accordion-body select, .accordion-body textarea").forEach(e => e.removeAttribute("disabled"));
let input = item.querySelector('[name="shipping_method"], [name="payment_method"]');
let parameters = {};
parameters[element.name] = element.value;
refreshCart(parameters, element);
}
});
//document.querySelector("[data-v-countries][readonly]")?.dispatchEvent(new Event('change', {bubbles:true}));
document.querySelector('input[name="register"]:checked')?.dispatchEvent(new MouseEvent('click', {bubbles:true}));
});
//load regions for region select when country changes
let regions = [];
function addRegionsToSelect(regionSelect, data, region_id = 0, countrySelect) {
regionSelect.replaceChildren();
for (const region of data) {
regionSelect.append(new Option(region.name, region.region_id));
}
regionSelect.value = region_id;
regionSelect.removeAttribute("readonly");
countrySelect.removeAttribute("readonly");
}
function reloadRegions(element){
let parameters = {};
if (element) {
document.querySelectorAll("[data-v-countries],[data-v-regions]").forEach(e => parameters[e.name] = e.value ?? 0);
}
document.querySelector("[data-v-countries][readonly]")?.dispatchEvent(new Event('change'));
refreshCart(parameters, element, ['.cart-summary', '[data-v-component-checkout-shipping]' ,'[data-v-component-checkout-payment]']);
}
document.addEventListener("change", function (e) {
let element = e.target.closest("[data-v-countries]");
if (element) {
let regionGroup = element.closest(".address");
let regionSelect = regionGroup.querySelector("[data-v-regions]");
let country_id = element.value;
let region_id = element.dataset.vRegionId;
let self = element;
element.readonly = false;
if (country_id) {
if (regions[country_id]) {
addRegionsToSelect(regionSelect, regions[country_id], region_id, self);
reloadRegions(element);
} else {
fetch(regionsUrl + "&country_id=" + country_id)
.then(response => {
if (!response.ok) { throw new Error(response) }
return response.json()
})
.then(data => {
regions[country_id] = data;
addRegionsToSelect(regionSelect, data, region_id, self);
document.querySelector("[data-v-countries][readonly]")?.dispatchEvent(new Event('change', {bubbles:true}));
reloadRegions(element);
})
.catch(error => {
console.log(error.statusText);
//displayToast("bg-danger", "Revision", "Error!");
});
}
}
element.dataset.vRegionId = 0;
} else {
let element = e.target.closest("[data-v-regions]");
if (element) {
reloadRegions(element);
}
}
});
function togglePasswordInput(element, input) {
let password = document.getElementById(input);
if (password.type == "password") {
password.type = "text";
let i = element.querySelector("i")
i.classList.add("la-eye")
i.classList.remove("la-eye-slash");
} else {
password.type = "password";
let i = element.querySelector("i")
i.classList.remove("la-eye")
i.classList.add("la-eye-slash");
}
}
document.addEventListener('click', function (e) {
let element = e.target.closest('.btn-coupon, .btn-remove-coupon');
if (element) {
let updateElements = [".cart-right-column", ".mini-cart", ".cart-summary"];
VvvebTheme.Cart.module= 'checkout/checkout';
if (element.classList.contains("btn-remove-coupon")) {
let coupon = element.parentNode.querySelector(".code").innerHTML;
let container = e.target.closest("[data-v-cart-coupon]");
VvvebTheme.Cart.removeCoupon({coupon}, element, updateElements);
container.remove();
} else {
let coupon = document.querySelector("[name='coupon']").value;
VvvebTheme.Cart.coupon({coupon}, element, updateElements);
}
e.preventDefault();
}
});
function toggleLoginForm() {
let container = document.getElementById('checkout-login-container');
container.style.display = container.style.display ? "" : "none";
}
document.querySelectorAll(".accordion-button .form-check-input:checked").forEach(e => e.parentNode.classList.remove("collapsed"));

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
self.addEventListener("fetch", function (event) {});

View File

@@ -0,0 +1,174 @@
/*
* Sticky navbar
*
*/
// When the user scrolls the page, execute navbarSticky
window.onscroll = function() {navbarSticky()};
// Get the navbar
var navbar = document.getElementsByClassName("navbar")[0];
// Get the offset position of the navbar
var sticky = navbar.offsetTop ? navbar.offsetTop : navbar.offsetHeight;
function toggleNavbarTheme () {
if (navbar.classList.contains("navbar-dark")) {
navbar.classList.add("navbar-light");
navbar.classList.remove("navbar-dark");
} else if (navbar.classList.contains("navbar-light")) {
navbar.classList.add("navbar-dark");
navbar.classList.remove("navbar-light");
}
}
// Add the sticky class to the navbar when you reach its scroll position. Remove "sticky" when you leave the scroll position
function navbarSticky(isSticky) {
if (isSticky == undefined) {
isSticky = (window.pageYOffset >= sticky);
}
if (isSticky) {
if (!navbar.classList.contains("sticky")) {
navbar.classList.add("sticky");
toggleNavbarTheme();
}
} else {
if (navbar.classList.contains("sticky")) {
navbar.classList.remove("sticky");
toggleNavbarTheme();
}
}
}
function setCookie(name, value) {
document.cookie = name + "=" + value + ";";
//try to set cookie to all subdomains
document.cookie = name + "=" + value + ";path=/;domain=." + window.location.host.replace(/^.*?\./, '') + ";";
}
let themeSwitch = document.querySelector("#color-theme-switch i");
let theme = document.documentElement.dataset.bsTheme;
let themeCookie = document.cookie.match(/theme=(.*?);/);
if (themeCookie && themeCookie[1]) {
theme = themeCookie[1];
}
if (theme) {
if (theme == "dark") {
let themeSwitch = document.querySelector("#color-theme-switch i");
themeSwitch.classList.remove("la-sun")
themeSwitch.classList.add("la-moon");
document.documentElement.dataset.bsTheme = theme;
}
}
document.addEventListener("click", function (e) {
let link = e.target.closest("#color-theme-switch");
if (link) {
let themeSwitch = link.querySelector("i");
let theme = document.documentElement.dataset.bsTheme;
if (theme == "dark") {
theme = "light";
themeSwitch.classList.remove("la-moon");
themeSwitch.classList.add("la-sun");
} else if (theme == "light" || theme == "auto" || !theme) {
theme = "dark";
themeSwitch.classList.remove("la-sun");
themeSwitch.classList.add("la-moon");
} else {
theme = "auto";
}
document.documentElement.dataset.bsTheme = theme;
//localStorage.setItem("theme", theme);
setCookie("theme", theme);
//serverStorage.setItem();
}
});
// product page
document.querySelectorAll('.quantity').forEach(e => e.addEventListener('click', function (e) {
let btn = e.target.closest(".btn-plus");
if (btn) {
let nrInput = btn.parentNode.querySelector("input[type=number]");
nrInput.value = parseInt(nrInput.value) + 1;
nrInput.dispatchEvent(new KeyboardEvent("change", {
bubbles: true,
cancelable: true,
}));
}
return false;
}));
document.querySelectorAll('.quantity').forEach(e => e.addEventListener('click', function (e) {
let btn = e.target.closest(".btn-minus");
if (btn) {
let nrInput = btn.parentNode.querySelector("input[type=number]");
nrInput.value = Math.max(1, parseInt(nrInput.value) - 1);
nrInput.dispatchEvent(new KeyboardEvent("change", {
bubbles: true,
cancelable: true,
}));
}
return false;
}));
function zoom(e) {
let img = e.currentTarget;
offsetX = e.offsetX || (e.touches ? e.touches[0].pageX : 0);
offsetY = e.offsetY || (e.touches ? e.touches[0].pageY : 0);
x = offsetX / img.offsetWidth * 100;
y = offsetY / img.offsetHeight * 100;
img.style.backgroundPosition = x + "% " + y + "%";
}
document.querySelectorAll('div.zoom').forEach(e => e.addEventListener('mousemove', zoom));
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js', { scope: '/' })
.then(function (registration){console.log('Service worker registered successfully');})
.catch(function (e){console.error('Error during service worker registration:', e);});
}
function togglePasswordInput(element, input) {
let password = document.getElementById(input);
if (password.type == "password") {
password.type = "text";
let i = element.querySelector("i")
i.classList.add("la-eye")
i.classList.remove("la-eye-slash");
} else {
password.type = "password";
let i = element.querySelector("i")
i.classList.remove("la-eye")
i.classList.add("la-eye-slash");
}
}
document.addEventListener("click", function (e) {
let link = e.target.closest(".dropdown-toggle");
if (link) {
let parent = link.closest(".nav-toggle");
if (link.classList.contains("show")) {
parent.classList.add("show");
} else {
parent.classList.remove("show");
}
}
});
//theme ajax configuration
//include elements that will be updated on ajax calls, include body > section to trigger whole page update if sections mismatch between different page structures
VvvebTheme.ajax.siteContainer = ["#site-content", ".inner-page-hero", "body > section"];
//include posts, product and menu items for ajax
//VvvebTheme.ajax.selector = "a[data-url], a[data-page-url], a[data-v-menu-item-url], a[data-v-post-url], a[data-v-product-url], a[data-v-cat-url], a[data-v-archive-url], a[data-v-admin-url], a[data-v-post-author-url], a[data-v-breadcrumb-item-url]";
//skip home for dark hero and contact form for google js code
//VvvebTheme.ajax.skipUrl = ["/", "/page/contact"];

View File

@@ -0,0 +1,36 @@
/*
Vvveb theme VvvebJs editor integration
*/
//Revert theme default styles for non changeable elements on editor page save
window.addEventListener("vvveb.getHtml.before", function(event) {
let doc = event.detail;
//remove sticky class from navbar
if (event.detail.defaultView.navbarSticky){
event.detail.defaultView.navbarSticky(false);
}
//event.detail.defaultView.scrollTo(0,0);
//set theme color scheme to auto
doc.querySelectorAll("html[data-bs-theme]").forEach(e => e.removeAttribute("data-bs-theme"));
doc.querySelectorAll("[data-init]").forEach(e => e.removeAttribute("data-init"));
doc.querySelectorAll(".navbar").forEach(e => e.classList.remove("sticky"));
doc.querySelectorAll(".header").forEach(e => e.classList.remove("active"));
//make sure no dropdown is saved as open
doc.querySelectorAll(".dropdown-toggle.show,.nav-toggle.show, .dropdown-menu.show").forEach(e => e.classList.remove("show"));
//remove animate on scroll classes
doc.querySelectorAll("[data-aos]").forEach(e => e.classList.remove("aos-animate", "aos-init"));
//close image lighbox if open before saving
if (typeof doc.defaultView.glightbox != "undefined") doc.defaultView.glightbox.close();
});
window.addEventListener("vvveb.getHtml.after", function(event) {
let doc = event.detail;
//add animate on scroll classes back
doc.querySelectorAll("[data-aos]").forEach(e => e.classList.add("aos-animate", "aos-init"));
});