(function() {
var STOREFRONT_TOKEN = '023adb4fad851d8f82aa1eefdb4ed95c';
var SHOP_DOMAIN = 'meckorn.com';
var API_URL = 'https://' + SHOP_DOMAIN + '/api/2025-01/graphql.json';
var PER_PAGE = 24;
var allProducts = [];
var selections = { brand: '', model: '', barLength: '', pitch: '', gauge: '', dl: '' };
var currentPage = 1;
var QUERY = 'query GetChainProducts($cursor: String) {'
+ ' products(first: 50, after: $cursor, query: "product_type:*Chainsaw Chain*") {'
+ ' pageInfo { hasNextPage endCursor }'
+ ' edges { node {'
+ ' id title handle'
+ ' featuredImage { url altText }'
+ ' priceRange { minVariantPrice { amount currencyCode } }'
+ ' barLength: metafield(namespace: "custom", key: "bar_length") { value }'
+ ' pitch: metafield(namespace: "custom", key: "pitch") { value }'
+ ' gauge: metafield(namespace: "custom", key: "gauge") { value }'
+ ' driveLinks: metafield(namespace: "custom", key: "drive_links") { value }'
+ ' modelRef: metafield(namespace: "custom", key: "model") {'
+ ' references(first: 25) { nodes { ... on Metaobject {'
+ ' modelField: field(key: "model") { value }'
+ ' brandRef: field(key: "numbers") { reference { ... on Metaobject { displayName: field(key: "name") { value } } } }'
+ ' } } }'
+ ' }'
+ ' } } }'
+ '}';
function gql(query, variables) {
return fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Shopify-Storefront-Access-Token': STOREFRONT_TOKEN
},
body: JSON.stringify({ query: query, variables: variables || {} })
}).then(function(res) { return res.json(); });
}
function fetchAllProducts() {
var products = [];
var cursor = null;
var hasMore = true;
function next() {
if (!hasMore) return Promise.resolve(products);
return gql(QUERY, { cursor: cursor }).then(function(data) {
var conn = data && data.data && data.data.products;
if (!conn) return products;
conn.edges.forEach(function(edge) {
var node = edge.node;
var brands = [], models = [];
var nodes = (node.modelRef && node.modelRef.references && node.modelRef.references.nodes) || [];
nodes.forEach(function(mo) {
var modelVal = mo.modelField && mo.modelField.value;
if (modelVal) {
modelVal.split(/[,\uff0c]/).forEach(function(s) {
var t = s.trim();
if (t) models.push(t);
});
}
var brandName = mo.brandRef && mo.brandRef.reference && mo.brandRef.reference.displayName && mo.brandRef.reference.displayName.value;
if (brandName && brands.indexOf(brandName) === -1) brands.push(brandName);
});
products.push({
id: node.id,
title: node.title,
handle: node.handle,
imageUrl: node.featuredImage ? node.featuredImage.url : null,
imageAlt: node.featuredImage ? node.featuredImage.altText : node.title,
price: node.priceRange && node.priceRange.minVariantPrice ? node.priceRange.minVariantPrice.amount : null,
currency: node.priceRange && node.priceRange.minVariantPrice ? node.priceRange.minVariantPrice.currencyCode : null,
_dims: {
brand: brands,
model: models,
barLength: node.barLength && node.barLength.value ? [node.barLength.value] : [],
pitch: node.pitch && node.pitch.value ? [node.pitch.value] : [],
gauge: node.gauge && node.gauge.value ? [node.gauge.value] : [],
dl: node.driveLinks && node.driveLinks.value ? [node.driveLinks.value] : []
}
});
});
hasMore = conn.pageInfo.hasNextPage;
cursor = conn.pageInfo.endCursor;
return next();
});
}
return next();
}
function getOptions(dim) {
var others = Object.keys(selections).filter(function(d) { return d !== dim; });
var compatible = allProducts.filter(function(p) {
return others.every(function(d) {
if (!selections[d]) return true;
var vals = p._dims[d];
return !vals || vals.length === 0 || vals.indexOf(selections[d]) !== -1;
});
});
var set = {};
compatible.forEach(function(p) {
(p._dims[dim] || []).forEach(function(v) { if (v) set[v] = true; });
});
return Object.keys(set).sort(function(a, b) {
return a.localeCompare(b, undefined, { numeric: true });
});
}
function getMatched() {
return allProducts.filter(function(p) {
return Object.keys(selections).every(function(d) {
if (!selections[d]) return true;
var vals = p._dims[d];
return !vals || vals.length === 0 || vals.indexOf(selections[d]) !== -1;
});
});
}
function formatPrice(amount, currency) {
if (!amount) return '';
try {
return new Intl.NumberFormat(undefined, { style: 'currency', currency: currency || 'USD' }).format(amount);
} catch(e) {
return '$' + parseFloat(amount).toFixed(2);
}
}
function renderDropdown(dim) {
var input = document.querySelector('input[data-dim="' + dim + '"]');
var ul = document.querySelector('.cf-dropdown[data-dim="' + dim + '"]');
if (!input || !ul) return;
var opts = getOptions(dim);
var query = input.value.trim().toLowerCase();
var effectiveQuery = (query && query !== (selections[dim] || '').toLowerCase()) ? query : '';
var filtered = effectiveQuery ? opts.filter(function(o) { return o.toLowerCase().indexOf(effectiveQuery) !== -1; }) : opts;
ul.innerHTML = '';
if (selections[dim]) {
var li = document.createElement('li');
li.className = 'clear-item';
li.textContent = '— Clear selection —';
li.addEventListener('mousedown', function(e) {
e.preventDefault();
selections[dim] = '';
input.value = '';
input.classList.remove('has-value');
updateAll();
});
ul.appendChild(li);
}
if (filtered.length === 0) {
var li2 = document.createElement('li');
li2.className = 'no-match';
li2.textContent = effectiveQuery ? 'No matches' : 'No options available';
ul.appendChild(li2);
} else {
filtered.forEach(function(opt) {
var li3 = document.createElement('li');
li3.textContent = opt;
if (opt === selections[dim]) li3.classList.add('selected');
li3.addEventListener('mousedown', function(e) {
e.preventDefault();
selections[dim] = opt;
input.value = opt;
input.classList.add('has-value');
ul.classList.remove('open');
updateAll();
});
ul.appendChild(li3);
});
}
}
function renderResults() {
var matched = getMatched();
var total = matched.length;
var totalPages = Math.max(1, Math.ceil(total / PER_PAGE));
if (currentPage > totalPages) currentPage = 1;
var paged = matched.slice((currentPage - 1) * PER_PAGE, currentPage * PER_PAGE);
document.getElementById('cf-count').textContent = total + ' product' + (total !== 1 ? 's' : '') + ' matched';
var grid = document.getElementById('cf-grid');
if (paged.length === 0) {
grid.innerHTML = '
No products match your selection. Try adjusting your filters.
';
} else {
var html = '';
paged.forEach(function(p) {
var imgHtml = p.imageUrl
? '

'
: '
📦
';
html += '
'
+ imgHtml
+ '
'
+ '
' + p.title + '
'
+ '
' + formatPrice(p.price, p.currency) + '
'
+ '
View Product'
+ '
';
});
grid.innerHTML = html;
}
var pag = document.getElementById('cf-pagination');
if (totalPages <= 1) { pag.innerHTML = ''; return; }
var pagHtml = '';
for (var i = 1; i <= totalPages; i++) {
pagHtml += '
';
}
pag.innerHTML = pagHtml;
pag.querySelectorAll('button').forEach(function(btn) {
btn.addEventListener('click', function() {
currentPage = parseInt(btn.dataset.page);
renderResults();
document.getElementById('chain-finder').scrollIntoView({ behavior: 'smooth', block: 'start' });
});
});
}
function updateAll() {
Object.keys(selections).forEach(function(dim) {
if (selections[dim] && getOptions(dim).indexOf(selections[dim]) === -1) {
selections[dim] = '';
var inp = document.querySelector('input[data-dim="' + dim + '"]');
if (inp) { inp.value = ''; inp.classList.remove('has-value'); }
}
});
Object.keys(selections).forEach(renderDropdown);
currentPage = 1;
renderResults();
}
function setupCombobox(dim) {
var input = document.querySelector('input[data-dim="' + dim + '"]');
var ul = document.querySelector('.cf-dropdown[data-dim="' + dim + '"]');
if (!input || !ul) return;
input.addEventListener('focus', function() { renderDropdown(dim); ul.classList.add('open'); });
input.addEventListener('blur', function() { setTimeout(function() { ul.classList.remove('open'); }, 180); });
input.addEventListener('input', function() {
renderDropdown(dim);
ul.classList.add('open');
if (input.value === '') { selections[dim] = ''; input.classList.remove('has-value'); updateAll(); }
});
}
document.getElementById('cf-reset').addEventListener('click', function() {
Object.keys(selections).forEach(function(d) {
selections[d] = '';
var inp = document.querySelector('input[data-dim="' + d + '"]');
if (inp) { inp.value = ''; inp.classList.remove('has-value'); }
});
currentPage = 1;
updateAll();
});
fetchAllProducts().then(function(products) {
allProducts = products;
document.getElementById('cf-loading').style.display = 'none';
Object.keys(selections).forEach(setupCombobox);
updateAll();
}).catch(function(err) {
document.getElementById('cf-loading').textContent = 'Failed to load products. Please refresh the page.';
console.error('Chain Finder error:', err);
});
})();