1(function(){ 2 if(window.__cpmViewer)return; window.__cpmViewer=true; 3 var v,img,count,title,cap,credit,link,panel,bT,bC,bCr,bL,items=[],cur=0,creditLabel='Foto: ',linkDefault='Zum Artikel',cpmvSlug='',cpmvName=''; 4 // v0.41.0: privacy-light view beacon. Fires at most once per slug+kind per 5 // page load. No cookies, no IDs -- the server only keeps aggregate counts. 6 // 7 // v0.115.0: the archive's address is BAKED IN at serve time (https://archiv.cpm-verlag.com). 8 // It used to be derived by stripping this script's own DAM path off its src, 9 // which silently broke when shortcode v2.9.0 started mirroring this file into 10 // each site's own wp-uploads (DAM-outage resilience, correct in itself): from 11 // a local path the pattern never matched, so every beacon posted to the 12 // script's own URL. The version string is a query parameter, so that wrong 13 // URL resolved to the .js file and answered 200 -- a silent success. Result: 14 // ~50k views/month in July, 3 in August, across all sites. The derivation 15 // stays as a fallback for a mirror written before this change. 16 // 17 // This does NOT make a host page depend on the archive: the beacon is 18 // fire-and-forget (sendBeacon, or fetch with keepalive+no-cors), runs after 19 // DOMContentLoaded, and every path is wrapped -- an unreachable archive costs 20 // the page nothing. The site keeps serving its own mirrored copy of this file. 21 var BASE=(function(){try{ 22 var b=window.CPM_DAM_BASE||'https://archiv.cpm-verlag.com'; 23 if(b&&b.indexOf('__CPM')!==0)return String(b).replace(/[/]+$/,''); 24 var s=(document.currentScript&&document.currentScript.src)||''; 25 var i=s.indexOf('/api/v1/embed/cpm-embed.js'); 26 return i>0?s.slice(0,i):''; 27 }catch(_){return '';}})(); 28 var beaconSent={}; 29 function beacon(slug,kind){ 30 if(!slug||!BASE)return;var key=slug+'|'+kind;if(beaconSent[key])return;beaconSent[key]=true; 31 var url=BASE+'/api/v1/collections/'+encodeURIComponent(slug)+'/view?kind='+kind; 32 try{if(navigator.sendBeacon){navigator.sendBeacon(url);} 33 else{fetch(url,{method:'POST',keepalive:true,mode:'no-cors'});}}catch(_){} 34 } 35 function beaconRenders(){ 36 try{document.querySelectorAll('[data-cpm-json][data-slug],.cpm-gallery-data[data-slug]').forEach(function(el){ 37 beacon(el.getAttribute('data-slug'),'render');});}catch(_){} 38 } 39 // v0.42.2: collection-name heading on each Laufband, top-left. We read the 40 // name from the band's existing aria-label (no snippet re-paste); a future 41 // snippet may set data-cpm-heading for a custom title, which wins. 42 function bandTitles(){ 43 try{document.querySelectorAll('.cpm-band').forEach(function(b){ 44 if(b.getAttribute('data-cpm-titled'))return; 45 var t=b.getAttribute('data-cpm-heading')||b.getAttribute('aria-label')||''; 46 t=(t||'').trim();if(!t||!b.parentNode)return; 47 b.setAttribute('data-cpm-titled','1'); 48 var el=document.createElement('div');el.className='cpm-band-title';el.textContent=t; 49 b.parentNode.insertBefore(el,b); // sits ABOVE the band, on the page 50 });}catch(_){} 51 } 52 function onReady(){beaconRenders();bandTitles();} 53 if(document.readyState!=='loading')onReady(); 54 else document.addEventListener('DOMContentLoaded',onReady); 55 // belt-and-suspenders: re-run after full load + a short delay, for themes 56 // that inject the band late. The existing-title check makes this idempotent. 57 window.addEventListener('load',bandTitles); 58 setTimeout(bandTitles,1500); 59 function labelFor(island){return island&&island.getAttribute('data-lang')==='en'?'Photo: ':'Foto: ';} 60 function linkLabelFor(island){return island&&island.getAttribute('data-lang')==='en'?'To the article':'Zum Artikel';} 61 var MAX='<svg viewBox="0 0 24 24"><path d="M4 9V4h5M20 9V4h-5M4 15v5h5M20 15v5h-5"/></svg>'; 62 var MIN='<svg viewBox="0 0 24 24"><path d="M9 4v5H4M15 4v5h5M9 20v-5H4M15 20v-5h5"/></svg>'; 63 var KPLAY='<svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>'; 64 // Align the right panel's title with the (vertically-centred) image's top 65 // edge. The image top is dynamic (depends on its aspect), so we sync it in JS 66 // and never go below the default 64px button clearance. Skipped on the mobile 67 // stacked layout and in full mode (panel hidden) -> CSS padding takes over. 68 function syncCaptionTop(){ 69 if(!v||!panel||!img||!v.classList.contains('open'))return; 70 if(window.innerWidth<=760||v.classList.contains('full')){panel.style.paddingTop='';return;} 71 var top=Math.round(img.getBoundingClientRect().top-v.getBoundingClientRect().top); 72 if(top>0)panel.style.paddingTop=Math.max(64,top)+'px'; 73 } 74 function build(){ 75 v=document.createElement('div');v.className='cpmv'; 76 v.innerHTML= 77 '<div class="cpmv-stage">' 78 +'<div class="cpmv-count"></div>' 79 +'<button class="cpmv-nav cpmv-prev" aria-label="Zurueck">‹</button>' 80 +'<img alt="">' 81 +'<button class="cpmv-nav cpmv-next" aria-label="Weiter">›</button>' 82 +'</div>' 83 +'<aside class="cpmv-panel"><h3 class="cpmv-title"></h3>' 84 +'<p class="cpmv-cap"></p>' 85 +'<a class="cpmv-link" target="_blank" rel="noopener"></a>' 86 +'<p class="cpmv-credit"></p></aside>' 87 +'<button class="cpmv-play" aria-label="Diashow">'+KPLAY+'</button>' 88 +'<button class="cpmv-max" aria-label="Vollbild">'+MAX+'</button>' 89 +'<button class="cpmv-x" aria-label="Schliessen">×</button>' 90 +'<div class="cpmv-bottom"><span class="bt"></span> <span class="bc"></span> <a class="bl" target="_blank" rel="noopener"></a> <span class="bcr c"></span></div>'; 91 document.body.appendChild(v); 92 img=v.querySelector('img');count=v.querySelector('.cpmv-count'); 93 title=v.querySelector('.cpmv-title');cap=v.querySelector('.cpmv-cap');credit=v.querySelector('.cpmv-credit');link=v.querySelector('.cpmv-link');panel=v.querySelector('.cpmv-panel'); 94 bT=v.querySelector('.cpmv-bottom .bt');bC=v.querySelector('.cpmv-bottom .bc');bCr=v.querySelector('.cpmv-bottom .bcr');bL=v.querySelector('.cpmv-bottom .bl'); 95 v.querySelector('.cpmv-prev').addEventListener('click',function(e){e.stopPropagation();show(cur-1);}); 96 v.querySelector('.cpmv-next').addEventListener('click',function(e){e.stopPropagation();show(cur+1);}); 97 v.querySelector('.cpmv-x').addEventListener('click',close); 98 v.querySelector('.cpmv-max').addEventListener('click',function(e){e.stopPropagation();toggleFull();}); 99 v.querySelector('.cpmv-play').addEventListener('click',function(e){e.stopPropagation();var its=items.slice();close();kStart(cpmvSlug,its,cpmvName);}); 100 v.querySelector('.cpmv-stage').addEventListener('click',function(e){if(e.target===e.currentTarget)close();}); 101 document.addEventListener('keydown',function(e){ 102 if(!v.classList.contains('open'))return; 103 if(e.key==='Escape'){ if(v.classList.contains('full')){toggleFull();} else {close();} } 104 else if(e.key==='ArrowLeft')show(cur-1); 105 else if(e.key==='ArrowRight')show(cur+1); 106 }); 107 window.addEventListener('resize',syncCaptionTop); 108 } 109 function toggleFull(){ 110 var f=v.classList.toggle('full'); 111 v.querySelector('.cpmv-max').innerHTML=f?MIN:MAX; 112 syncCaptionTop(); 113 } 114 function show(i){ 115 var n=items.length;cur=((i%n)+n)%n;var it=items[cur]; 116 img.onload=syncCaptionTop; // re-align the title once the new image lays out 117 img.src=it.full;img.alt=it.cap||it.title||''; 118 syncCaptionTop(); 119 count.textContent=(cur+1)+' / '+n; 120 title.textContent=it.title||'';title.style.display=it.title?'':'none'; 121 cap.textContent=it.cap||'';cap.style.display=it.cap?'':'none'; 122 credit.textContent=it.credit?(creditLabel+it.credit):'';credit.style.display=it.credit?'':'none'; 123 if(it.art){link.href=it.art;link.textContent=it.artlabel||linkDefault;link.style.display=''; 124 bL.href=it.art;bL.textContent=it.artlabel||linkDefault;bL.style.display='';} 125 else{link.style.display='none';link.removeAttribute('href');bL.style.display='none';bL.removeAttribute('href');} 126 bT.textContent=it.title||''; 127 bC.textContent=it.cap||''; 128 bCr.textContent=it.credit?(creditLabel+it.credit):''; 129 } 130 function open(its,i,full){ if(!v)build(); items=its; 131 if(full){v.classList.add('full');v.querySelector('.cpmv-max').innerHTML=MIN;} 132 else{v.classList.remove('full');v.querySelector('.cpmv-max').innerHTML=MAX;} 133 show(i||0); v.classList.add('open'); } 134 function close(){ if(v){v.classList.remove('open','full');img.src='';} } 135 // v0.44.7: resolve a gallery's data element. Prefer the content-bearing 136 // CONTAINER (.cpm-band/.cpm-gallery/.cpm-inline carries data-cpm-json) -- it 137 // survives page builders (WPBakery/wpautop strip a standalone island div). 138 // Fall back to the legacy separate .cpm-gallery-data element. 139 function cpmIsland(slug,fromEl){
140 var el=(fromEl&&fromEl.closest)?fromEl.closest('[data-cpm-json]'):null; 141 if(!el&&slug)el=document.querySelector('[data-cpm-json][data-slug="'+slug+'"]'); 142 if(!el&&slug)el=document.querySelector('.cpm-gallery-data[data-slug="'+slug+'"]'); 143 return el; 144 } 145 document.addEventListener('click',function(e){ 146 var a=e.target.closest?e.target.closest('[data-cpmg-slug]'):null; 147 if(!a)return; 148 var island=cpmIsland(a.getAttribute('data-cpmg-slug'),a); 149 if(!island)return; 150 var its;try{its=JSON.parse((island.getAttribute('data-cpm-json')||island.textContent));}catch(_){return;} 151 if(!its||!its.length)return; 152 e.preventDefault();e.stopPropagation(); 153 creditLabel=labelFor(island);linkDefault=linkLabelFor(island); 154 cpmvSlug=a.getAttribute('data-cpmg-slug')||''; 155 cpmvName=(island.getAttribute('aria-label')||island.getAttribute('data-cpm-heading')||'').trim(); 156 beacon(cpmvSlug,'open'); 157 open(its,parseInt(a.getAttribute('data-cpmg-index')||'0',10)); 158 },true); 159 // Programmatic open (used by the inline viewer's maximize button). 160 window.cpmvOpen=function(slug,idx,full){ 161 var island=cpmIsland(slug,null); 162 if(!island)return;var its;try{its=JSON.parse((island.getAttribute('data-cpm-json')||island.textContent));}catch(_){return;} 163 if(its&&its.length){creditLabel=labelFor(island);linkDefault=linkLabelFor(island); 164 cpmvSlug=slug||'';cpmvName=(island.getAttribute('aria-label')||island.getAttribute('data-cpm-heading')||'').trim(); 165 beacon(slug,'open');open(its,idx||0,full);} 166 }; 167 // Inline gallery viewer (the [cpm_viewer] widget). 168 function setInline(w,i){ 169 var imgs=w.querySelectorAll('.cpm-inline-img');var n=imgs.length;if(!n)return; 170 i=((i%n)+n)%n; 171 for(var k=0;k<n;k++)imgs[k].classList.toggle('active',k===i); 172 w.setAttribute('data-cur',i); 173 var c=w.querySelector('.cpm-inline-count');if(c)c.textContent=(i+1)+' / '+n; 174 var island=cpmIsland(w.getAttribute('data-slug'),w); 175 var side=w.querySelector('.cpm-inline-side'); 176 if(island&&side){try{var it=JSON.parse((island.getAttribute('data-cpm-json')||island.textContent))[i];side.innerHTML=''; 177 if(it.title){var t=document.createElement('span');t.className='t';t.textContent=it.title;side.appendChild(t);} 178 if(it.cap){var d=document.createElement('p');d.className='d';d.textContent=it.cap;side.appendChild(d);} 179 if(it.art){var lk=document.createElement('a');lk.className='lk';lk.href=it.art;lk.target='_blank';lk.rel='noopener';lk.textContent=it.artlabel||linkLabelFor(island);side.appendChild(lk);} 180 if(it.credit){var c=document.createElement('p');c.className='cr';c.textContent=labelFor(island)+it.credit;side.appendChild(c);} 181 }catch(_){}} 182 } 183 document.addEventListener('click',function(e){ 184 var b=e.target.closest?e.target.closest('.cpm-inline-prev,.cpm-inline-next,.cpm-inline-max'):null; 185 if(!b)return;var w=b.closest('.cpm-inline');if(!w)return; 186 e.preventDefault();e.stopPropagation(); 187 var cur=parseInt(w.getAttribute('data-cur')||'0',10); 188 if(b.classList.contains('cpm-inline-max')){if(window.cpmvOpen)window.cpmvOpen(w.getAttribute('data-slug'),cur,true);return;} 189 setInline(w,b.classList.contains('cpm-inline-prev')?cur-1:cur+1); 190 },true); 191 // ---- Kinomode (v0.47.0): fullscreen auto-slideshow over a gallery -------- 192 // A play button rides each gallery container; click => the .cpmk overlay 193 // crossfades through every image with Ken-Burns motion. No captions/sidebar, 194 // just play + stop (X / Esc). All motion on: randomized order, smooth 195 // crossfade, dynamic motion, ~5s/pic, CPM logo + collection name bottom-left. 196 var kv,ka,kb,kTop=true,kItems=[],kOrder=[],kPos=0,kTimer=null,kWake=null,KSEC=5,KDUR=11; 197 var kPre=['1,0,0,1.22,-8,-6','1.22,-8,-6,1,0,0','1.08,-9,0,1.2,9,0', 198 '1.08,9,0,1.2,-9,0','1.08,0,-9,1.2,0,9','1.2,7,7,1,-7,-7']; 199 function khash(s){var h=0,i;s=s||'';for(i=0;i<s.length;i++){h=((h<<5)-h+s.charCodeAt(i))|0;}return Math.abs(h);} 200 function kShuffle(a){a=a.slice();for(var i=a.length-1;i>0;i--){var j=Math.floor(Math.random()*(i+1)),t=a[i];a[i]=a[j];a[j]=t;}return a;} 201 function kKB(layer,src){ 202 var p=kPre[khash(src)%6].split(','),st=layer.style; 203 st.setProperty('--s0',p[0]);st.setProperty('--x0',p[1]+'%');st.setProperty('--y0',p[2]+'%'); 204 st.setProperty('--s1',p[3]);st.setProperty('--x1',p[4]+'%');st.setProperty('--y1',p[5]+'%'); 205 var d=(0.2+(khash(src)%4)*0.07)*KDUR; 206 st.animation='none';void layer.offsetWidth; // restart the keyframe 207 st.animation='cpmk-kb '+KDUR.toFixed(2)+'s linear -'+d.toFixed(2)+'s forwards'; 208 } 209 // Screen wake lock: keep the display awake during the slideshow. Feature- 210 // detected; re-acquired on tab re-focus (the OS releases it when hidden). 211 function kWakeOn(){try{if(!navigator.wakeLock||document.visibilityState!=='visible'||!kv||!kv.classList.contains('open'))return;navigator.wakeLock.request('screen').then(function(s){kWake=s;}).catch(function(){});}catch(_){}} 212 function kWakeOff(){try{if(kWake){kWake.release();kWake=null;}}catch(_){}} 213 function kBuild(){ 214 kv=document.createElement('div');kv.className='cpmk'; 215 kv.innerHTML='<img class="cpmk-l cpmk-a" alt="">' 216 +'<img class="cpmk-l cpmk-b" alt="">' 217 +'<div class="cpmk-brand"><img class="cpmk-logo" alt="CPM" src="'+BASE+'/api/v1/embed/cpm-logo.png">' 218 +'<div class="cpmk-title"></div></div>' 219 +'<button class="cpmk-x" type="button" aria-label="Schliessen">×</button>'; 220 document.body.appendChild(kv); 221 ka=kv.querySelector('.cpmk-a');kb=kv.querySelector('.cpmk-b'); 222 kv.querySelector('.cpmk-x').addEventListener('click',kStop); 223 document.addEventListener('keydown',function(e){ 224 if(kv&&kv.classList.contains('open')&&e.key==='Escape')kStop(); 225 }); 226 document.addEventListener('visibilitychange',function(){if(document.visibilityState==='visible')kWakeOn();}); 227 } 228 function kShow(idx){ 229 var n=kOrder.length;if(!n)return;kPos=((idx%n)+n)%n; 230 var it=kItems[kOrder[kPos]],src=it.full||it.thumb||''; 231 var top=kTop?kb:ka,under=kTop?ka:kb; 232 kKB(top,src);
233 var reveal=function(){top.classList.add('show');under.classList.remove('show');}; 234 top.onload=reveal;top.onerror=reveal;top.src=src; 235 if(top.complete&&top.naturalWidth){top.onload=null;reveal();} 236 kTop=!kTop; 237 var nx=kItems[kOrder[(kPos+1)%n]];if(nx){var pi=new Image();pi.src=nx.full||nx.thumb||'';} 238 } 239 function kTick(){if(kTimer)clearTimeout(kTimer);kTimer=setTimeout(function(){kShow(kPos+1);kTick();},Math.max(2,KSEC)*1000);} 240 var kNameCache={},kCurSlug=''; 241 function kFetchName(slug,cb){ 242 // The Betrachter (.cpm-inline) container carries no collection name, so 243 // pull it from the public JSON (cross-origin allowed since v0.47.1). Only 244 // cache a non-empty name, so a transient blank never poisons the cache. 245 if(!slug||!BASE){cb('');return;} 246 if(kNameCache[slug]){cb(kNameCache[slug]);return;} 247 try{fetch(BASE+'/api/v1/collections/'+encodeURIComponent(slug)).then(function(r){return r.ok?r.json():null;}).then(function(d){ 248 var nm=(d&&d.name)?d.name:'';if(nm)kNameCache[slug]=nm;cb(nm); 249 }).catch(function(){cb('');});}catch(_){cb('');} 250 } 251 function kStart(slug,its,name){ 252 if(!its||its.length<1)return;if(!kv)kBuild(); 253 kCurSlug=slug||''; 254 kItems=its;kOrder=kShuffle(its.map(function(_,i){return i;}));kPos=0;kTop=true; 255 var tEl=kv.querySelector('.cpmk-title'); 256 function setT(nm){tEl.textContent=nm||'';tEl.style.display=nm?'':'none';} 257 setT(name); 258 // Open FIRST, then resolve the name -- kFetchName may call back 259 // synchronously on a cache hit, and the callback's open-guard would 260 // otherwise skip setting the title (the "sometimes no title" bug). The 261 // slug check guards against a stale async result from another collection. 262 kv.classList.add('open'); 263 if(!name)kFetchName(slug,function(nm){if(kv&&kv.classList.contains('open')&&slug===kCurSlug)setT(nm);}); 264 try{if(kv.requestFullscreen)kv.requestFullscreen();}catch(_){} 265 kShow(0);kTick();beacon(slug,'open');kWakeOn(); 266 } 267 function kStop(){ 268 if(kTimer){clearTimeout(kTimer);kTimer=null;} 269 kWakeOff(); 270 if(kv){kv.classList.remove('open');if(ka)ka.src='';if(kb)kb.src='';} 271 try{if(document.fullscreenElement)document.exitFullscreen();}catch(_){} 272 } 273})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.