(function() {
'use strict';
// Commonly used CSS selectors for Lightspeed / Ecwid product grids and out-of-stock indicators
const GRID_SELECTOR = '.grid-product, .ec-grid, .grid__products';
const ITEM_SELECTOR = '.grid-product__wrap, .grid-product, .ec-grid__item';
// Selectors or text markers indicating out-of-stock items
const OUT_OF_STOCK_SELECTOR = '.grid-product__out-of-stock, .ec-product-option--out-of-stock, .grid-product__sold-out';
let isSorting = false;
function moveOutOfStockToBottom() {
if (isSorting) return;
const grid = document.querySelector(GRID_SELECTOR);
if (!grid) return;
const items = Array.from(grid.querySelectorAll(ITEM_SELECTOR));
if (items.length === 0) return;
// Separate items into in-stock vs out-of-stock
const inStockItems = [];
const outOfStockItems = [];
items.forEach(item => {
// Check for out-of-stock class or "Sold Out" text within the item
const hasOOSClass = item.querySelector(OUT_OF_STOCK_SELECTOR);
const hasOOSText = /out of stock|sold out/i.test(item.innerText);
if (hasOOSClass || hasOOSText) {
outOfStockItems.push(item);
} else {
inStockItems.push(item);
}
});
// Only re-order if there are out-of-stock items mixed in
if (outOfStockItems.length > 0 && inStockItems.length > 0) {
isSorting = true;
// Re-append items to DOM: in-stock first, then out-of-stock at the end
const fragment = document.createDocumentFragment();
inStockItems.forEach(item => fragment.appendChild(item));
outOfStockItems.forEach(item => fragment.appendChild(item));
grid.appendChild(fragment);
// Cooldown to prevent infinite loops triggered by MutationObserver
setTimeout(() => {
isSorting = false;
}, 300);
}
}
// Hook into Ecwid's native lifecycle event
if (window.Ecwid) {
window.Ecwid.OnPageLoaded.add(function(page) {
if (page.type === "CATEGORY" || page.type === "SEARCH") {
setTimeout(moveOutOfStockToBottom, 400);
}
});
}
// DOM Observer to handle dynamic content loads (e.g., Infinite Scroll)
const observer = new MutationObserver(() => {
if (!isSorting) {
moveOutOfStockToBottom();
}
});
// Start watching document body after initial DOM ready
document.addEventListener('DOMContentLoaded', () => {
setTimeout(moveOutOfStockToBottom, 500);
observer.observe(document.body, { childList: true, subtree: true });
});
})();