(function (){
'use strict';
if(!window.FrankVisualSearch){
return;
}
var state={
logId: 0
};
function ready(fn){
if(document.readyState!=='loading'){
fn();
return;
}
document.addEventListener('DOMContentLoaded', fn);
}
function createButton(){
var button=document.createElement('button');
button.type='button';
button.className='fvis-trigger';
button.dataset.fvisOpen='1';
button.setAttribute('aria-label', 'Image search');
button.innerHTML='<span aria-hidden="true">📷</span>';
button.addEventListener('click', openModal);
return button;
}
function isSearchElement(element){
if(!element||element.closest('.fvis-modal')||element.closest('.fvis-product')){
return false;
}
var text=[
element.className||'',
element.id||'',
element.getAttribute('aria-label')||'',
element.getAttribute('title')||'',
element.getAttribute('href')||'',
element.getAttribute('data-target')||''
].join(' ').toLowerCase();
return text.indexOf('search')!==-1||text.indexOf('s=')!==-1;
}
function hasNearbyButton(element){
if(!element||!element.parentElement){
return false;
}
if(element.parentElement.querySelector('.fvis-trigger')){
return true;
}
var next=element.nextElementSibling;
return !!(next&&next.classList&&next.classList.contains('fvis-trigger-wrap'));
}
function insertAfter(control){
if(!control||hasNearbyButton(control)){
return false;
}
var wrap=document.createElement('span');
wrap.className='fvis-trigger-wrap';
wrap.appendChild(createButton());
control.insertAdjacentElement('afterend', wrap);
if(control.parentElement){
control.parentElement.classList.add('fvis-icon-row');
}
return true;
}
function appendInside(container){
if(!container||container.querySelector('.fvis-trigger')){
return false;
}
container.classList.add('fvis-host');
container.appendChild(createButton());
return true;
}
function insertButton(){
var inserted=0;
var iconSelectors=[
'.jas-header a.sf-open',
'.jas-header .sf-open',
'.jas-header a[href*="search"]',
'.jas-header button[class*="search"]',
'.jas-header a[class*="search"]',
'.jas-header .pe-7s-search',
'.jas-header .fa-search',
'.jas-header .la-search',
'.jas-header .icon-search',
'.jas-header .jas-icon-search',
'.header__search > a',
'.header__search button',
'.header__search .icon-search',
'.header-search > a',
'.header-search button',
'.header-search .icon-search',
'.header-search .pe-7s-search',
'.header-search .fa-search',
'.mini-search > a',
'.mini-search button',
'.search-icon',
'.search-trigger',
'.search-button',
'.sf-open',
'a[href*="#search"]',
'a[href*="search"]',
'button[class*="search"]',
'[class*="search"] > a',
'[class*="search"] > button'
];
iconSelectors.forEach(function (selector){
document.querySelectorAll(selector).forEach(function (element){
var control=element.matches('a, button') ? element:element.closest('a, button')||element;
if(!isSearchElement(control)&&!isSearchElement(element)){
return;
}
if(insertAfter(control)){
inserted++;
}});
});
var containerSelectors=[
'.jas-header .header-search',
'.jas-header .jas-search',
'.jas-header .mini-search',
'.jas-action .search',
'.header-icons .search',
'.header-icons [class*="search"]',
'.header__search',
'.header-search',
'.header-search-form',
'.search-outer',
'.mini-search',
'.jas-search',
'.searchform',
'form.woocommerce-product-search',
'form[role="search"]',
'.site-header .search',
'header .search'
];
containerSelectors.forEach(function (selector){
document.querySelectorAll(selector).forEach(function (target){
if(appendInside(target)){
inserted++;
}});
});
if(!inserted){
var header=document.querySelector('.jas-header, .site-header, #header, header');
if(header&&!header.querySelector('.fvis-header-fallback .fvis-trigger')){
var wrap=document.createElement('div');
wrap.className='fvis-header-fallback';
wrap.appendChild(createButton());
header.appendChild(wrap);
}}
}
function openModal(){
var modal=document.getElementById('fvis-modal');
var pageUrl=document.getElementById('fvis-page-url');
if(!modal){
return;
}
if(pageUrl){
pageUrl.value=window.location.href;
}
modal.setAttribute('aria-hidden', 'false');
document.documentElement.classList.add('fvis-lock');
var file=document.getElementById('fvis-file');
if(file){
setTimeout(function (){
file.focus();
}, 60);
}}
function closeModal(){
var modal=document.getElementById('fvis-modal');
if(!modal){
return;
}
modal.setAttribute('aria-hidden', 'true');
document.documentElement.classList.remove('fvis-lock');
}
function setStatus(message, isError){
var status=document.getElementById('fvis-status');
if(!status){
return;
}
status.textContent=message||'';
status.classList.toggle('is-error', !!isError);
}
function clearResults(){
var results=document.getElementById('fvis-results');
if(results){
results.innerHTML='';
}}
function renderResults(items, logId){
var results=document.getElementById('fvis-results');
if(!results){
return;
}
results.innerHTML='';
if(!items||!items.length){
setStatus(FrankVisualSearch.strings.noResults, false);
return;
}
setStatus('', false);
items.forEach(function (item){
var card=document.createElement('a');
card.className='fvis-product';
card.href=item.url;
card.dataset.productId=item.id;
card.addEventListener('click', function (){
trackClick(logId, item.id);
});
var image=document.createElement('img');
image.src=item.image;
image.alt=item.title;
image.loading='lazy';
var body=document.createElement('span');
body.className='fvis-product-body';
var title=document.createElement('span');
title.className='fvis-product-title';
title.textContent=item.title;
var price=document.createElement('span');
price.className='fvis-product-price';
price.innerHTML=item.price||'';
var meta=document.createElement('span');
meta.className='fvis-product-score';
var detectedShape=item.detectedShape||'Unknown';
meta.innerHTML=''
+ '<span>Detected Shape: ' + escapeHtml(detectedShape) + '</span>'
+ '<span>Shape Match: ' + Number(item.shapeScore||0) + '%</span>'
+ '<span>Final Match: ' + Number(item.score||0) + '%</span>';
body.appendChild(title);
body.appendChild(price);
body.appendChild(meta);
card.appendChild(image);
card.appendChild(body);
results.appendChild(card);
});
}
function escapeHtml(value){
return String(value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}
function search(file){
var maxBytes=Number(FrankVisualSearch.maxUploadMb||5) * 1024 * 1024;
var allowed=['image/jpeg', 'image/png', 'image/webp'];
if(!file||allowed.indexOf(file.type)===-1||file.size > maxBytes){
setStatus(FrankVisualSearch.strings.uploadError, true);
return;
}
var formData=new FormData();
formData.append('action', 'fvis_search');
formData.append('nonce', FrankVisualSearch.nonce);
formData.append('image', file);
formData.append('page_url', window.location.href);
clearResults();
setStatus(FrankVisualSearch.strings.searching, false);
fetch(FrankVisualSearch.ajaxUrl, {
method: 'POST',
credentials: 'same-origin',
body: formData
})
.then(function (response){
return response.json();
})
.then(function (payload){
if(!payload||!payload.success){
throw new Error(payload&&payload.data&&payload.data.message ? payload.data.message:FrankVisualSearch.strings.genericError);
}
state.logId=payload.data.logId||0;
renderResults(payload.data.results||[], state.logId);
})
.catch(function (error){
setStatus(error.message||FrankVisualSearch.strings.genericError, true);
});
}
function trackClick(logId, productId){
if(!logId||!productId){
return;
}
var formData=new FormData();
formData.append('action', 'fvis_track_click');
formData.append('nonce', FrankVisualSearch.nonce);
formData.append('log_id', logId);
formData.append('product_id', productId);
if(navigator.sendBeacon){
var params=new URLSearchParams();
params.append('action', 'fvis_track_click');
params.append('nonce', FrankVisualSearch.nonce);
params.append('log_id', logId);
params.append('product_id', productId);
navigator.sendBeacon(FrankVisualSearch.ajaxUrl, params);
return;
}
fetch(FrankVisualSearch.ajaxUrl, {
method: 'POST',
credentials: 'same-origin',
body: formData,
keepalive: true
}).catch(function (){});
}
ready(function (){
insertButton();
setTimeout(insertButton, 500);
setTimeout(insertButton, 1500);
if(window.MutationObserver){
var observer=new MutationObserver(function (){
insertButton();
});
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
setTimeout(function (){
observer.disconnect();
}, 8000);
}
document.addEventListener('click', function (event){
if(event.target&&event.target.closest('[data-fvis-open]')){
event.preventDefault();
openModal();
return;
}
if(event.target&&event.target.matches('[data-fvis-close]')){
closeModal();
}
if(event.target&&event.target.id==='fvis-modal'){
closeModal();
}});
document.addEventListener('keydown', function (event){
if(event.key==='Escape'){
closeModal();
}});
var file=document.getElementById('fvis-file');
if(file){
file.addEventListener('change', function (){
if(file.files&&file.files[0]){
search(file.files[0]);
}});
}});
})();