var isProductDetail = false;
var products = new Array();
var storeResultsCount = 0;
var storeResultsMax = 0;
var searchQuery = "";
var categories = new Array();
var alphas = [1.0,0.5,0.25,0.12,0.06,0];
var isTall = false;
var currentCategory = "all";
var isDarkMode = false;
var stores = new Array();
var locallymadeonly = false;
var instockonly = false;
var zerowasteonly = false;
var chainsAllowed = [1,2,3];
var excludeStores = [];
var excludeCats = [];
getStores();
mapkit.init({
authorizationCallback: done => {
done("eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IkYzWFIyQzk3S0MifQ.eyJpc3MiOiJZRDg4WFpLTUtLIiwiaWF0IjoxNjc1NDYxMTU5LCJleHAiOjE3MDY4MzIwMDAsIm9yaWdpbiI6ImJldGEuanV4dGEuc2hvcCJ9.21tEYSzPPyNqz1p3rcOFG3y92Xg7XxT7nd1-mpKfmLj7sVnqYIgg7JFiUmbzoDBy92dN6fiPkslOzIvQp1b_Pw");
}
});
var ogcenter = new mapkit.Coordinate(37.76784189377097, -122.44941338146273),
ogspan = new mapkit.CoordinateSpan(0.13, 0.13),
ogregion = new mapkit.CoordinateRegion(ogcenter, ogspan);
var geocoder = new mapkit.Geocoder({
getsUserLocation: false
});
var map = new mapkit.Map("productDetailMapCanvas", {
mapType: mapkit.Map.MapTypes.Standard,
region: ogregion,
showsCompass: mapkit.FeatureVisibility.Hidden,
showsZoomControl: true,
showsMapTypeControl: false
});
function addOrRemove(array,value,shouldAdd) {
if(shouldAdd) {
if (array.indexOf(value) === -1) {
array.push(value);
}
return array;
} else {
return array.filter(item => item !== value);
}
}
function filterChecked(obj, checked) {
switch (obj.id) {
case 'sfmade':
locallymadeonly = checked;
break;
case 'instock':
instockonly = checked;
break;
case 'zerowaste':
zerowasteonly = checked;
break;
case 'regionalchains':
chainsAllowed = addOrRemove(chainsAllowed, 1, checked);
break;
case 'nationalchains':
chainsAllowed = addOrRemove(chainsAllowed, 2, checked);
break;
case 'globalchains':
chainsAllowed = addOrRemove(chainsAllowed, 3, checked);
break;
case 'chains':
chainsAllowed = checked ? [1,2,3] : [];
break;
default:
if (obj.id.startsWith("li_store_")) {
excludeStores = addOrRemove(excludeStores, obj.id.split("_")[2], !checked);
} else if (obj.id.startsWith("li_")) {
excludeCats = addOrRemove(excludeCats, obj.id.split("_")[1], !checked);
}
}
updateResults();
}
function getStores() {
$.getJSON( "src/stores_json.php", function( data ) {
var storeQuery = "";
var storeUrlQuery = "";
var matches = searchQuery.match(/(.*)\s+store:\s*(\S+)(\s*.*)/i);
if (matches != null && matches.length > 0) {
storeQuery = matches[2];
searchQuery = matches[1] + matches[3];
storeUrlQuery = "&store=" + storeQuery;
}
stores = data["stores"];
storesToFetch = Object.keys(stores).reduce(function (filtered, key) {
if (stores[key]["db"] == null || stores[key]["db"] != 1) {
if (!(stores[key]["disabled"] != null && stores[key]["disabled"] == "1")) {
if ((storeQuery.length > 0 && key == storeQuery) || storeQuery.length <= 0) {
filtered[key] = stores[key];
}
}
}
return filtered;
}, {});
// storesToFetch = stores.filter(store => store["db"] == null || store["db"] != 1);
categories = data["categories"];
storeResultsMax = Object.keys(storesToFetch).length + 1;
let selectTag = document.getElementById('search_category');
let filterCatsList = document.getElementById('filter_stores');
var index = 0;
for (cat in categories) {
index++;
let opt = document.createElement("option");
opt.innerHTML = categories[cat]["displayName"];
opt.name = cat;
opt.value = cat;
opt.selected = (cat == "");
selectTag.append(opt);
if(index == 1) { continue; }
let filterCat = document.createElement("li");
filterCat.id = "li_" + cat;
let filterCatCheck = document.createElement("input");
filterCatCheck.type = "checkbox";
filterCatCheck.value = cat;
filterCat.append(filterCatCheck);
let filterCatLabel = document.createElement("label");
filterCatLabel.innerHTML = categories[cat]["displayName"];
filterCat.append(filterCatLabel);
var matchingStores = storesInCat(stores, cat);
if(Object.keys(matchingStores).length > 0) {
let filterStores = document.createElement("ul");
for (store in matchingStores) {
let filterStore = document.createElement("li");
filterStore.id = "li_store_" + store;
let filterStoreCheck = document.createElement("input");
filterStoreCheck.type = "checkbox";
filterStoreCheck.value = matchingStores[store]["name"];
filterStore.append(filterStoreCheck);
let filterStoreLabel = document.createElement("label");
filterStoreLabel.innerHTML = matchingStores[store]["name"];
filterStore.append(filterStoreLabel);
filterStores.append(filterStore);
}
filterCat.append(filterStores);
}
filterCatsList.append(filterCat);
}
$(function(){
$('ul.tree').checkTree({
onCheck(which) {
filterChecked(which[0],true);
},
onUnCheck(which) {
filterChecked(which[0],false);
}
});
});
console.log(("plugins/search.php?q=" + encodeURI(searchQuery) + storeUrlQuery));
$.ajax({ url: ("plugins/search.php?q=" + encodeURI(searchQuery) + storeUrlQuery), dataType: "json" }).done(function(stuff) {
if(stuff != null) {
storeResultsCount++;
$("#triangle_fill").width(Math.round((storeResultsCount/storeResultsMax)*100) + "%");
if(storeResultsCount == storeResultsMax) {
isLoading = false;
}
products.push(...stuff);
updateResults();
}
});
$.each(storesToFetch, function (store, storeData) {
// if (storeData["db"] == null || storeData["db"] != 1) {
console.log(store);
$.ajax({ url: ("plugins/products.php?q=&store=" + store), dataType: "json" }).done(function(stuff) {
console.log("got " + store);
storeResultsCount++;
$("#triangle_fill").width(Math.round((storeResultsCount/storeResultsMax)*100) + "%");
if(storeResultsCount == storeResultsMax) {
isLoading = false;
}
if(stuff != null) {
products.push(...stuff);
updateResults();
// updateResults(stuff, storeData["categories"][0]);
}
});
// }
});
});
}
function hexToRgb(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? [
parseInt(result[1], 16),
parseInt(result[2], 16),
parseInt(result[3], 16)
] : null;
}
const pSBC=(p,c0,c1,l)=>{
let r,g,b,P,f,t,h,i=parseInt,m=Math.round,a=typeof(c1)=="string";
if(typeof(p)!="number"||p<-1||p>1||typeof(c0)!="string"||(c0[0]!='r'&&c0[0]!='#')||(c1&&!a))return null;
if(!this.pSBCr)this.pSBCr=(d)=>{
let n=d.length,x={};
if(n>9){
[r,g,b,a]=d=d.split(","),n=d.length;
if(n<3||n>4)return null;
x.r=i(r[3]=="a"?r.slice(5):r.slice(4)),x.g=i(g),x.b=i(b),x.a=a?parseFloat(a):-1
}else{
if(n==8||n==6||n<4)return null;
if(n<6)d="#"+d[1]+d[1]+d[2]+d[2]+d[3]+d[3]+(n>4?d[4]+d[4]:"");
d=i(d.slice(1),16);
if(n==9||n==5)x.r=d>>24&255,x.g=d>>16&255,x.b=d>>8&255,x.a=m((d&255)/0.255)/1000;
else x.r=d>>16,x.g=d>>8&255,x.b=d&255,x.a=-1
}return x};
h=c0.length>9,h=a?c1.length>9?true:c1=="c"?!h:false:h,f=this.pSBCr(c0),P=p<0,t=c1&&c1!="c"?this.pSBCr(c1):P?{r:0,g:0,b:0,a:-1}:{r:255,g:255,b:255,a:-1},p=P?p*-1:p,P=1-p;
if(!f||!t)return null;
if(l)r=m(P*f.r+p*t.r),g=m(P*f.g+p*t.g),b=m(P*f.b+p*t.b);
else r=m((P*f.r**2+p*t.r**2)**0.5),g=m((P*f.g**2+p*t.g**2)**0.5),b=m((P*f.b**2+p*t.b**2)**0.5);
a=f.a,t=t.a,f=a>=0||t>=0,a=f?a<0?t:t<0?a:a*P+t*p:0;
if(h)return"rgb"+(f?"a(":"(")+r+","+g+","+b+(f?","+m(a*1000)/1000:"")+")";
else return"#"+(4294967296+r*16777216+g*65536+b*256+(f?m(a*255):0)).toString(16).slice(1,f?undefined:-2)
}
// updateResults();
var dm = window.matchMedia('(prefers-color-scheme: dark)');
setColorScheme(dm);
dm.addEventListener('change', e => { setColorScheme(e); });
function setColorScheme(e) {
isDarkMode = e.matches ? true : false;
updateColors();
scrollPage();
}
function scrollPage() {
var scrollOffset = Math.max(0,window.pageYOffset);
var ratio = isTall ? 1.6248 : 0.611111;
var spacing = isTall ? 0.15 : 0.2;
var posY = isProductDetail ? (scrollOffset % 30) : (scrollOffset % ((window.innerWidth * spacing) * ratio));
var percent = isProductDetail ? (posY / 30) : (posY / ((window.innerWidth * spacing) * ratio));
var expopercent = Math.min(percent * 1.5,1); //Math.pow(percent,0.5);
document.getElementById("head").style.transform = "translateY(" + Math.max(0,scrollOffset/2.5) + "px)";
for (var i = 2; i <= 7; i++) {
var obj = document.getElementById("triangle" + i);
obj.style.transform = "translateY(" + Math.min(0,-posY) + "px)";
// obj.style.top = Math.min(0,-posY) + "px";
obj.style.opacity = ((alphas[Math.max(0,i-3)] - alphas[i-2]) * percent) + alphas[i-2];
if (i == 2) {
// var startRGB = hexToRgb(colors[currentCategory]);
var startRGB = hexToRgb(primaryColor());
var endRGB = hexToRgb(pSBC((isDarkMode ? 0.15 : -0.3), primaryColor()));
// var startRGB = [18,208,184];
// var endRGB = [4, 182, 160];
var rgb = "rgb(" +
(((endRGB[0] - startRGB[0]) * expopercent) + startRGB[0]) + "," +
(((endRGB[1] - startRGB[1]) * expopercent) + startRGB[1]) + "," +
(((endRGB[2] - startRGB[2]) * expopercent) + startRGB[2]) + ")";
obj.style.backgroundColor = rgb; //"rgb(255,50,150)"; //rgb;
}
}
}
function primaryColor() {
var primary = "#12D0B8";
if (categories != null && Object.keys(categories).length > 1) {
primary = categories[currentCategory]["color"];
}
return primary;
}
function updateColors() {
map.colorScheme = isDarkMode ? mapkit.Map.ColorSchemes.Dark : mapkit.Map.ColorSchemes.Light;
document.documentElement.style.setProperty('--primary', primaryColor());
document.documentElement.style.setProperty('--darker', pSBC((isDarkMode ? 0.15 : -0.3), primaryColor()));
var rgb = hexToRgb(pSBC((isDarkMode ? -0.9 : 0.9), primaryColor()));
document.documentElement.style.setProperty('--primaryBGTransparent', "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ",0.5)");
document.documentElement.style.setProperty('--primaryBG', pSBC(0.9, primaryColor()));
}
function selectedCategory(selectObject) {
if(selectObject.value == null || selectObject.value == "") {
currentCategory = "all";
} else {
currentCategory = selectObject.value;
}
// currentCategory = selectObject.selectedIndex;
document.getElementById("search_field").placeholder = "Search " + currentCategory.toLowerCase() + "...";
updateColors();
clearResults();
updateResults();
scrollPage();
isTall = (currentCategory == "books");
for (var i = 1; i <= 7; i++) {
var obj = document.getElementById("triangle" + i);
if (isTall) {
obj.classList.add("tall");
document.getElementById("main").classList.remove("small");
document.getElementById("main").classList.add("wide");
} else {
obj.classList.remove("tall");
document.getElementById("main").classList.remove("wide");
document.getElementById("main").classList.add("small");
}
}
for (let cat in categories) {
if (cat != "all") {
if(currentCategory == "all" || cat == currentCategory) {
document.getElementById("li_" + cat).classList.remove("hidden");
} else if(!document.getElementById("li_" + cat).classList.contains("hidden")) {
document.getElementById("li_" + cat).classList.add("hidden");
}
}
}
}
var isLoading = true;
var firstRun = true;
var count = 2;
var productLine;
function clearResults() {
document.getElementById("main").innerHTML = '';
productLine = null;
firstRun = true;
count = 2;
}
function storesInCat(stores, categoryName) {
var matchingStores = new Array();
for (store in stores) {
let cat = stores[store]["categories"];
if (cat != null && cat.includes(categoryName)) {
matchingStores[store] = stores[store];
}
}
return matchingStores;
}
function productsInCat(prods, categoryName) {
return prods.filter(function(prod) {
return prod['categories'].includes(categoryName);
});
}
function productForIndex(prods, index) {
return prods.find(function(prod) {
return prod['id'] == index;
});
}
function matchesQuery(str, query) {
if(str == null) { return 0; }
str = str.toLowerCase();
query = query.toLowerCase();
var matchCount = query.includes(" ") ? str.includes(query) * 1 : 0;
var words = query.split(/\s+/);
matchCount += words.map((word,index) => {
return hasWord(str,word);
}).reduce((a,b) => a+b, 0);
return Math.pow(matchCount,2); // Using power to amplify multiple matches
}
const hasWord = (str, word) => str != null ? str.toLowerCase().split(/\s+/).includes(word.toLowerCase()) : false;
function matchScore(prod) {
var score = (matchesQuery(prod["title"],searchQuery) * 10) + (matchesQuery(prod["product_type"],searchQuery) * 8) + (matchesQuery(prod["product_tags"],searchQuery) * 4) + (matchesQuery(prod["subtitle"],searchQuery) * 6) + (matchesQuery(prod["author"],searchQuery) * 6) + (matchesQuery(prod["summary"],searchQuery) * 2) + (matchesQuery(prod["description"],searchQuery) * 1);
score = score * Math.pow(((4 - stores[prod["store"]]["isChain"]) / 4),0.25);
console.log(prod["title"] + ": " + score);
return score;
}
function updateResults(newResults,newCategory) {
// var catName = currentCategory; //categories[currentCategory];
// var keys = categories; //Object.keys(products);
var results = new Array();
if ((newResults == null || newResults == "") && (searchQuery != null && searchQuery.length > 0)) {
clearResults();
results = products;
} else {
results = newResults;
// results[newCategory] = newResults;
}
var prodsincat = new Array();
if(currentCategory == "all") {
prodsincat = results;
} else {
prodsincat = results.filter(function(prod) {
return prod['categories'].includes(currentCategory.toLowerCase());
});
}
prodsincat = prodsincat.filter(function(prod) {
let store = stores[prod["store"]];
if (store == null) { return false; }
if (locallymadeonly == true && (store["sfmade"] == null || store["sfmade"] == false)) {
return false;
}
if (store["isChain"] != null && store["isChain"] != 0) {
if (chainsAllowed.indexOf(store["isChain"]) === -1) {
return false
}
}
if(excludeStores.includes(prod["store"])) {
return false;
}
if(prod['categories'].some(r => excludeCats.includes(r))) {
return false;
}
return true;
});
prodsincat = prodsincat.sort(function(matchA, matchB) {
return matchScore(matchB) - matchScore(matchA);
});
// for (cat in categories) {
// if (cat.toLowerCase() == currentCategory.toLowerCase() || currentCategory == "all") {
// var prodsincat = productsInCat(results, cat.toLowerCase());
// if (prodsincat != null && prodsincat.length > 0) {
//
// // if (results[cat] != null && results[cat].length > 0) {
// var index = 0;
for (prod of prodsincat) {
count++;
if (firstRun) {
productLine = newObj('div','product_line_init');
firstRun = false;
} else if (count % 7 == 0) {
document.getElementById("main").appendChild(productLine);
productLine = newObj('div','product_line_alt');
} else if (count % 7 == 3) {
document.getElementById("main").appendChild(productLine);
productLine = newObj('div','product_line');
}
var product = newObj('div','product');
if (prod["url"] != null) {
product.setAttribute('onclick',"productDetails('" + prod['id'] + "');");
// product.setAttribute('onclick',"productDetails('" + cat + "'," + index + ");");
// product.setAttribute('onclick',"location.href = '" + prod["url"].toString() + "';");
// console.log(prod["url"]);
}
productLine.appendChild(product);
// product.addEventListener("click", function() {
// location.href = prod["url"].toString();
// });
var innerProduct = newObj('div','inner_product');
product.appendChild(innerProduct);
var productImage = newObj('div','product_image');
productImage.style.backgroundImage = "url('" + prod["imageThumb"] + "')";
innerProduct.appendChild(productImage);
var productInfo = newObj('div','product_info');
innerProduct.appendChild(productInfo);
var productTitle = newObj('div','product_title');
productTitle.innerHTML = prod["title"];
productInfo.appendChild(productTitle);
var productPrice = newObj('div','product_price');
productPrice.innerHTML = prod["price"];
productInfo.appendChild(productPrice);
var productLocation = newObj('div','product_location');
// if (prod["url"] != null) {
productLocation.innerHTML = "In stock at " + stores[prod["store"]]["name"] + " 4 min";
// } else {
// productLocation.innerHTML = "Out of stock";
// }
productInfo.appendChild(productLocation);
var productAltLocations = newObj('div','product_alt_locations');
productAltLocations.innerHTML = "7 other stores nearby also have this product.";
productInfo.appendChild(productAltLocations);
// index++;
}
// }
//
// }
// }
if (productLine != null) {
document.getElementById("main").appendChild(productLine);
} else {
if (searchQuery == null || searchQuery.length < 1) {
// No search
} else {
var noResults = newObj('div','results_overlay');
if(isLoading) {
noResults.innerHTML = '