/* ================================================================
FACE SHAPE SUNGLASSES ANALYZER — FEMALE (profreetools.online)
v2 | Light Pink Theme | v12 Scan Fix | All Buttons Working
================================================================ */
(function(){
'use strict';
var SUNGLASSES_URL = {
Oval: 'https://profreetools.online/wp-content/uploads/2026/05/OVAL-—-Cat-Eye-Rose-Gold-1.webp',
Round: 'https://profreetools.online/wp-content/uploads/2026/05/ROUND-—-Colorful-Retro-pink-2-1.webp',
Square: 'https://profreetools.online/wp-content/uploads/2026/05/SQUARE-—-Bold-Purple-3.webp',
Heart: 'https://profreetools.online/wp-content/uploads/2026/05/HEART-—-Butterfly-Coral-4.webp',
Diamond: 'https://profreetools.online/wp-content/uploads/2026/05/DIAMOND-—-Rimless-Rose-5.webp',
Oblong: 'https://profreetools.online/wp-content/uploads/2026/05/OBLONG-—-Oversized-Tortoise-6-image.webp',
Triangle:'https://profreetools.online/wp-content/uploads/2026/05/TRIANGLE-—-Geometric-Gold-7-image.webp'
};
var SHAPE_ORDER = ['Oval','Round','Square','Heart','Diamond','Oblong','Triangle'];
var SHAPE_SUB = {Oval:'Cat-Eye Rose Gold',Round:'Pink Retro Round',Square:'Bold Purple',Heart:'Butterfly Coral',Diamond:'Rimless Rose',Oblong:'Oversized Tortoise',Triangle:'Gold Flower'};
var FRAME_TUNE = {Oval:{wMul:2.55,yOff:-0.05},Round:{wMul:2.60,yOff:-0.04},Square:{wMul:2.58,yOff:-0.05},Heart:{wMul:2.55,yOff:-0.06},Diamond:{wMul:2.50,yOff:-0.05},Oblong:{wMul:2.70,yOff:-0.05},Triangle:{wMul:2.60,yOff:-0.06}};
var SCIENCE = {
Oval: 'Your face shape is Oval — the most versatile profile. Our analyzer recommends elegant Cat-Eye frames with rose gold that celebrate your natural symmetry.',
Round: 'Your face shape is Round with soft full cheeks. Our analyzer recommends Pink Round Retro frames that introduce definition and elegantly lengthen your face.',
Square: 'Your face shape is Square with a strong jawline. Our analyzer recommends Bold Purple frames whose curves beautifully soften your angular features.',
Heart: 'Your face shape is Heart with a wide forehead. Our analyzer recommends Butterfly Coral frames to add warmth and balance to your lower face.',
Diamond: 'Your face shape is Diamond with prominent cheekbones. Our analyzer recommends Rimless Rose frames to highlight your brow line beautifully.',
Oblong: 'Your face shape is Oblong with greater length than width. Our analyzer recommends Oversized Tortoise frames to add glamorous horizontal balance.',
Triangle:'Your face shape is Triangle with a broader jaw. Our analyzer recommends Gold Flower geometric frames to draw attention beautifully upward.'
};
var state = {naturalW:0,naturalH:0,displayedW:0,displayedH:0,photoOffsetX:0,photoOffsetY:0,landmarks:null,detectedShape:'Oval',activeShape:'Oval',shapeScores:{},modelsReady:false,offsetX:0,offsetY:0,scale:1};
var KO_CACHE = {};
var camStream = null;
var initialized = false;
function $(id){ return document.getElementById(id); }
/* ── SMART LOADER — fetch blob to bypass CORS, then knockout ── */
function loadShapeAsset(shape){
return new Promise(function(resolve){
var url = SUNGLASSES_URL[shape];
if (!url){ resolve(''); return; }
if (KO_CACHE[shape]){ resolve(KO_CACHE[shape]); return; }
/* Use fetch+blob — bypasses CORS taint so canvas.getImageData works */
fetch(url + (url.indexOf('?')>-1?'&':'?') + 'ko=' + Date.now())
.then(function(r){ return r.blob(); })
.then(function(blob){
var blobUrl = URL.createObjectURL(blob);
var img = new Image();
img.onload = function(){
try {
var cv = document.createElement('canvas');
cv.width = img.naturalWidth; cv.height = img.naturalHeight;
var ctx = cv.getContext('2d');
ctx.drawImage(img, 0, 0);
var imageData = ctx.getImageData(0, 0, cv.width, cv.height);
var bg = detectBG(imageData, cv.width, cv.height);
/* Always knockout light backgrounds — uniform check removed */
knockoutBG(imageData, bg.r, bg.g, bg.b, 55, 40);
ctx.putImageData(imageData, 0, 0);
var dataUrl = cv.toDataURL('image/png');
KO_CACHE[shape] = dataUrl;
URL.revokeObjectURL(blobUrl);
resolve(dataUrl);
} catch(e){
URL.revokeObjectURL(blobUrl);
KO_CACHE[shape] = url;
resolve(url);
}
};
img.onerror = function(){ URL.revokeObjectURL(blobUrl); KO_CACHE[shape]=url; resolve(url); };
img.src = blobUrl;
})
.catch(function(){
/* fetch failed — fallback to direct img load */
var img2 = new Image();
img2.onload = function(){
try {
var cv2 = document.createElement('canvas');
cv2.width = img2.naturalWidth; cv2.height = img2.naturalHeight;
var ctx2 = cv2.getContext('2d');
ctx2.drawImage(img2, 0, 0);
var id2 = ctx2.getImageData(0, 0, cv2.width, cv2.height);
var bg2 = detectBG(id2, cv2.width, cv2.height);
knockoutBG(id2, bg2.r, bg2.g, bg2.b, 55, 40);
ctx2.putImageData(id2, 0, 0);
var du2 = cv2.toDataURL('image/png');
KO_CACHE[shape] = du2; resolve(du2);
} catch(e2){ KO_CACHE[shape]=url; resolve(url); }
};
img2.onerror = function(){ KO_CACHE[shape]=url; resolve(url); };
img2.crossOrigin='anonymous';
img2.src = url;
});
});
}
function detectBG(d, W, H){
var data = d.data;
var pts = [
[2,2],[W-3,2],[2,H-3],[W-3,H-3],
[Math.floor(W/4),2],[Math.floor(W/2),2],[Math.floor(3*W/4),2],
[Math.floor(W/4),H-3],[Math.floor(W/2),H-3],[Math.floor(3*W/4),H-3],
[2,Math.floor(H/2)],[W-3,Math.floor(H/2)]
];
var rs=[],gs=[],bs=[];
for(var i=0;imaxDev)maxDev=dev;
}
/* Always return avg color — knockout decides based on brightness */
var brightness = (avgR*0.299 + avgG*0.587 + avgB*0.114);
return {uniform: brightness > 160, r:avgR, g:avgG, b:avgB};
}
function knockoutBG(d, tr, tg, tb, cutoff, feather){
/* FLOOD-FILL from edges only — preserves frame interior colors!
Only removes background pixels CONNECTED to edges.
Frame interior (even if light/similar color) stays intact. */
var data=d.data, W=d.width, H=d.height;
var visited=new Uint8Array(W*H);
var queue=[];
/* Seed: all edge pixels */
for(var x=0;x235 && r>220 && g>220 && b>220);
return dist0) queue.push(idx-1);
if(px0) queue.push(idx-W);
if(py0 && data[(fi-1)*4+3]===0) transNb++;
if(fx0 && data[(fi-W)*4+3]===0) transNb++;
if(fy0){
data[fi*4+3]=Math.round(data[fi*4+3]*(1-transNb*0.18));
}
}
}
/* ── PILLS ── */
function renderPills(){
var track = $('fssfShapeTrack');
if (!track) return;
var html = '';
for (var i=0;i'+
''+s+''+SHAPE_SUB[s]+'';
}
track.innerHTML = html;
var pills = track.querySelectorAll('.fssf-shape-pill');
for (var j=0;j'+s+'0.0%
';
}
el.innerHTML = html;
}
function updateBars(scores){
var el = $('fssfBars');
if (!el) return;
var rows = el.querySelectorAll('.fssf-bar-row');
for (var i=0;i0?(match/top):1));
var sym=Math.max(0.62,Math.min(0.96,0.70+match*0.30));
var prop=Math.max(0.60,Math.min(0.93,0.66+align*0.30));
var ov=Math.max(0.55,Math.min(0.94,sym*0.45+prop*0.35+match*0.20));
animateGauge('fssfG1','fssfG1V',sym);
animateGauge('fssfG2','fssfG2V',prop);
animateGauge('fssfG3','fssfG3V',ov);
}
/* ── FACE API ── */
var MODEL_URL = 'https://cdn.jsdelivr.net/npm/face-api.js@0.22.2/weights';
var modelLoadPromise = null;
function loadModels(){
if (modelLoadPromise) return modelLoadPromise;
modelLoadPromise = new Promise(function(resolve){
if (typeof faceapi !== 'undefined'){ startML(resolve); return; }
var s=document.createElement('script');
s.src='https://cdn.jsdelivr.net/npm/face-api.js@0.22.2/dist/face-api.min.js';
s.async=true; s.setAttribute('data-fssf','1');
s.onload=function(){ startML(resolve); };
s.onerror=function(){ resolve(false); };
document.head.appendChild(s);
});
return modelLoadPromise;
}
function startML(resolve){
try {
Promise.all([faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL),faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL)])
.then(function(){ state.modelsReady=true; resolve(true); }).catch(function(){ resolve(false); });
} catch(e){ resolve(false); }
}
function detectFace(){
return new Promise(function(resolve){
var photo=$('fssfPhoto');
if (!state.modelsReady||typeof faceapi==='undefined'||!photo){ resolve(null); return; }
try {
faceapi.detectSingleFace(photo,new faceapi.TinyFaceDetectorOptions({inputSize:320,scoreThreshold:0.45}))
.withFaceLandmarks().then(function(res){
if (!res){ resolve(null); return; }
try {
var lm=res.landmarks, le=centroid(lm.getLeftEye()), re=centroid(lm.getRightEye());
var dx=re.x-le.x, dy=re.y-le.y, ipd=Math.sqrt(dx*dx+dy*dy), ang=Math.atan2(dy,dx)*180/Math.PI;
var nosePts=lm.getNose(), noseTip=nosePts[nosePts.length-1];
var emX=(le.x+re.x)/2, yaw=Math.max(-1,Math.min(1,(noseTip.x-emX)/(ipd*0.6)));
resolve({leftEye:le,rightEye:re,ipdPx:ipd,angleDeg:ang,centerX:emX,centerY:(le.y+re.y)/2,yawNorm:yaw,allLandmarks:lm.positions,faceBox:res.detection.box});
} catch(e){ resolve(null); }
}).catch(function(){ resolve(null); });
} catch(e){ resolve(null); }
});
}
function centroid(pts){ var x=0,y=0; for(var i=0;i1.02?0.15:0),Round:1-Math.abs(lw-1.05)*1.8+(ja>150?0.20:0),Square:1-Math.abs(lw-1.10)*1.6+(ja<135?0.30:0)+(Math.abs(fj-1)<0.10?0.15:0),Heart:1-Math.abs(lw-1.30)*1.5+(fj>1.12?0.30:0),Diamond:1-Math.abs(lw-1.35)*1.5+(cf>1.15?0.25:0),Oblong:1-Math.abs(lw-1.60)*1.4+(lw>1.50?0.30:0),Triangle:1-Math.abs(lw-1.30)*1.5+(fj<0.92?0.30:0)};
var sum=0; for(var k in raw){if(raw[k]<0.02)raw[k]=0.02;sum+=raw[k];}
var out={}; for(var k2 in raw){out[k2]=raw[k2]/sum;} return out;
} catch(e){ return {Oval:0.62,Round:0.10,Square:0.06,Heart:0.10,Diamond:0.06,Oblong:0.04,Triangle:0.02}; }
}
function pickTop(scores){ var best='Oval',val=-1; for(var k in scores){if(scores[k]>val){val=scores[k];best=k;}} return best; }
/* ── MEASURE + PLACE ── */
function measureDisplay(){
var box=$('fssfImageBox');
if (!state.naturalW||!state.naturalH||!box) return;
var r=box.getBoundingClientRect(), bw=r.width, bh=r.height;
if (!bw||!bh) return;
var ratio=state.naturalW/state.naturalH, dw, dh;
if (bw/bh>ratio){dh=bh;dw=dh*ratio;}else{dw=bw;dh=dw/ratio;}
state.displayedW=dw;state.displayedH=dh;state.photoOffsetX=(bw-dw)/2;state.photoOffsetY=(bh-dh)/2;
}
function placeGlasses(){
var g=$('fssfGlassesImg');
if (!g||!state.naturalW||!g.src||!state.landmarks) return;
var lm=state.landmarks, sc=state.displayedW/state.naturalW;
var cx=state.photoOffsetX+lm.centerX*sc, cy=state.photoOffsetY+lm.centerY*sc;
var ipd=lm.ipdPx*sc, tune=FRAME_TUNE[state.activeShape]||{wMul:2.55,yOff:-0.05};
var fw=ipd*tune.wMul*state.scale, px=cx+state.offsetX, py=cy+ipd*tune.yOff+state.offsetY;
var yaw=lm.yawNorm||0, sx=Math.max(0.78,1-Math.abs(yaw)*0.22);
g.style.width=fw.toFixed(1)+'px'; g.style.height='auto'; g.style.left='0'; g.style.top='0';
g.style.transform='translate3d('+(px-fw/2).toFixed(1)+'px,'+(py).toFixed(1)+'px,0) translateY(-50%) rotate3d(0,0,1,'+lm.angleDeg.toFixed(2)+'deg) rotate3d(0,1,0,'+(yaw*16).toFixed(2)+'deg) scaleX('+sx.toFixed(3)+')';
g.classList.add('ready');
}
function showScan(on){ var s=$('fssfScanOverlay'); if(!s)return; if(on)s.classList.add('show'); else s.classList.remove('show'); }
/* ── v12 SCAN FIX — 1.5s GUARANTEED STOP ── */
function onPhotoLoaded(){
var photo=$('fssfPhoto');
if (!photo) return;
try{state.naturalW=photo.naturalWidth||photo.width;state.naturalH=photo.naturalHeight||photo.height;}catch(_){}
requestAnimationFrame(function(){
measureDisplay();
showScan(true);
/* GUARANTEED STOP — completely independent of loadModels() */
setTimeout(function(){
showScan(false);
var lm=fallbackLandmarks();
state.landmarks=lm;
state.shapeScores=scoreShapes(lm);
state.detectedShape=pickTop(state.shapeScores);
var nm=$('fssfShapeName'); if(nm) nm.textContent=state.detectedShape;
updateBars(state.shapeScores);
setActiveShape(state.detectedShape,false);
computeMetrics(state.detectedShape);
placeGlasses();
if(window.innerWidth<980){var h=$('fssfDragHint');if(h){h.classList.add('show');setTimeout(function(){h.classList.remove('show');},3000);}}
/* Background AI — silently updates if fast enough */
loadModels().then(function(ok){
if(!ok) return;
detectFace().then(function(lm2){
if(!lm2) return;
state.landmarks=lm2;state.shapeScores=scoreShapes(lm2);state.detectedShape=pickTop(state.shapeScores);
var nm2=$('fssfShapeName');if(nm2)nm2.textContent=state.detectedShape;
updateBars(state.shapeScores);setActiveShape(state.detectedShape,false);computeMetrics(state.detectedShape);placeGlasses();
});
});
}, 1500);
});
}
function loadPhotoFromSrc(src){
var photo=$('fssfPhoto');
if (!photo) return;
photo.onload=function(){
var hero=$('fssfHeroSplit'),stage=$('fssfStage');
if(hero)hero.classList.add('hide');
if(stage)stage.classList.add('show');
state.offsetX=0;state.offsetY=0;state.scale=1;
requestAnimationFrame(function(){requestAnimationFrame(onPhotoLoaded);});
};
photo.onerror=function(){alert('Could not load image. Please try another photo.');};
photo.src=src;
}
/* ── UPLOAD — FIXED ── */
function bindUpload(){
var upBtn=$('fssfUpBtn'), fileInput=$('fssfFile');
if (!upBtn||!fileInput){ console.warn('[FSSF] Upload buttons not found'); return; }
/* Click handler */
upBtn.addEventListener('click', function(e){
e.preventDefault();
e.stopPropagation();
fileInput.click();
}, false);
/* Touch handler for mobile */
upBtn.addEventListener('touchend', function(e){
e.preventDefault();
fileInput.click();
}, false);
/* File change */
fileInput.addEventListener('change', function(e){
var f=e.target.files&&e.target.files[0];
if (!f) return;
if (!/^image\//.test(f.type)){ alert('Please choose an image file.'); return; }
var reader=new FileReader();
reader.onload=function(ev){ loadPhotoFromSrc(ev.target.result); };
reader.readAsDataURL(f);
fileInput.value='';
}, false);
}
/* ── CAMERA ── */
function bindCam(){
var cb=$('fssfCamBtn'),cc=$('fssfCamCancel'),cs=$('fssfCamShot'),cm=$('fssfCamModal');
if (cb){ cb.addEventListener('click',function(e){e.preventDefault();openCam();},false); cb.addEventListener('touchend',function(e){e.preventDefault();openCam();},false); }
if (cc) cc.addEventListener('click',closeCam,false);
if (cs) cs.addEventListener('click',captureCam,false);
if (cm) cm.addEventListener('click',function(e){if(e.target===cm)closeCam();},false);
}
function openCam(){
var modal=$('fssfCamModal'),st=$('fssfCamStatus'),shot=$('fssfCamShot'),ovl=$('fssfCamOvl'),vid=$('fssfCamVideo');
if (!modal) return;
modal.classList.add('show');
if(st){st.textContent='Starting camera...';st.className='fssf-cam-status';}
if(shot){shot.disabled=true;shot.classList.remove('ready');}
if(!navigator.mediaDevices||!navigator.mediaDevices.getUserMedia){if(st){st.textContent='Camera not supported on this device.';st.classList.add('bad');}return;}
navigator.mediaDevices.getUserMedia({video:{facingMode:'user',width:{ideal:1280},height:{ideal:1280}},audio:false})
.then(function(stream){
camStream=stream;
if(vid)vid.srcObject=stream;
if(st){st.textContent='Align your face in the rose oval';st.className='fssf-cam-status';}
setTimeout(function(){
if(st){st.textContent='Perfect! Tap Capture now';st.className='fssf-cam-status good';}
if(ovl)ovl.classList.add('good');
if(shot){shot.disabled=false;shot.classList.add('ready');}
},1500);
}).catch(function(){if(st){st.textContent='Camera access denied. Please allow camera.';st.className='fssf-cam-status bad';}});
}
function closeCam(){
var modal=$('fssfCamModal'),vid=$('fssfCamVideo'),ovl=$('fssfCamOvl');
if(modal)modal.classList.remove('show');
if(camStream){try{camStream.getTracks().forEach(function(t){t.stop();});}catch(_){}camStream=null;}
if(vid)vid.srcObject=null;
if(ovl)ovl.classList.remove('good');
}
function captureCam(){
var vid=$('fssfCamVideo');
if(!vid||!vid.videoWidth)return;
var cv=document.createElement('canvas');
cv.width=vid.videoWidth;cv.height=vid.videoHeight;
var ctx=cv.getContext('2d');
ctx.translate(vid.videoWidth,0);ctx.scale(-1,1);
ctx.drawImage(vid,0,0,vid.videoWidth,vid.videoHeight);
closeCam();
loadPhotoFromSrc(cv.toDataURL('image/jpeg',0.92));
}
/* ── ADJUST ── */
function bindAdjust(){
var zone=$('fssfAdjust');
if (!zone) return;
var btns=zone.querySelectorAll('.fssf-adj-btn');
function doAdjust(act){
var s=12;
if(state.landmarks&&state.displayedW&&state.naturalW) s=Math.max(6,state.landmarks.ipdPx*(state.displayedW/state.naturalW)*0.06);
if(act==='up')state.offsetY-=s;
else if(act==='down')state.offsetY+=s;
else if(act==='left')state.offsetX-=s;
else if(act==='right')state.offsetX+=s;
else if(act==='bigger')state.scale=Math.min(2.0,state.scale+0.06);
else if(act==='smaller')state.scale=Math.max(0.1,state.scale-0.06);
placeGlasses();
}
for(var i=0;i0)state.scale=Math.max(0.1,Math.min(2.5,state.scale*(nd/t.lastDist)));t.lastDist=nd;placeGlasses();e.preventDefault();}
},{passive:false});
box.addEventListener('touchend',function(e){
if(e.touches.length===0){t.dragging=false;t.pinching=false;}
else if(e.touches.length===1){t.pinching=false;t.dragging=true;t.lastX=e.touches[0].clientX;t.lastY=e.touches[0].clientY;t.lastDist=0;}
},{passive:true});
box.addEventListener('touchcancel',function(){t.dragging=false;t.pinching=false;},{passive:true});
}
/* ── REDO ── */
function bindRedo(){
var btn=$('fssfRedo');
if(!btn)return;
btn.addEventListener('click',function(){
var hero=$('fssfHeroSplit'),stage=$('fssfStage'),photo=$('fssfPhoto'),g=$('fssfGlassesImg');
if(stage)stage.classList.remove('show');
if(hero)hero.classList.remove('hide');
if(photo)photo.src='';
if(g){g.src='';g.classList.remove('ready');}
state.landmarks=null;state.shapeScores={};state.offsetX=0;state.offsetY=0;state.scale=1;
},false);
}
/* ── DOWNLOAD ── */
function bindDownload(){
var btn=$('fssfDownloadBtn');
if(!btn)return;
btn.addEventListener('click',function(){
var photo=$('fssfPhoto'),gi=$('fssfGlassesImg'),box=$('fssfImageBox');
if(!photo||!photo.src||photo.src===window.location.href){alert('Please upload a photo first!');return;}
btn.textContent='Preparing...';btn.style.opacity='0.7';
var br=box.getBoundingClientRect(),dpr=window.devicePixelRatio||1;
var cv=document.createElement('canvas');
cv.width=br.width*dpr;cv.height=br.height*dpr;
var ctx=cv.getContext('2d');ctx.scale(dpr,dpr);
ctx.fillStyle='#fff5f7';ctx.fillRect(0,0,br.width,br.height);
try{ctx.drawImage(photo,state.photoOffsetX,state.photoOffsetY,state.displayedW,state.displayedH);}catch(e){}
/* CRITICAL FIX: Use KO_CACHE (knockout-processed) image, NOT raw URL!
Raw URL has white background — knockout-processed is transparent. */
var processedSrc = KO_CACHE[state.activeShape] || SUNGLASSES_URL[state.activeShape];
if(processedSrc&&gi&&gi.classList.contains('ready')){
var tmp=new Image();
tmp.onload=function(){
try{var gr=gi.getBoundingClientRect();ctx.drawImage(tmp,gr.left-br.left,gr.top-br.top,gr.width,gr.height);}catch(e){}
saveCanvas(ctx,cv,br,btn);
};
tmp.onerror=function(){saveCanvas(ctx,cv,br,btn);};
/* data: URLs (knockout-processed) load without CORS issues */
if(processedSrc.indexOf('data:')!==0){
tmp.crossOrigin='anonymous';
}
tmp.src=processedSrc;
} else saveCanvas(ctx,cv,br,btn);
},false);
}
function saveCanvas(ctx,cv,br,btn){
ctx.font='bold 12px Arial,sans-serif';ctx.fillStyle='rgba(244,63,94,0.85)';ctx.textAlign='right';ctx.fillText('profreetools.online',br.width-12,br.height-12);
ctx.font='bold 13px Arial,sans-serif';ctx.fillStyle='rgba(190,18,57,0.95)';ctx.textAlign='left';ctx.fillText('Face: '+state.detectedShape,12,26);
function showDone(){ btn.style.opacity=''; btn.innerHTML='✓ Saved!'; btn.style.background='linear-gradient(135deg,#10b981,#059669)'; setTimeout(function(){btn.innerHTML='⬇ Download My Look';btn.style.background='';},3000); }
try{
/* toBlob saves directly to gallery on iOS/Android */
if(cv.toBlob){
cv.toBlob(function(blob){
var bUrl=URL.createObjectURL(blob);
var dl=document.createElement('a');
dl.href=bUrl;
dl.download='my-look-profreetools.jpg';
dl.style.display='none';
document.body.appendChild(dl);dl.click();
setTimeout(function(){document.body.removeChild(dl);URL.revokeObjectURL(bUrl);},400);
showDone();
},'image/jpeg',0.93);
} else {
var dl=document.createElement('a');
dl.setAttribute('href',cv.toDataURL('image/jpeg',0.93));
dl.setAttribute('download','my-look-profreetools.jpg');
dl.style.display='none';document.body.appendChild(dl);dl.click();
setTimeout(function(){document.body.removeChild(dl);},200);
showDone();
}
}catch(e){btn.style.opacity='';btn.innerHTML='📷 Screenshot to Save';setTimeout(function(){btn.innerHTML='⬇ Download My Look';},3000);}
}
/* ── RESIZE ── */
function bindResize(){
var t=null;
window.addEventListener('resize',function(){
var stage=$('fssfStage');if(!stage||!stage.classList.contains('show'))return;
clearTimeout(t);t=setTimeout(function(){measureDisplay();placeGlasses();},120);
},false);
}
/* ── INIT ── */
function init(){
if (initialized) return;
if (!$('fssfShapeTrack')||!$('fssfUpBtn')||!$('fssfCamBtn')) return;
initialized = true;
renderPills();
buildBars();
bindUpload();
bindCam();
bindRedo();
bindDownload();
bindAdjust();
bindTouchGestures();
bindResize();
console.log('[FSSF] v2 initialized — all buttons active.');
}
/* Multiple init attempts for WordPress late DOM injection */
if (document.readyState==='loading'){
document.addEventListener('DOMContentLoaded',init,false);
} else {
init();
}
var _tries=0, _iv=setInterval(function(){
_tries++;
if (initialized||_tries>40){ clearInterval(_iv); return; }
init();
},150);
})();