Suchergebnis

Suchergebnisse für: daikin

Filter
console.log('PropertyWizard v1.3.0 loaded (shadow+iframe+picker)', cfg); const toIdSet=s=>new Set(String(s||'').split(',').map(x=>x.trim()).filter(v=>/^\d+$/.test(v)).map(Number)); const isUrl=v=>/^https?:\/\//i.test(String(v||'')); const labelFor=pid=>cfg.labelMap[pid+'_de']||cfg.labelMap[pid+'_en']||('Property '+pid); const once=(el,k)=>{const key='__pw_'+k; if(el[key])return false; el[key]=true; return true;}; function injectStyles(doc){ try{ const s=doc.createElement('style'); s.textContent=CSS_INJECT; (doc.head||doc.documentElement).appendChild(s); }catch(e){} } function getDocs(){ const docs=[document]; document.querySelectorAll('iframe').forEach(f=>{ try{ if(f.contentDocument){ docs.push(f.contentDocument); injectStyles(f.contentDocument); } }catch(e){ /* cross-origin, ignore */ } }); return docs; } function getShards(doc){ const shards=[doc]; // collect shadow roots const all = Array.from(doc.querySelectorAll('*')); for(let i=0;is.trim()).filter(Boolean); const genericAttrSelectors=['[data-variation-id]','[data-variationid]','[data-variationId]','[data-item-id]','[data-vid]','[data-variation]','[data-product-id]','[data-productid]']; const basketSelectors=['.basket-item','.basket-list-item','[data-basket-item]']; const schemaSelectors=['[itemscope][itemtype*=\"Product\"]','[itemtype*=\"schema.org/Product\"]']; const linkSelectors=['a[href*=\"/item/\"]','a[href*=\"/artikel/\"]','a[href*=\"/produkt/\"]','a[href*=\"/p/\"]']; function overrideFor(ctxKey){ switch(ctxKey){ case 'category': return ""; case 'search': return ""; case 'crossSelling': return ""; case 'slider': return ""; case 'basket': return ""; } return ""; } function nearestCard(node){ let el=node; let depth=0; while(el && depth<6){ if(el.className && /product|item|tile|card|box|cmp|grid|list/i.test(String(el.className))) return el; el = el.parentElement; depth++; } return node.parentElement || node; } function autoDiscoverRoots(shards){ const candidates=[]; for(const sh of shards){ const kids = Array.from(sh.querySelectorAll(':scope > *')); // top-level children in this shard // If no direct children container, scan elements with many children const scan = kids.length? kids : Array.from(sh.querySelectorAll('*')).filter(e=>e.children && e.children.length>=6).slice(0,200); for(const el of scan){ if(!el.children || el.children.length<6) continue; const groups = new Map(); const arr = Array.from(el.children); arr.forEach(k=>{ const tag = k.tagName.toLowerCase(); const cls = Array.from(k.classList).sort().join('.'); const key = tag+(cls?'.'+cls:''); if(!groups.has(key)) groups.set(key, []); groups.get(key).push(k); }); groups.forEach(list=>{ if(list.length>=6){ const score = list.reduce((acc,k)=>acc+ (k.querySelector('a[href],img')?1:0),0) / list.length; candidates.push({list, score}); } }); } } if(!candidates.length) return []; candidates.sort((a,b)=> b.score - a.score || b.list.length - a.list.length); return candidates[0].list; } function detectRoots(ctxKey){ const docs=getDocs(); let by='none'; let nodes=[]; for(const doc of docs){ const shards=getShards(doc); const ov = overrideFor(ctxKey).trim(); let found=[]; if(ctxKey==='basket'){ found = qAll(shards, basketSelectors); if(found.length){ by = (doc !== document ? 'iframe' : 'basket'); nodes.push(...found); continue; } } if(ov){ found=qAll(shards, [ov]); if(found.length){ by='override'; nodes.push(...found); continue; } } if(defaultTileSelectors.length){ found=qAll(shards, defaultTileSelectors); if(found.length){ by='classes'; nodes.push(...found); continue; } } found = qAll(shards, genericAttrSelectors).map(nearestCard); if(found.length){ by='data-*'; nodes.push(...found); continue; } found = qAll(shards, schemaSelectors).map(nearestCard); if(found.length){ by='schema'; nodes.push(...found); continue; } found = qAll(shards, linkSelectors).map(nearestCard); if(found.length){ by='links'; nodes.push(...found); continue; } // auto found = autoDiscoverRoots(shards); if(found && found.length){ by='auto'; nodes.push(...found); continue; } } nodes = [...new Set(nodes)]; return {by, nodes}; } function renderBadge(doc, pid,value){ const w=doc.createElement('span');w.className='pw-badge pw-muted'; const i=doc.createElement('span');i.className='pw-icon';i.innerHTML='📄';w.appendChild(i); const c=doc.createElement(isUrl(value)?'a':'span'); if(isUrl(value)){c.href=value;c.target='_blank';c.rel='noopener';} c.textContent=labelFor(String(pid)); w.appendChild(c); return w; } function addTileMarker(tile){ if(!cfg.debug.showTileMarkers) return; if(tile.querySelector('.pw-marker')) return; const doc=tile.ownerDocument||document; const m=doc.createElement('span'); m.className='pw-marker'; m.textContent='PW'; if(!tile.style.position) tile.style.position='relative'; tile.appendChild(m); } function pickTarget(tile,placement,customSelector){ const sel=(r,s)=>r.querySelector(s); if(customSelector){const n=sel(tile,customSelector); if(n) return n;} const cand={afterPrice:['[data-testing=\"current-price\"]','.price','.price-box','.price-wrapper .text-muted','.cmp-price'], afterTitle:['.item-title a','.item-title','h3 a','h3','.title a','.title'], afterImage:['.thumb a','.thumb img','.item-image a','.item-image img','.cmp-image img']}[placement]||['.item-title','.title']; for(const s of cand){const n=sel(tile,s); if(n) return n;} return tile.firstElementChild||tile; } function extractId(tile){ const a = tile.querySelector('a[href]'); const dataIds = [ tile.getAttribute('data-variation-id'), tile.getAttribute('data-variationid'), tile.getAttribute('data-item-id'), tile.getAttribute('data-product-id'), tile.dataset?.variationId, tile.dataset?.itemId, tile.dataset?.productId ].filter(Boolean); if(dataIds.length) return String(dataIds[0]).match(/\d+/)?.[0] || null; if(a){ const m = String(a.getAttribute('href')||'').match(/(\d{4,})/g); if(m && m.length) return m[m.length-1]; } return null; } function timeoutPromise(ms){ return new Promise((_,rej)=>setTimeout(()=>rej(new Error('timeout')),ms)); } async function fetchJson(url, useCred){ const opt = {headers: {'Accept':'application/json'}}; if(useCred) opt.credentials = 'same-origin'; const res = await Promise.race([fetch(url,opt), timeoutPromise(cfg.apiFetch.timeoutMs)]); if(!res.ok) throw new Error('HTTP '+res.status); return res.json().catch(()=> ({})); } async function fetchPropsById(id){ for(const p of cfg.apiFetch.paths){ const url = p.replace('{id}', encodeURIComponent(id)); try{ const j = await fetchJson(url, cfg.apiFetch.useCredentials); if(Array.isArray(j) && j.length && (j[0]?.property || j[0]?.texts || j[0]?.id)) return j; const keys = cfg.apiFetch.propPath.split('.'); let cur=j; for(const k of keys){ if(cur && typeof cur==='object' && k in cur) cur=cur[k]; else { cur=null; break; } } if(Array.isArray(cur)) return cur; if(j && j.variation && Array.isArray(j.variation.properties)) return j.variation.properties; }catch(e){} } return null; } function ensureOverlay(doc){ if(!cfg.debug.enabled || !cfg.debug.overlay) return null; if(doc!==document) return null; // overlay nur im Top-Dokument let el=document.getElementById('pw-overlay'); if(!el){ el=document.createElement('div'); el.id='pw-overlay'; el.className='pw-overlay'; el.title='PW Overlay — Alt+R: Root-Highlight, Alt+P: DOM-Picker'; document.body.appendChild(el); } return el; } function domPickerActivate(){ let last=null; function over(e){ if(last) last.classList.remove('pw-pick'); last = e.target; last.classList.add('pw-pick'); e.preventDefault(); e.stopPropagation(); } function out(e){ if(last){ last.classList.remove('pw-pick'); last=null; } } function click(e){ e.preventDefault(); e.stopPropagation(); if(!last) return; // compute simple selector path const path=[]; let el=last; let depth=0; while(el && el!==document.body && depth<5){ const id = el.id ? '#'+el.id : ''; const cls = (el.className && typeof el.className==='string') ? '.'+el.className.split(/\s+/).filter(Boolean).slice(0,2).join('.') : ''; path.unshift(el.tagName.toLowerCase()+id+cls); el=el.parentElement; depth++; } const selector = path.join(' > '); localStorage.setItem('pw.selectorOverride.category', selector); alert('PW: Root-Selector gespeichert:\n'+selector+'\nReload, um ihn zu verwenden.'); deactivate(); } function key(e){ if(e.key==='Escape'){ deactivate(); } } function deactivate(){ document.removeEventListener('mousemove', over, true); document.removeEventListener('mouseout', out, true); document.removeEventListener('click', click, true); document.removeEventListener('keydown', key, true); if(last){ last.classList.remove('pw-pick'); last=null; } } document.addEventListener('mousemove', over, true); document.addEventListener('mouseout', out, true); document.addEventListener('click', click, true); document.addEventListener('keydown', key, true); } function processContext(ctxKey){ const ctx=cfg.contexts[ctxKey]; // merge in local override from picker const lsOverride = localStorage.getItem('pw.selectorOverride.'+ctxKey); if(lsOverride && !overrideFor(ctxKey)) { cfg.themeHints.knownTileClasses = lsOverride; } const ids = toIdSet(ctx.propertyIds); const allowNoIds = true; if(!ids.size && !allowNoIds) return; // bei Force trotzdem laufen const bucket=detectRoots(ctxKey); const roots=[...new Set(bucket.nodes)]; let processed=0, matchedCount=0, fetchedOk=0, fetchedFail=0, idx=0; const overlay=ensureOverlay(document); const upd=()=>{ if(overlay) overlay.innerHTML = 'PW ctx='+ctxKey+' by='+bucket.by+' roots='+roots.length+' matched='+matchedCount+' fetched='+fetchedOk+'/'+(fetchedOk+fetchedFail); }; upd(); if(cfg.debug.enabled) console.log('[PropertyWizard] %s by=%s roots=%d', ctxKey, bucket.by, roots.length); if(cfg.debug.picker){ document.addEventListener('keydown', (ev)=>{ if(ev.altKey && (ev.key==='p' || ev.key==='P')){ domPickerActivate(); } }); } async function processTile(tile){ addTileMarker(tile); const doc = tile.ownerDocument || document; let props=null; const s=tile.querySelector('script[type=\"application/json\"]'); if(s){ try{ const o=JSON.parse(s.textContent||'{}'); if(Array.isArray(o?.properties)) props=o.properties; if(Array.isArray(o?.variationProperties)) props=o.variationProperties; }catch(e){} } if(!props && cfg.apiFetch.enabled){ const id = extractId(tile); if(id){ try{ props = await fetchPropsById(id); if(Array.isArray(props)&&props.length){ tile.__pw_props=props; fetchedOk++; } else { fetchedFail++; } } catch(e){ fetchedFail++; } upd(); } } let matched=false; if(Array.isArray(props) && props.length){ const m = props.find(p=>{ const pid=p?.property?.id||p?.id||p?.propertyId; const val=p?.texts?.value||p?.value||p?.selection?.name||p?.valueText; return ids.has(Number(pid)) && val; }); if(m){ matched=true; matchedCount++; const pid=m.property?.id||m.id||m.propertyId; const val=m.texts?.value||m.value||m.selection?.name||m.valueText; const target=tile.querySelector(ctx.selector) || pickTarget(tile,ctx.placement,''); const badge=renderBadge(doc, pid,val); if(!(target && target.parentNode && target.parentNode.insertBefore(badge, target.nextSibling))){ tile.appendChild(badge); } } } if(!matched && cfg.debug.forceRender){ const target=tile.querySelector(ctx.selector) || pickTarget(tile,ctx.placement,''); const b=renderBadge(doc, 0,''); b.textContent='PW (force)'; if(!(target && target.parentNode && target.parentNode.insertBefore(b, target.nextSibling))){ tile.appendChild(b); } } } async function run(){ const conc = 4; let running=0; let qIndex=0; return new Promise(resolve=>{ const kick=()=>{ while(running{ running--; processed++; kick(); if(running===0 && (qIndex>=roots.length || processed>=cfg.performance.maxTiles)) resolve(); }); } }; kick(); }); } run(); const mo=new MutationObserver(()=>{ const again=detectRoots(ctxKey); const newRoots=[...new Set(again.nodes)]; if(newRoots.length!==roots.length){ roots.length=0; roots.push(...newRoots); upd(); run(); } }); mo.observe(document, {subtree:true, childList:true}); window.addEventListener('load', ()=> setTimeout(()=>{ const again=detectRoots(ctxKey); if(again.nodes.length!==roots.length){ roots.length=0; roots.push(...again.nodes); upd(); run(); } }, 500)); ['popstate','pushstate','replacestate'].forEach(evt=> window.addEventListener(evt, ()=> setTimeout(()=>{ const again=detectRoots(ctxKey); if(again.nodes.length!==roots.length){ roots.length=0; roots.push(...again.nodes); upd(); run(); } }, 300))); } const onReady=fn=>(document.readyState==='loading')?document.addEventListener('DOMContentLoaded',fn):fn(); onReady(()=>['category','search','crossSelling','slider','basket'].forEach(processContext)); })();