@xiboplayer/pwa 0.7.20 → 0.7.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{main-C8f1ieZL.js → main-oacre7st.js} +4 -4
- package/dist/assets/{main-C8f1ieZL.js.map → main-oacre7st.js.map} +1 -1
- package/dist/assets/main-vwJkNw4Y.js +3 -0
- package/dist/assets/{main-BqJjhM5z.js.map → main-vwJkNw4Y.js.map} +1 -1
- package/dist/assets/{setup-Dpox3R4b.js → setup-B4gZX38p.js} +2 -2
- package/dist/assets/{setup-Dpox3R4b.js.map → setup-B4gZX38p.js.map} +1 -1
- package/dist/assets/{src-CIERor10.js → src-B_BNICay.js} +2 -2
- package/dist/assets/{src-CIERor10.js.map → src-B_BNICay.js.map} +1 -1
- package/dist/assets/{src-GjeUdI7b.js → src-Bjt9ooXK.js} +2 -2
- package/dist/assets/{src-GjeUdI7b.js.map → src-Bjt9ooXK.js.map} +1 -1
- package/dist/assets/{src-Bzheh5E2.js → src-BtVLiVYZ.js} +2 -2
- package/dist/assets/{src-Bzheh5E2.js.map → src-BtVLiVYZ.js.map} +1 -1
- package/dist/assets/{src-Cvcg0wpA.js → src-CKpVxGpH.js} +2 -2
- package/dist/assets/{src-Cvcg0wpA.js.map → src-CKpVxGpH.js.map} +1 -1
- package/dist/assets/{src-DzgDstdA.js → src-CROvYSP8.js} +2 -2
- package/dist/assets/{src-DzgDstdA.js.map → src-CROvYSP8.js.map} +1 -1
- package/dist/assets/{src-e2yErlKg.js → src-C_Lx4lXp.js} +2 -2
- package/dist/assets/{src-e2yErlKg.js.map → src-C_Lx4lXp.js.map} +1 -1
- package/dist/assets/{src-B0l-hkrA.js → src-Cx3tXAAu.js} +2 -2
- package/dist/assets/{src-B0l-hkrA.js.map → src-Cx3tXAAu.js.map} +1 -1
- package/dist/assets/{src-BLwxK97o.js → src-DAB0dqGG.js} +2 -2
- package/dist/assets/{src-BLwxK97o.js.map → src-DAB0dqGG.js.map} +1 -1
- package/dist/assets/{src-DTJp4-F0.js → src-WDu491CE.js} +2 -2
- package/dist/assets/{src-DTJp4-F0.js.map → src-WDu491CE.js.map} +1 -1
- package/dist/assets/{src-D_pit2UZ.js → src-cUopH0nN.js} +2 -2
- package/dist/assets/{src-D_pit2UZ.js.map → src-cUopH0nN.js.map} +1 -1
- package/dist/assets/{sync-manager-B6kqa4bv.js → sync-manager-8Z-qwkod.js} +2 -2
- package/dist/assets/{sync-manager-B6kqa4bv.js.map → sync-manager-8Z-qwkod.js.map} +1 -1
- package/dist/index.html +1 -1
- package/dist/setup.html +3 -3
- package/dist/sw-pwa.js +2 -2
- package/dist/sw-pwa.js.map +1 -1
- package/package.json +13 -13
- package/dist/assets/main-BqJjhM5z.js +0 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./pdf-Bxz9Nzto.js","./preload-helper-Chd9yIcd.js"])))=>i.map(i=>d[i]);
|
|
2
|
-
import{t as e}from"./preload-helper-Chd9yIcd.js";import{c as t,d as n,t as r,u as i}from"./src-Bzheh5E2.js";import{n as a}from"./src-CIERor10.js";import{r as o}from"./src-DzgDstdA.js";var s={name:`@xiboplayer/renderer`,version:`0.7.20`,description:`RendererLite - Fast, efficient XLF layout rendering engine`,type:`module`,main:`./src/index.js`,types:`./src/index.d.ts`,exports:{".":`./src/index.js`,"./renderer-lite":`./src/renderer-lite.js`,"./layout":`./src/layout.js`},scripts:{test:`vitest run`,"test:watch":`vitest`,"test:coverage":`vitest run --coverage`},dependencies:{"@xiboplayer/cache":`workspace:*`,"@xiboplayer/schedule":`workspace:*`,"@xiboplayer/utils":`workspace:*`,"pdfjs-dist":`^5.6.205`},devDependencies:{jsdom:`^29.0.2`,vitest:`^4.1.2`},keywords:[`xibo`,`digital-signage`,`renderer`,`xlf`,`layout`],author:`Pau Aliagas <linuxnow@gmail.com>`,license:`AGPL-3.0-or-later`,repository:{type:`git`,url:`git+https://github.com/xibo-players/xiboplayer.git`,directory:`packages/renderer`},homepage:`https://xiboplayer.org`},c=i(`LayoutPool`),l=class e{constructor(e=2){this.layouts=new Map,this.maxSize=e,this.hotLayoutId=null}has(e){return this.layouts.has(e)}get(e){return this.layouts.get(e)}add(e,t){if(this.layouts.has(e)){let n=this.layouts.get(e);Object.assign(n,t),n.lastAccess=Date.now();return}this.layouts.size>=this.maxSize&&this.evictLRU(),t.status=`warm`,t.lastAccess=Date.now(),this.layouts.set(e,t),c.info(`Added layout ${e} to pool (size: ${this.layouts.size}/${this.maxSize})`)}setHot(e){if(this.hotLayoutId!==null&&this.layouts.has(this.hotLayoutId)&&(this.layouts.get(this.hotLayoutId).status=`warm`),this.layouts.has(e)){let t=this.layouts.get(e);t.status=`hot`,t.lastAccess=Date.now()}this.hotLayoutId=e}evict(t){let n=this.layouts.get(t);if(n){if(c.info(`Evicting layout ${t} from pool`),n.regions)for(let[e,t]of n.regions)t.timer&&=(clearTimeout(t.timer),null);if(n.container&&e.releaseMediaElements(n.container),n.blobUrls&&n.blobUrls.size>0&&(n.blobUrls.forEach(e=>{URL.revokeObjectURL(e)}),c.info(`Revoked ${n.blobUrls.size} blob URLs for layout ${t}`)),n.mediaUrlCache)for(let[e,t]of n.mediaUrlCache)t&&typeof t==`string`&&t.startsWith(`blob:`)&&URL.revokeObjectURL(t);n.container&&n.container.parentNode&&n.container.remove(),this.layouts.delete(t),this.hotLayoutId===t&&(this.hotLayoutId=null)}}static releaseMediaElements(t){requestAnimationFrame(()=>e._releaseMediaElementsSync(t))}static _releaseMediaElementsSync(e){let t=0,n=0;e.querySelectorAll(`video`).forEach(e=>{e._hlsInstance&&(e._hlsInstance.destroy(),e._hlsInstance=null,n++),e._mediaStream&&(e._mediaStream.getTracks().forEach(e=>e.stop()),e._mediaStream=null,e.srcObject=null),e.pause(),e.removeAttribute(`src`),e.load(),t++}),e.querySelectorAll(`audio`).forEach(e=>{e.pause(),e.removeAttribute(`src`),e.load()});let r=0;e.querySelectorAll(`iframe`).forEach(e=>{try{let n=e.contentDocument||e.contentWindow?.document;n&&(n.querySelectorAll(`video`).forEach(e=>{e.pause(),e.removeAttribute(`src`),e.load(),t++}),n.querySelectorAll(`audio`).forEach(e=>{e.pause(),e.removeAttribute(`src`),e.load()}))}catch{}e.src=`about:blank`,r++}),e.querySelectorAll(`.pdf-widget`).forEach(e=>{e._pdfDestroy&&e._pdfDestroy()}),(t>0||r>0)&&c.info(`Released ${t} video(s)${n?` (${n} HLS)`:``}${r?`, ${r} iframe(s)`:``}`)}evictLRU(){let e=null,t=1/0;for(let[n,r]of this.layouts)r.status===`warm`&&r.lastAccess<t&&(e=n,t=r.lastAccess);e!==null&&this.evict(e)}clearWarm(){let e=0,t=[];for(let[e,n]of this.layouts)n.status===`warm`&&t.push(e);for(let n of t)this.evict(n),e++;return e>0&&c.info(`Cleared ${e} warm layout(s) from pool`),e}clearWarmNotIn(e){let t=0,n=[];for(let[t,r]of this.layouts)r.status===`warm`&&!e.has(t)&&n.push(t);for(let e of n)this.evict(e),t++;return t>0&&c.info(`Cleared ${t} warm layout(s) no longer in schedule`),t}getLatest(){let e;for(let t of this.layouts.keys())e=t;return e}clear(){let e=Array.from(this.layouts.keys());for(let t of e)this.evict(t);this.hotLayoutId=null}get size(){return this.layouts.size}},u={fadeIn(e,t){let n=[{opacity:0},{opacity:1}],r={duration:t,easing:`linear`,fill:`forwards`};return e.animate(n,r)},fadeOut(e,t){let n=[{opacity:1},{opacity:0}],r={duration:t,easing:`linear`,fill:`forwards`};return e.animate(n,r)},getFlyKeyframes(e,t,n,r){let i={N:{x:0,y:r?-n:n},NE:{x:r?t:-t,y:r?-n:n},E:{x:r?t:-t,y:0},SE:{x:r?t:-t,y:r?n:-n},S:{x:0,y:r?n:-n},SW:{x:r?-t:t,y:r?n:-n},W:{x:r?-t:t,y:0},NW:{x:r?-t:t,y:r?-n:n}},a=i[e]||i.N;return r?{from:{transform:`translate(${a.x}px, ${a.y}px)`,opacity:0},to:{transform:`translate(0, 0)`,opacity:1}}:{from:{transform:`translate(0, 0)`,opacity:1},to:{transform:`translate(${a.x}px, ${a.y}px)`,opacity:0}}},flyIn(e,t,n,r,i){let a=this.getFlyKeyframes(n,r,i,!0),o={duration:t,easing:`ease-out`,fill:`forwards`};return e.animate([a.from,a.to],o)},flyOut(e,t,n,r,i){let a=this.getFlyKeyframes(n,r,i,!1),o={duration:t,easing:`ease-in`,fill:`forwards`};return e.animate([a.from,a.to],o)},slideIn(e,t,n,r,i){let a={N:{x:0,y:-i},NE:{x:r,y:-i},E:{x:r,y:0},SE:{x:r,y:i},S:{x:0,y:i},SW:{x:-r,y:i},W:{x:-r,y:0},NW:{x:-r,y:-i}},o=a[n]||a.E;return e.animate([{transform:`translate(${o.x}px, ${o.y}px)`},{transform:`translate(0, 0)`}],{duration:t,easing:`ease-out`,fill:`forwards`})},slideOut(e,t,n,r,i){let a={N:{x:0,y:-i},NE:{x:r,y:-i},E:{x:r,y:0},SE:{x:r,y:i},S:{x:0,y:i},SW:{x:-r,y:i},W:{x:-r,y:0},NW:{x:-r,y:-i}},o=a[n]||a.W;return e.animate([{transform:`translate(0, 0)`},{transform:`translate(${o.x}px, ${o.y}px)`}],{duration:t,easing:`ease-in`,fill:`forwards`})},wipeIn(e,t,n){let r={E:{from:`inset(0 100% 0 0)`,to:`inset(0 0 0 0)`},W:{from:`inset(0 0 0 100%)`,to:`inset(0 0 0 0)`},S:{from:`inset(0 0 100% 0)`,to:`inset(0 0 0 0)`},N:{from:`inset(100% 0 0 0)`,to:`inset(0 0 0 0)`},SE:{from:`inset(0 100% 100% 0)`,to:`inset(0 0 0 0)`},SW:{from:`inset(0 0 100% 100%)`,to:`inset(0 0 0 0)`},NE:{from:`inset(100% 100% 0 0)`,to:`inset(0 0 0 0)`},NW:{from:`inset(100% 0 0 100%)`,to:`inset(0 0 0 0)`}},i=r[n]||r.E;return e.animate([{clipPath:i.from},{clipPath:i.to}],{duration:t,easing:`ease-out`,fill:`forwards`})},apply(e,t,n,r,i){if(!t||!t.type)return null;let a=t.type.toLowerCase(),o=t.duration||1e3,s=t.direction||`N`;switch(a){case`fade`:return n?this.fadeIn(e,o):this.fadeOut(e,o);case`fadein`:return n?this.fadeIn(e,o):null;case`fadeout`:return n?null:this.fadeOut(e,o);case`fly`:return n?this.flyIn(e,o,s,r,i):this.flyOut(e,o,s,r,i);case`flyin`:return n?this.flyIn(e,o,s,r,i):null;case`flyout`:return n?null:this.flyOut(e,o,s,r,i);case`slide`:return n?this.slideIn(e,o,s,r,i):this.slideOut(e,o,s,r,i);case`wipe`:return n?this.wipeIn(e,o,s):null;default:return null}}},d=class{constructor(e,n,r={}){this.config=e,this.container=n,this.options=r,this.log=i(`RendererLite`,r.logLevel),this.emitter=new t,this.currentLayout=null,this.currentLayoutId=null,this._preloadingLayoutId=null,this._preloadingPromise=null,this.regions=new Map,this.layoutTimer=null,this.layoutEndEmitted=!1,this._deferredTimerLayoutId=null,this._deferredTimerFallback=null,this._paused=!1,this._layoutTimerStartedAt=null,this._layoutTimerDurationMs=null,this.layoutBlobUrls=new Map,this.audioOverlays=new Map,this._stopWidgetBound=(e,t)=>this.stopWidget(e,t),this._renderWidgetBound=(e,t)=>this.renderWidget(e,t),this.scaleFactor=1,this.offsetX=0,this.offsetY=0,this.overlayContainer=null,this.activeOverlays=new Map,this._keydownHandler=null,this._keyboardActions=[],this._subPlaylistCycleIndex=new Map,this._startedWidgets=new Set,this.layoutPool=new l(2),this.preloadTimer=null,this._preloadRetryTimer=null,this.layoutTransition=this._normalizeLayoutTransition(r.layoutTransition),this.setupContainer(),this.emitter.on(`interactiveTrigger`,e=>this._handleInteractiveTrigger(e)),this.emitter.on(`widgetExpire`,e=>this._handleWidgetExpire(e)),this.emitter.on(`widgetExtendDuration`,e=>this._handleWidgetExtendDuration(e)),this.emitter.on(`widgetSetDuration`,e=>this._handleWidgetSetDuration(e)),this.log.info(`Initialized`)}setupContainer(){if(this.container.style.position=`relative`,this.container.style.width=`100%`,this.container.style.height=`100vh`,this.container.style.overflow=`hidden`,this._resizeSuppressed=!1,typeof ResizeObserver<`u`){let e=null;this.resizeObserver=new ResizeObserver(()=>{this._resizeSuppressed||(e&&clearTimeout(e),e=setTimeout(()=>this.rescaleRegions(),150))}),this.resizeObserver.observe(this.container)}this.overlayContainer=document.createElement(`div`),this.overlayContainer.id=`overlay-container`,this.overlayContainer.style.position=`absolute`,this.overlayContainer.style.top=`0`,this.overlayContainer.style.left=`0`,this.overlayContainer.style.width=`100%`,this.overlayContainer.style.height=`100%`,this.overlayContainer.style.zIndex=`1000`,this.overlayContainer.style.pointerEvents=`none`,this.container.appendChild(this.overlayContainer)}calculateScale(e){let t=this.container.clientWidth,n=this.container.clientHeight;if(!t||!n)return;let r=t/e.width,i=n/e.height;this.scaleFactor=Math.min(r,i),this.offsetX=(t-e.width*this.scaleFactor)/2,this.offsetY=(n-e.height*this.scaleFactor)/2,this.log.info(`Scale: ${this.scaleFactor.toFixed(3)} (${e.width}x${e.height} → ${t}x${n}, offset ${Math.round(this.offsetX)},${Math.round(this.offsetY)})`)}applyRegionScale(e,t){let n=this.scaleFactor;e.style.left=`${t.left*n+this.offsetX}px`,e.style.top=`${t.top*n+this.offsetY}px`,e.style.width=`${t.width*n}px`,e.style.height=`${t.height*n}px`}rescaleRegions(){if(this.currentLayout){this.calculateScale(this.currentLayout);for(let[e,t]of this.regions)this.applyRegionScale(t.element,t.config),t.width=t.config.width*this.scaleFactor,t.height=t.config.height*this.scaleFactor;for(let[e,t]of this.activeOverlays){this.calculateScale(t.layout);for(let[e,n]of t.regions)this.applyRegionScale(n.element,n.config),n.width=n.config.width*this.scaleFactor,n.height=n.config.height*this.scaleFactor}}}on(e,t){this.emitter.on(e,t)}emit(e,...t){this.emitter.emit(e,...t)}parseActions(e){let t=[];for(let n of e.children)n.tagName===`action`&&t.push({id:n.getAttribute(`id`)||``,actionType:n.getAttribute(`actionType`)||``,triggerType:n.getAttribute(`triggerType`)||``,triggerCode:n.getAttribute(`triggerCode`)||``,source:n.getAttribute(`source`)||``,sourceId:n.getAttribute(`sourceId`)||``,target:n.getAttribute(`target`)||``,targetId:n.getAttribute(`targetId`)||``,widgetId:n.getAttribute(`widgetId`)||``,layoutCode:n.getAttribute(`layoutCode`)||``,commandCode:n.getAttribute(`commandCode`)||``});return t}_normalizeLayoutTransition(e){return{type:e?.type||`instant`,duration:Number.isFinite(e?.duration)?e.duration:500,direction:e?.direction||void 0}}_resolveLayoutTransition(e){let t=e?.layoutTransitionIn;return!t||!t.type?this.layoutTransition:{type:t.type,duration:Number.isFinite(t.duration)?t.duration:this.layoutTransition.duration,direction:t.direction||this.layoutTransition.direction}}parseXlf(e){let t=new DOMParser().parseFromString(e,`text/xml`).querySelector(`layout`);if(!t)throw Error(`Invalid XLF: no <layout> element`);let n=t.getAttribute(`duration`),r=t.getAttribute(`layoutTransitionIn`),i=t.getAttribute(`layoutTransitionInDuration`),a=t.getAttribute(`layoutTransitionInDirection`),s={schemaVersion:parseInt(t.getAttribute(`schemaVersion`)||`1`),width:parseInt(t.getAttribute(`width`)||`1920`),height:parseInt(t.getAttribute(`height`)||`1080`),duration:n?parseInt(n):0,bgcolor:t.getAttribute(`backgroundColor`)||t.getAttribute(`bgcolor`)||`#000000`,background:t.getAttribute(`background`)||null,enableStat:t.getAttribute(`enableStat`)!==`0`,actions:this.parseActions(t),layoutTransitionIn:r?{type:r,duration:i?parseInt(i):void 0,direction:a||void 0}:null,regions:[]};s.schemaVersion>1&&this.log.debug(`XLF schema version: ${s.schemaVersion}`),n?this.log.info(`Layout duration from XLF: ${s.duration}s`):this.log.info(`Layout duration NOT in XLF, will calculate from widgets`);let c=t.querySelectorAll(`:scope > region, :scope > drawer`);for(let e of c){let t=e.tagName===`drawer`,n=e.getAttribute(`type`)||null,r={id:e.getAttribute(`id`),width:parseInt(e.getAttribute(`width`)||`0`),height:parseInt(e.getAttribute(`height`)||`0`),top:parseInt(e.getAttribute(`top`)||`0`),left:parseInt(e.getAttribute(`left`)||`0`),zindex:parseInt(e.getAttribute(`zindex`)||(t?`2000`:`0`)),enableStat:e.getAttribute(`enableStat`)!==`0`,actions:this.parseActions(e),exitTransition:null,transitionType:null,transitionDuration:null,transitionDirection:null,loop:!0,isDrawer:t,isCanvas:n===`canvas`,widgets:[]},i=Array.from(e.children).find(e=>e.tagName===`options`);if(i){let e=i.querySelector(`exitTransType`);if(e&&e.textContent){let t=i.querySelector(`exitTransDuration`),n=i.querySelector(`exitTransDirection`);r.exitTransition={type:e.textContent,duration:parseInt(t&&t.textContent||`1000`),direction:n&&n.textContent||`N`}}let t=i.querySelector(`loop`);t&&(r.loop=t.textContent!==`0`);let n=i.querySelector(`transitionType`);if(n&&n.textContent){r.transitionType=n.textContent;let e=i.querySelector(`transitionDuration`),t=i.querySelector(`transitionDirection`);r.transitionDuration=parseInt(e&&e.textContent||`1000`),r.transitionDirection=t&&t.textContent||`N`}}for(let t of e.children){if(t.tagName!==`media`)continue;let e=this.parseWidget(t);r.widgets.push(e)}!r.isCanvas&&r.widgets.some(e=>e.type===`global`)&&(r.isCanvas=!0),s.regions.push(r),t&&this.log.info(`Parsed drawer: id=${r.id} with ${r.widgets.length} widgets`),r.isCanvas&&this.log.info(`Parsed canvas region: id=${r.id} with ${r.widgets.length} widgets (all render simultaneously)`)}if(s.duration===0){let{duration:t,isDynamic:n}=o(e);s.duration=t,s.isDynamic=n,this.log.info(`Calculated layout duration: ${s.duration}s (not specified in XLF)${n?` [dynamic — has useDuration=0 video]`:``}`)}return s}parseWidget(e){let t=e.getAttribute(`type`),n=parseInt(e.getAttribute(`duration`)||`10`),r=parseInt(e.getAttribute(`useDuration`)||`1`),i=e.getAttribute(`id`),a=e.getAttribute(`fileId`),o={},s=e.querySelector(`options`);if(s)for(let e of s.children)o[e.tagName]=e.textContent;let c=e.querySelector(`raw`),l=c?c.textContent:``,u={in:null,out:null};o.transIn&&(u.in={type:o.transIn,duration:parseInt(o.transInDuration||`1000`),direction:o.transInDirection||`N`}),o.transOut&&(u.out={type:o.transOut,duration:parseInt(o.transOutDuration||`1000`),direction:o.transOutDirection||`N`});let d=this.parseActions(e),f=[];for(let t of e.children)if(t.tagName.toLowerCase()===`audio`){let e=t.querySelector(`uri`);e?f.push({mediaId:e.getAttribute(`mediaId`)||null,uri:e.textContent||``,volume:parseInt(e.getAttribute(`volume`)||`100`),loop:e.getAttribute(`loop`)===`1`}):f.push({mediaId:t.getAttribute(`mediaId`)||null,uri:t.getAttribute(`uri`)||``,volume:parseInt(t.getAttribute(`volume`)||`100`),loop:t.getAttribute(`loop`)===`1`})}let p=[],m=Array.from(e.children).find(e=>e.tagName===`commands`);if(m)for(let e of m.children)e.tagName===`command`&&p.push({commandCode:e.getAttribute(`commandCode`)||``,commandString:e.getAttribute(`commandString`)||``});let h=e.getAttribute(`parentWidgetId`)||null,g=parseInt(e.getAttribute(`displayOrder`)||`0`),_=e.getAttribute(`cyclePlayback`)===`1`,v=parseInt(e.getAttribute(`playCount`)||`0`),y=e.getAttribute(`isRandom`)===`1`,b=e.getAttribute(`fromDt`)||e.getAttribute(`fromdt`)||null,x=e.getAttribute(`toDt`)||e.getAttribute(`todt`)||null;return{type:t,duration:n,useDuration:r,id:i,fileId:a,render:e.getAttribute(`render`)||null,fromDt:b,toDt:x,enableStat:e.getAttribute(`enableStat`)!==`0`,webhookUrl:o.webhookUrl||null,options:o,raw:l,transitions:u,actions:d,audioNodes:f,commands:p,parentWidgetId:h,displayOrder:g,cyclePlayback:_,playCount:v,isRandom:y}}trackBlobUrl(e){let t=this._preloadingLayoutId||this.currentLayoutId||0;t||this.log.warn(`trackBlobUrl called without currentLayoutId, tracking under key 0`),this.layoutBlobUrls.has(t)||this.layoutBlobUrls.set(t,new Set),this.layoutBlobUrls.get(t).add(e)}revokeBlobUrlsForLayout(e){let t=this.layoutBlobUrls.get(e);t&&(t.forEach(e=>{URL.revokeObjectURL(e)}),this.layoutBlobUrls.delete(e),this.log.info(`Revoked ${t.size} blob URLs for layout ${e}`))}updateLayoutDuration(){if(!this.currentLayout)return;let e=0;for(let t of this.currentLayout.regions){if(t.isDrawer)continue;let n=0;for(let e of t.widgets)e.duration>0&&(n+=e.duration);e=Math.max(e,n)}if(e>0&&e!==this.currentLayout.duration){let t=this.currentLayout.duration;this.currentLayout.duration=e,this.currentLayout._durationFromMetadata=!0,this.log.info(`Layout duration updated: ${t}s → ${e}s (based on video metadata)`);let n=!this._hasUnprobedVideos();if(this.emit(`layoutDurationUpdated`,this.currentLayoutId,e,n),this._deferredTimerLayoutId===this.currentLayoutId&&!this.layoutTimer)if(this._hasUnprobedVideos())this.log.info(`Layout duration updated to ${e}s but still has unprobed videos — keeping timer deferred`);else{this._deferredTimerFallback&&=(clearTimeout(this._deferredTimerFallback),null);let t=Date.now()-(this._layoutTimerStartedAt||Date.now()),n=Math.max(1e3,e*1e3-t);this._deferredTimerLayoutId=null,this._layoutTimerDurationMs=n,this.layoutTimer=setTimeout(()=>{this.log.info(`Layout ${this.currentLayoutId} duration expired (${this.currentLayout.duration}s)`),this.currentLayoutId&&(this.layoutEndEmitted=!0,this.emit(`layoutEnd`,this.currentLayoutId))},n),this.log.info(`All video durations resolved — deferred timer started: ${(n/1e3).toFixed(1)}s remaining (waited ${(t/1e3).toFixed(1)}s for metadata)`)}else if(this.layoutTimer){clearTimeout(this.layoutTimer);let e=Date.now()-(this._layoutTimerStartedAt||Date.now()),t=Math.max(1e3,this.currentLayout.duration*1e3-e);this.layoutTimer=setTimeout(()=>{this.log.info(`Layout ${this.currentLayoutId} duration expired (${this.currentLayout.duration}s)`),this.currentLayoutId&&(this.layoutEndEmitted=!0,this.emit(`layoutEnd`,this.currentLayoutId))},t),this.log.info(`Layout timer adjusted to ${(t/1e3).toFixed(1)}s remaining (elapsed ${(e/1e3).toFixed(1)}s of ${this.currentLayout.duration}s)`)}else this.log.info(`Layout duration updated to ${e}s (timer not yet started, will use new value)`);this._scheduleNextLayoutPreload(this.currentLayout)}}attachActionListeners(e){let t=[],n=0;for(let r of e.actions||[])r.triggerType===`touch`?(this.attachTouchAction(this.container,r,null,null),n++):r.triggerType?.startsWith(`keyboard:`)&&t.push(r);for(let r of e.regions){let e=this.regions.get(r.id);if(e){for(let i of r.actions||[])i.triggerType===`touch`?(this.attachTouchAction(e.element,i,r.id,null),n++):i.triggerType.startsWith(`keyboard:`)&&t.push(i);for(let i of r.widgets){if(!i.actions||i.actions.length===0)continue;let a=e.widgetElements.get(i.id);if(a)for(let e of i.actions)e.triggerType===`touch`?(this.attachTouchAction(a,e,r.id,i.id),n++):e.triggerType.startsWith(`keyboard:`)&&t.push(e)}}}this.setupKeyboardListener(t),(n>0||t.length>0)&&this.log.info(`Actions attached: ${n} touch, ${t.length} keyboard`)}attachTouchAction(e,t,n,r){e.style.cursor=`pointer`;let i=e=>{e.stopPropagation();let i=r?`widget ${r}`:`region ${n}`;this.log.info(`Touch action fired on ${i}: ${t.actionType}`),this.emit(`action-trigger`,{actionType:t.actionType,triggerType:`touch`,triggerCode:t.triggerCode,layoutCode:t.layoutCode,targetId:t.targetId,commandCode:t.commandCode,source:{regionId:n,widgetId:r}})};e.addEventListener(`click`,i),e._actionHandlers||=[],e._actionHandlers.push(i)}setupKeyboardListener(e){this.removeKeyboardListener(),this._keyboardActions=e,e.length!==0&&(this._keydownHandler=e=>{let t=e.key;for(let e of this._keyboardActions)if(t===e.triggerType.substring(9)){this.log.info(`Keyboard action (key: ${t}): ${e.actionType}`),this.emit(`action-trigger`,{actionType:e.actionType,triggerType:e.triggerType,triggerCode:e.triggerCode,layoutCode:e.layoutCode,targetId:e.targetId,commandCode:e.commandCode,source:{key:t}});break}},document.addEventListener(`keydown`,this._keydownHandler))}removeKeyboardListener(){this._keydownHandler&&=(document.removeEventListener(`keydown`,this._keydownHandler),null),this._keyboardActions=[]}removeActionListeners(){for(let[,e]of this.regions){this._cleanElementActionHandlers(e.element);for(let[,t]of e.widgetElements)this._cleanElementActionHandlers(t)}this.removeKeyboardListener()}_cleanElementActionHandlers(e){if(e._actionHandlers){for(let t of e._actionHandlers)e.removeEventListener(`click`,t);delete e._actionHandlers,e.style.cursor=``}}_findRegionByWidgetId(e){for(let[t,n]of this.regions){let r=n.widgets.findIndex(t=>t.id===e);if(r!==-1)return{regionId:t,region:n,widget:n.widgets[r],widgetIndex:r,regionMap:this.regions}}for(let t of this.activeOverlays.values())if(t.regions)for(let[n,r]of t.regions){let i=r.widgets.findIndex(t=>t.id===e);if(i!==-1)return{regionId:n,region:r,widget:r.widgets[i],widgetIndex:i,regionMap:t.regions}}return null}_advanceRegion(e,t){let n=t.get(e);if(!n)return;n.currentIndex=(n.currentIndex+1)%n.widgets.length;let r=t===this.regions;this._startRegionCycle(n,e,this._renderWidgetBound,this._stopWidgetBound,r?()=>this.checkLayoutComplete():void 0)}_handleInteractiveTrigger({targetId:e,triggerCode:t}){this.log.info(`XIC interactiveTrigger: target=${e} code=${t}`),this._findRegionByWidgetId(e)?this.navigateToWidget(e):this.log.warn(`XIC interactiveTrigger: widget ${e} not found`)}_handleWidgetExpire({widgetId:e}){let t=this._findRegionByWidgetId(e);if(!t){this.log.warn(`XIC widgetExpire: widget ${e} not found`);return}let{regionId:n,region:r,widgetIndex:i,regionMap:a}=t;this.log.info(`XIC widgetExpire: widget=${e} region=${n}`),r.timer&&=(clearTimeout(r.timer),null),this.stopWidget(n,i),this._advanceRegion(n,a)}_handleWidgetExtendDuration({widgetId:e,duration:t}){let n=this._findRegionByWidgetId(e);if(!n){this.log.warn(`XIC widgetExtendDuration: widget ${e} not found`);return}let{regionId:r,region:i}=n;this.log.info(`XIC widgetExtendDuration: widget=${e} +${t}s`),i.timer&&=(clearTimeout(i.timer),null),i.timer=setTimeout(()=>{this.stopWidget(r,i.currentIndex),this._advanceRegion(r,n.regionMap)},t*1e3)}_handleWidgetSetDuration({widgetId:e,duration:t}){let n=this._findRegionByWidgetId(e);if(!n){this.log.warn(`XIC widgetSetDuration: widget ${e} not found`);return}let{regionId:r,region:i}=n;this.log.info(`XIC widgetSetDuration: widget=${e} ${t}s`),i.timer&&=(clearTimeout(i.timer),null),i.timer=setTimeout(()=>{this.stopWidget(r,i.currentIndex),this._advanceRegion(r,n.regionMap)},t*1e3)}navigateToWidget(e){for(let[t,n]of this.regions){let r=n.widgets.findIndex(t=>t.id===e);if(r!==-1){if(this.log.info(`Navigating to widget ${e} in region ${t} (index ${r})`),n.isDrawer&&n.element.style.display===`none`&&(n.element.style.display=``,this.log.info(`Drawer region ${t} revealed`)),n.timer&&=(clearTimeout(n.timer),null),this.stopWidget(t,n.currentIndex),n.currentIndex=r,this.renderWidget(t,r),n.widgets.length>1){let e=n.widgets[r].duration*1e3;n.timer=setTimeout(()=>{this.stopWidget(t,r);let e=(r+1)%n.widgets.length;n.currentIndex=e,n.isDrawer&&e===0?(n.element.style.display=`none`,this.log.info(`Drawer region ${t} hidden (cycle complete)`)):n.isDrawer?this.navigateToWidget(n.widgets[e].id):this.startRegion(t)},e)}else if(n.isDrawer){let e=n.widgets[r].duration*1e3;n.timer=setTimeout(()=>{this.stopWidget(t,r),n.element.style.display=`none`,this.log.info(`Drawer region ${t} hidden (single widget done)`)},e)}return}}this.log.warn(`Target widget ${e} not found in any region`)}nextWidget(e){let t=e?this.regions.get(e):this.regions.values().next().value;if(!t||t.widgets.length<=1)return;let n=(t.currentIndex+1)%t.widgets.length,r=t.widgets[n];this.log.info(`nextWidget → index ${n} (widget ${r.id})`),this.navigateToWidget(r.id)}previousWidget(e){let t=e?this.regions.get(e):this.regions.values().next().value;if(!t||t.widgets.length<=1)return;let n=(t.currentIndex-1+t.widgets.length)%t.widgets.length,r=t.widgets[n];this.log.info(`previousWidget → index ${n} (widget ${r.id})`),this.navigateToWidget(r.id)}_mediaFileUrl(e){return`${window.location.origin}${r}/media/file/${e}`}_positionWidgetElement(e){Object.assign(e.style,{position:`absolute`,top:`0`,left:`0`,width:`100%`,height:`100%`,visibility:`hidden`,opacity:`0`})}_applyBackgroundImage(e,t){Object.assign(e.style,{backgroundImage:`url(${t})`,backgroundSize:`cover`,backgroundPosition:`center`,backgroundRepeat:`no-repeat`})}_clearRegionTimers(e){for(let[,t]of e)t.timer&&=(clearTimeout(t.timer),null)}async renderLayout(e,t){try{if(this.log.info(`Rendering layout ${t}`),this.currentLayoutId===t){this.log.info(`Replaying layout ${t} - reusing elements (no recreation!)`),this._clearRegionTimers(this.regions),this._stopAllRegionWidgets(this.regions,this._stopWidgetBound);for(let[,e]of this.regions)e.currentIndex=0,e.complete=!1;this.layoutTimer&&=(clearTimeout(this.layoutTimer),null),this.layoutEndEmitted=!1,this._deferredTimerLayoutId=null,this._deferredTimerFallback&&=(clearTimeout(this._deferredTimerFallback),null),this.emit(`layoutStart`,t,this.currentLayout);for(let[e,t]of this.regions)t.isDrawer||this.startRegion(e);this.startLayoutTimerWhenReady(t,this.currentLayout),this.log.info(`Layout ${t} restarted (reused elements)`),this._scheduleNextLayoutPreload(this.currentLayout);return}if(this.layoutPool.has(t)){this.log.info(`Layout ${t} found in preload pool - instant swap!`),await this._swapToPreloadedLayout(t);return}this.log.info(`Switching to new layout ${t}`),this.stopCurrentLayout();let n=this.parseXlf(e);if(this.currentLayout=n,this.currentLayoutId=t,this.calculateScale(n),this.container.style.backgroundColor=n.bgcolor,this.container.style.backgroundImage=``,n.background){let e=this.options.fileIdToSaveAs?.get(String(n.background))||n.background;this._applyBackgroundImage(this.container,this._mediaFileUrl(e)),this.log.info(`Background image set: ${n.background} → ${e}`)}for(let e of n.regions)await this.createRegion(e);this.log.info(`Pre-creating widget elements for instant transitions...`);for(let[e,t]of this.regions)for(let n=0;n<t.widgets.length;n++){let r=t.widgets[n];r.layoutId=this.currentLayoutId,r.regionId=e;try{let e=await this.createWidgetElement(r,t);this._positionWidgetElement(e),t.element.appendChild(e),t.widgetElements.set(r.id,e)}catch(e){this.log.error(`Failed to pre-create widget ${r.id}:`,e)}}if(this.log.info(`All widget elements pre-created`),this.attachActionListeners(n),this.emit(`layoutStart`,t,n),n.duration>0){let e=!this._hasUnprobedVideos();this.emit(`layoutDurationUpdated`,t,n.duration,e)}for(let[e,t]of this.regions)t.isDrawer||this.startRegion(e);this.startLayoutTimerWhenReady(t,n),this._scheduleNextLayoutPreload(n),this.log.info(`Layout ${t} started`)}catch(e){throw this.log.error(`Error rendering layout:`,e),this.emit(`error`,{type:`layoutError`,error:e,layoutId:t}),e}}_createRegionEntry(e,t,n,r={}){let{className:i=`renderer-lite-region`,...a}=r,o=document.createElement(`div`);o.id=t,o.className=i,o.style.position=`absolute`,o.style.zIndex=String(e.zindex),o.style.overflow=`hidden`,this.applyRegionScale(o,e),n.appendChild(o);let s=this.scaleFactor;return{element:o,config:e,widgets:e.widgets,currentIndex:0,timer:null,width:e.width*s,height:e.height*s,complete:!1,widgetElements:new Map,...a}}async createRegion(e){let t=this._createRegionEntry(e,`region_${e.id}`,this.container,{isDrawer:e.isDrawer||!1,isCanvas:e.isCanvas||!1});e.isDrawer&&(t.element.style.display=`none`);let n=e.widgets.filter(e=>this._isWidgetActive(e));n.some(e=>e.cyclePlayback)&&(n=this._applyCyclePlayback(n)),t.widgets=n,this.regions.set(e.id,t)}startRegion(e){let t=this.regions.get(e);this._startRegionCycle(t,e,this._renderWidgetBound,this._stopWidgetBound,()=>{this.log.info(`Region ${e} completed one full cycle`),this.checkLayoutComplete()})}async createWidgetElement(e,t){if(e.render===`html`&&e.type!==`pdf`)return await this.renderGenericWidget(e,t);switch(e.type){case`image`:return await this.renderImage(e,t);case`video`:return await this.renderVideo(e,t);case`audio`:return await this.renderAudio(e,t);case`text`:case`ticker`:return await this.renderTextWidget(e,t);case`pdf`:return await this.renderPdf(e,t);case`webpage`:return await this.renderWebpage(e,t);case`localvideo`:return await this.renderVideo(e,t);case`videoin`:return await this.renderVideoIn(e,t);case`powerpoint`:case`flash`:return this.log.warn(`Widget type '${e.type}' is not supported on web players (widget ${e.id})`),this._renderUnsupportedPlaceholder(e,t);default:return await this.renderGenericWidget(e,t)}}findMediaElement(e,t){return e.tagName===t?e:e.querySelector(t.toLowerCase())}updateMediaElement(e,t){let n=this.findMediaElement(e,`VIDEO`)||this.findMediaElement(e,`AUDIO`);if(n){if(n.tagName===`VIDEO`&&n._mediaConstraints&&!n._mediaStream){navigator.mediaDevices.getUserMedia(n._mediaConstraints).then(e=>{n.srcObject=e,n._mediaStream=e,this.log.info(`Webcam stream re-acquired for widget ${t.id}`)}).catch(e=>{this.log.warn(`Failed to re-acquire webcam stream:`,e.message)});return}this._restartMediaElement(n),this.log.info(`${n.tagName===`VIDEO`?`Video`:`Audio`} restarted: ${t.fileId||t.id}`)}}_restartMediaElement(e){e.currentTime=0;let t=()=>{e.removeEventListener(`seeked`,t),e.play().catch(()=>{})};e.addEventListener(`seeked`,t),e.play().catch(()=>{})}waitForWidgetReady(e,t){let n=1e4,r=this.findMediaElement(e,`VIDEO`);if(r)return!r.paused&&r.readyState>=3?Promise.resolve():new Promise(e=>{let i=setTimeout(()=>{this.log.warn(`Video ready timeout (${n}ms) for widget ${t.id}`),e()},n),a=()=>{r.removeEventListener(`playing`,a),clearTimeout(i),this.log.info(`Video widget ${t.id} ready (playing)`),e()};r.addEventListener(`playing`,a)});let i=this.findMediaElement(e,`AUDIO`);if(i)return!i.paused&&i.readyState>=3?Promise.resolve():new Promise(e=>{let r=setTimeout(()=>{this.log.warn(`Audio ready timeout (${n}ms) for widget ${t.id}`),e()},n),a=()=>{i.removeEventListener(`playing`,a),clearTimeout(r),this.log.info(`Audio widget ${t.id} ready (playing)`),e()};i.addEventListener(`playing`,a)});let a=this.findMediaElement(e,`IMG`);return a?a.complete&&a.naturalWidth>0?Promise.resolve():new Promise(e=>{let r=()=>{a.removeEventListener(`load`,r),clearTimeout(i),e()},i=setTimeout(()=>{a.removeEventListener(`load`,r),this.log.warn(`Image ready timeout for widget ${t.id}`),e()},n);a.addEventListener(`load`,r)}):Promise.resolve()}async startLayoutTimerWhenReady(e,t){if(!t||t.duration<=0)return;let n=[];for(let[e,t]of this.regions){if(t.widgets.length===0)continue;let e=t.widgets[t.currentIndex||0],r=t.widgetElements.get(e.id);r&&n.push(this.waitForWidgetReady(r,e))}if(n.length>0&&(this.log.info(`Waiting for ${n.length} widget(s) to be ready before starting layout timer...`),await Promise.all(n),this.log.info(`All widgets ready — starting layout timer`)),this.currentLayoutId!==e){this.log.warn(`Layout changed while waiting for widgets — skipping timer for ${e}`);return}if(t.isDynamic&&!t._durationFromMetadata&&this._hasUnprobedVideos()){this._deferredTimerLayoutId=e,this._layoutTimerStartedAt=Date.now(),this.log.info(`Layout ${e} has unprobed videos — deferring timer until metadata loads`),this._deferredTimerFallback=setTimeout(()=>{this._deferredTimerFallback=null,this._deferredTimerLayoutId===e&&!this.layoutTimer&&(this.log.warn(`Layout ${e}: metadata timeout after 30s — starting timer with ${t.duration}s estimate`),this._deferredTimerLayoutId=null,this._startLayoutTimer(e,t))},3e4);return}this._startLayoutTimer(e,t)}_hasUnprobedVideos(){for(let[,e]of this.regions)for(let t of e.widgets)if(t.type===`video`&&t.useDuration===0&&t._probed)return!1;for(let[,e]of this.regions)for(let t of e.widgets)if(t.type===`video`&&t.useDuration===0)return!0;return!1}_startLayoutTimer(e,t){this._deferredTimerLayoutId=null,this._deferredTimerFallback&&=(clearTimeout(this._deferredTimerFallback),null);let n=t.duration*1e3;this.log.info(`Layout ${e} will end after ${t.duration}s`),this._layoutTimerStartedAt=Date.now(),this._layoutTimerDurationMs=n,this.layoutTimer=setTimeout(()=>{this.log.info(`Layout ${e} duration expired (${t.duration}s)`),this.currentLayoutId&&(this.layoutEndEmitted=!0,this.emit(`layoutEnd`,this.currentLayoutId))},n)}async _showWidget(e,t){let n=e.widgets[t];if(!n)return null;let r=e.widgetElements.get(n.id);if(r||(this.log.warn(`Widget ${n.id} not pre-created, creating now`),r=await this.createWidgetElement(n,e),r.style.position=`absolute`,r.style.top=`0`,r.style.left=`0`,r.style.width=`100%`,r.style.height=`100%`,e.widgetElements.set(n.id,r),e.element.appendChild(r)),!e.isCanvas)for(let[t,r]of e.widgetElements)t!==n.id&&(r.getAnimations?.().forEach(e=>e.cancel()),r.style.visibility=`hidden`,r.style.opacity=`0`);return this.updateMediaElement(r,n),r.getAnimations?.().forEach(e=>e.cancel()),r.style.visibility=`visible`,n.transitions.in?u.apply(r,n.transitions.in,!0,e.width,e.height):r.style.opacity=`1`,r._pdfResume&&r._pdfResume(),this._startAudioOverlays(n),n}_startAudioOverlays(e){if(!e.audioNodes||e.audioNodes.length===0)return;this._stopAudioOverlays(e.id);let t=[];for(let n of e.audioNodes){if(!n.uri)continue;let r=document.createElement(`audio`);r.autoplay=!0,r.loop=n.loop,r.volume=Math.max(0,Math.min(1,n.volume/100)),r.src=n.uri?this._mediaFileUrl(n.uri):``,r.style.display=`none`,this.container.appendChild(r);let i=r.play();i&&i.catch&&i.catch(()=>{}),t.push(r),this.log.info(`Audio overlay started for widget ${e.id}: ${n.uri} (loop=${n.loop}, vol=${n.volume})`)}t.length>0&&this.audioOverlays.set(e.id,t)}_stopAudioOverlays(e){let t=this.audioOverlays.get(e);if(t){for(let e of t)e.pause(),e.removeAttribute(`src`),e.load(),e.parentNode&&e.parentNode.removeChild(e);this.audioOverlays.delete(e),this.log.info(`Audio overlays stopped for widget ${e}`)}}_hideWidget(e,t){let n=e.widgets[t];if(!n)return{widget:null,animPromise:null};let r=e.widgetElements.get(n.id);if(!r)return{widget:null,animPromise:null};let i=null;if(n.transitions.out){let t=u.apply(r,n.transitions.out,!1,e.width,e.height);t&&(i=new Promise(e=>{t.onfinish=e}))}let a=r.querySelector(`video`);if(a&&(a.pause(),a._mediaStream&&(a._mediaStream.getTracks().forEach(e=>e.stop()),a._mediaStream=null,a.srcObject=null),a._hlsInstance&&=(a._hlsInstance.destroy(),null),a.removeAttribute(`src`),a.load(),a._eventCleanup)){for(let[e,t]of a._eventCleanup)a.removeEventListener(e,t);a._eventCleanup=null}let o=r.querySelector(`audio`);if(o&&n.options.loop!==`1`&&o.pause(),o?._eventCleanup){for(let[e,t]of o._eventCleanup)o.removeEventListener(e,t);o._eventCleanup=null}this._stopAudioOverlays(n.id),r._pdfCleanup&&r._pdfCleanup();let s=r.querySelectorAll(`iframe`);for(let e of s){try{let t=e.contentDocument||e.contentWindow?.document;t&&(t.querySelectorAll(`video`).forEach(e=>{e.pause(),e.removeAttribute(`src`),e.load()}),t.querySelectorAll(`audio`).forEach(e=>{e.pause(),e.removeAttribute(`src`),e.load()}))}catch{}e.src=`about:blank`}return{widget:n,animPromise:i}}_isWidgetActive(e){let t=new Date;return!(e.fromDt&&t<new Date(e.fromDt)||e.toDt&&t>new Date(e.toDt))}_parseDurationComments(e,t){let n=t.duration,r=e.match(/<!--\s*DURATION=(\d+)\s*-->/);if(r){let e=parseInt(r[1],10);if(e>0){this.log.info(`Widget ${t.id}: DURATION comment overrides duration ${t.duration}→${e}s`),t.duration=e,t.duration!==n&&this.updateLayoutDuration();return}}let i=e.match(/<!--\s*NUMITEMS=(\d+)\s*-->/);if(i){let e=parseInt(i[1],10);if(e>0&&t.duration>0){let n=e*t.duration;this.log.info(`Widget ${t.id}: NUMITEMS=${e} × ${t.duration}s = ${n}s`),t.duration=n}}t.duration!==n&&this.updateLayoutDuration()}_applyCyclePlayback(e){this._subPlaylistCycleIndex||=new Map;let t=new Map,n=[];for(let r of e)r.parentWidgetId&&r.cyclePlayback?(t.has(r.parentWidgetId)||t.set(r.parentWidgetId,[]),t.get(r.parentWidgetId).push(r)):n.push({type:`direct`,widget:r});for(let[e,r]of t){r.sort((e,t)=>e.displayOrder-t.displayOrder);let t;if(r.some(e=>e.isRandom))t=r[Math.floor(Math.random()*r.length)];else{let n=this._subPlaylistCycleIndex.get(e)||{widgetIdx:0,playsDone:0};t=r[n.widgetIdx%r.length];let i=t.playCount||1;n.playsDone++,n.playsDone>=i&&(n.widgetIdx++,n.playsDone=0),this._subPlaylistCycleIndex.set(e,n)}this.log.info(`Sub-playlist cycle: group ${e} selected widget ${t.id} (${r.length} in group)`),n.push({type:`direct`,widget:t})}return n.map(e=>e.widget)}_startRegionCycle(e,t,n,r,i){if(!e||e.widgets.length===0)return;if(e.isCanvas){this._startCanvasRegion(e,t,n,i);return}if(e.widgets.length===1){n(t,0);return}let a=()=>{let o=e.currentIndex,s=e.widgets[o];n(t,o);let c=s.duration*1e3;this.log.info(`Region ${t} widget ${s.id} (${s.type}) playing for ${s.duration}s (useDuration=${s.useDuration}, index ${o}/${e.widgets.length})`),e.timer=setTimeout(()=>{this._handleWidgetCycleEnd(s,e,t,o,n,r,i,a)},c)};a()}_startCanvasRegion(e,t,n,r){for(let r=0;r<e.widgets.length;r++)n(t,r);let i=Math.max(...e.widgets.map(e=>e.duration))*1e3;i>0?e.timer=setTimeout(()=>{e.complete||(e.complete=!0,r?.())},i):(e.complete=!0,r?.())}_handleWidgetCycleEnd(e,t,n,r,i,a,o,s){e.webhookUrl&&this.emit(`widgetAction`,{type:`durationEnd`,widgetId:e.id,layoutId:this.currentLayoutId,regionId:n,url:e.webhookUrl}),a(n,r);let c=(t.currentIndex+1)%t.widgets.length;if(c===0&&!t.complete&&(t.complete=!0,o?.()),c===0&&t.config?.loop===!1&&t.widgets.length===1){i(n,0);return}this.layoutEndEmitted||(t.currentIndex=c,s())}async renderWidget(e,t){let n=this.regions.get(e);if(n)try{let r=await this._showWidget(n,t);if(r&&(this.log.info(`Showing widget ${r.type} (${r.id}) in region ${e}`),this._startedWidgets.add(`${e}:${t}`),this.emit(`widgetStart`,{widgetId:r.id,regionId:e,layoutId:this.currentLayoutId,mediaId:parseInt(r.fileId||r.id)||null,type:r.type,duration:r.duration,enableStat:r.enableStat}),r.commands&&r.commands.length>0))for(let t of r.commands)this.emit(`widgetCommand`,{commandCode:t.commandCode,commandString:t.commandString,widgetId:r.id,regionId:e,layoutId:this.currentLayoutId})}catch(r){this.log.error(`Error rendering widget:`,r),this.emit(`error`,{type:`widgetError`,error:r,widgetId:n.widgets[t]?.id,regionId:e})}}async stopWidget(e,t){let n=`${e}:${t}`;if(!this._startedWidgets.delete(n))return;let r=this.regions.get(e);if(!r)return;let{widget:i,animPromise:a}=this._hideWidget(r,t);i&&this.emit(`widgetEnd`,{widgetId:i.id,regionId:e,layoutId:this.currentLayoutId,mediaId:parseInt(i.fileId||i.id)||null,type:i.type,enableStat:i.enableStat}),a&&await a}_stopAllRegionWidgets(e,t){for(let[n,r]of e)if(r.isCanvas)for(let e=0;e<r.widgets.length;e++)t(n,e);else r.widgets.length>0&&t(n,r.currentIndex)}async renderImage(e,t){let n=document.createElement(`img`);n.className=`renderer-lite-widget`,n.style.width=`100%`,n.style.height=`100%`;let r=e.options.scaleType,i={stretch:`fill`,center:`contain`,fit:`cover`};n.style.objectFit=i[r]||`contain`;let a={left:`left`,center:`center`,right:`right`},o={top:`top`,middle:`center`,bottom:`bottom`},s=a[e.options.alignId]||`center`,c=o[e.options.valignId]||`center`;return n.style.objectPosition=`${s} ${c}`,n.style.opacity=`0`,n.src=e.options.uri?this._mediaFileUrl(e.options.uri):``,n}async renderVideo(t,n){let r=document.createElement(`video`);r.className=`renderer-lite-widget`,r.style.width=`100%`,r.style.height=`100%`;let i=t.options.scaleType,a={stretch:`fill`,center:`none`,fit:`contain`};r.style.objectFit=a[i]||`contain`,r.style.opacity=`1`,r.autoplay=!0,r.preload=`auto`,r.muted=t.options.mute===`1`,r.loop=!1,r.controls=!1,r.playsInline=!0;let o=t.options.uri||``,s=t.fileId||t.id,c=()=>{t.options.loop===`1`?(r.currentTime=0,this.log.info(`Video ${o} ended - reset to start, waiting for widget cycle to replay`)):this.log.info(`Video ${o} ended - paused on last frame`)};r.addEventListener(`ended`,c);let l=o?this._mediaFileUrl(o):``;if(l.includes(`.m3u8`))if(r.canPlayType(`application/vnd.apple.mpegurl`))this.log.info(`HLS stream (native): ${s}`),r.src=l;else try{let{default:t}=await e(async()=>{let{default:e}=await import(`hls.js`);return{default:e}},[],import.meta.url);if(t.isSupported()){let e=new t({enableWorker:!0,lowLatencyMode:!0});e.loadSource(l),e.attachMedia(r),r._hlsInstance=e,e.on(t.Events.ERROR,(t,n)=>{n.fatal&&(this.log.error(`HLS fatal error: ${n.type}`,n.details),e.destroy(),r._hlsInstance=null)}),this.log.info(`HLS stream (hls.js): ${s}`)}else this.log.warn(`HLS not supported on this browser for ${s}`),r.src=l}catch(e){this.log.warn(`hls.js not available, falling back to native: ${e.message}`),r.src=l}else r.src=l;let u=this._preloadingLayoutId||this.currentLayoutId,d=()=>{let e=r.duration;this.log.info(`Video ${o} duration detected: ${e}s`),(t.duration===0||t.useDuration===0)&&(t.duration=e,t._probed=!0,this.log.info(`Updated widget ${t.id} duration to ${e}s (useDuration=0)`),this.currentLayoutId===u?this.updateLayoutDuration():this.log.info(`Video ${o} duration set but layout timer not updated (preloaded for layout ${u}, current is ${this.currentLayoutId})`))};r.addEventListener(`loadedmetadata`,d);let f=()=>{this.log.info(`Video loaded and ready:`,o)};r.addEventListener(`loadeddata`,f);let p=()=>{let e=r.error,n=e?.code,i=e?.message||`Unknown error`;this.log.warn(`Video error: ${o}, code: ${n}, time: ${r.currentTime.toFixed(1)}s, message: ${i}`),t.useDuration===0&&t.duration===0&&(t.duration=60,this.log.info(`Set fallback duration 60s for errored widget ${t.id}`),this.currentLayoutId===u&&this.updateLayoutDuration()),this.emit(`videoError`,{storedAs:o,fileId:s,errorCode:n,errorMessage:i,currentTime:r.currentTime})};r.addEventListener(`error`,p);let m=()=>{this.log.info(`Video playing:`,o)};return r.addEventListener(`playing`,m),r._eventCleanup=[[`ended`,c],[`loadedmetadata`,d],[`loadeddata`,f],[`error`,p],[`playing`,m]],this.log.info(`Video element created:`,o,r.src),r}async renderVideoIn(e,t){let n=document.createElement(`video`);n.className=`renderer-lite-widget`,n.style.width=`100%`,n.style.height=`100%`,n.style.objectFit=e.options.showFullScreen===`1`?`cover`:`contain`,n.autoplay=!0,n.playsInline=!0,n.controls=!1,n.muted=e.options.mute!==`0`,e.options.mirror===`1`&&(n.style.transform=`scaleX(-1)`);let r={width:{ideal:t.width},height:{ideal:t.height}},i=e.options.sourceId||e.options.deviceId;i?r.deviceId={exact:i}:r.facingMode=e.options.facingMode||`environment`;let a={video:r,audio:e.options.captureAudio===`1`};n._mediaConstraints=a;try{let t=await navigator.mediaDevices.getUserMedia(a);n.srcObject=t,n._mediaStream=t,this.log.info(`Webcam stream acquired for widget ${e.id} (tracks: ${t.getTracks().length})`)}catch(n){return this.log.warn(`getUserMedia failed for widget ${e.id}: ${n.message}`),this._renderUnsupportedPlaceholder({...e,type:`Camera unavailable`},t)}return n}async renderAudio(e,t){let n=document.createElement(`div`);n.className=`renderer-lite-widget audio-widget`,n.style.width=`100%`,n.style.height=`100%`,n.style.display=`flex`,n.style.flexDirection=`column`,n.style.alignItems=`center`,n.style.justifyContent=`center`,n.style.background=`linear-gradient(135deg, #667eea 0%, #764ba2 100%)`,n.style.opacity=`0`;let r=document.createElement(`audio`);r.autoplay=!0,r.loop=e.options.loop===`1`,r.volume=parseFloat(e.options.volume||`100`)/100;let i=e.options.uri||``;e.fileId||e.id,r.src=i?this._mediaFileUrl(i):``;let a=()=>{e.options.loop===`1`?(r.currentTime=0,this.log.info(`Audio ${i} ended - reset to start, waiting for widget cycle to replay`)):this.log.info(`Audio ${i} ended - playback complete`)};r.addEventListener(`ended`,a);let o=this._preloadingLayoutId||this.currentLayoutId,s=()=>{let t=Math.floor(r.duration);this.log.info(`Audio ${i} duration detected: ${t}s`),(e.duration===0||e.useDuration===0)&&(e.duration=t,this.log.info(`Updated widget ${e.id} duration to ${t}s (useDuration=0)`),this.currentLayoutId===o?this.updateLayoutDuration():this.log.info(`Audio ${i} duration set but layout timer not updated (preloaded for layout ${o}, current is ${this.currentLayoutId})`))};r.addEventListener(`loadedmetadata`,s);let c=()=>{let e=r.error;this.log.warn(`Audio error (non-fatal): ${i}, code: ${e?.code}, message: ${e?.message||`Unknown`}`)};r.addEventListener(`error`,c),r._eventCleanup=[[`ended`,a],[`loadedmetadata`,s],[`error`,c]];let l=document.createElement(`div`);l.innerHTML=`♪`,l.style.fontSize=`120px`,l.style.color=`white`,l.style.marginBottom=`20px`;let u=document.createElement(`div`);u.style.color=`white`,u.style.fontSize=`24px`,u.textContent=`Playing Audio`;let d=document.createElement(`div`);return d.style.color=`rgba(255,255,255,0.7)`,d.style.fontSize=`16px`,d.style.marginTop=`10px`,d.textContent=e.options.uri,n.appendChild(r),n.appendChild(l),n.appendChild(u),n.appendChild(d),n}async renderTextWidget(e,t){return await this._renderIframeWidget(e,t)}async renderPdf(t,r){let i=document.createElement(`div`);if(i.className=`renderer-lite-widget pdf-widget`,i.style.width=`100%`,i.style.height=`100%`,i.style.backgroundColor=`transparent`,i.style.opacity=`0`,i.style.position=`relative`,window.pdfjsLib===void 0)try{let t=await e(()=>import(`./pdf-Bxz9Nzto.js`),__vite__mapDeps([0,1]),import.meta.url);window.pdfjsLib=t;let n=window.location.pathname.replace(/\/[^/]*$/,`/`);window.pdfjsLib.GlobalWorkerOptions.workerSrc=`${window.location.origin}${n}pdf.worker.min.mjs`}catch(e){return this.log.error(`PDF.js not available:`,e),i.innerHTML=`<div style="color:white;padding:20px;text-align:center;">PDF viewer unavailable</div>`,i.style.opacity=`1`,i}let a=t.options.uri?this._mediaFileUrl(t.options.uri):``;try{let e=await window.pdfjsLib.getDocument(a).promise,o=e.numPages,s=t.duration||60,c=s*1e3/o;this.log.info(`[pdf] PDF loaded: ${o} pages, ${s}s duration, ${(c/1e3).toFixed(1)}s/page`);let l=await e.getPage(1),u=l.getViewport({scale:1}),d=Math.min(r.width/u.width,r.height/u.height);l.cleanup();let f=document.createElement(`canvas`);f.className=`pdf-page`,f.width=Math.floor(u.width*d),f.height=Math.floor(u.height*d),f.style.cssText=`display:block;margin:auto;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);`;let p=f.getContext(`2d`);i.appendChild(f);let m=document.createElement(`div`);m.style.cssText=`position:absolute;bottom:10px;right:10px;background:rgba(0,0,0,0.7);color:white;padding:8px 12px;border-radius:4px;font:14px system-ui;z-index:1;`,n()||(m.style.display=`none`),i.appendChild(m);let h=1,g=null,_=null,v=!1,y=async()=>{if(v)return;m.textContent=`Page ${h} / ${o}`;let t=await e.getPage(h),n=t.getViewport({scale:d});p.clearRect(0,0,f.width,f.height),_=t.render({canvasContext:p,viewport:n});try{await _.promise}catch(e){if(v)return;throw e}_=null,t.cleanup(),o>1&&!v&&(g=setTimeout(()=>{h=h>=o?1:h+1,y()},c))};await y();let b=null;i._pdfCleanup=()=>{if(v=!0,g&&clearTimeout(g),g=null,_){let e=_;_=null,e.cancel(),b=e.promise.catch(()=>{})}},i._pdfResume=async()=>{i._pdfCleanup(),b&&=(await b,null),v=!1,h=1,y()},i._pdfDestroy=()=>{i._pdfCleanup(),f.width=0,f.height=0,e.destroy()}}catch(e){this.log.error(`PDF render failed:`,e),i.innerHTML=`<div style="color:white;padding:20px;text-align:center;">Failed to load PDF</div>`}return i.style.opacity=`1`,i}async renderWebpage(e,t){if(parseInt(e.options.modeId||`1`)===0)return await this.renderGenericWidget(e,t);let n=document.createElement(`iframe`);return n.className=`renderer-lite-widget`,n.style.width=`100%`,n.style.height=`100%`,n.style.border=`none`,n.style.opacity=`0`,n.src=decodeURIComponent(e.options.uri||``),n}async renderGenericWidget(e,t){return await this._renderIframeWidget(e,t)}async _renderIframeWidget(e,t){let n=document.createElement(`iframe`);n.className=`renderer-lite-widget`,n.style.width=`100%`,n.style.height=`100%`,n.style.border=`none`,n.style.opacity=`0`;let r=e.raw;if(this.options.getWidgetHtml){let t=await this.options.getWidgetHtml(e);if(t&&typeof t==`object`&&t.url)return n.src=t.url,t.fallback&&this._parseDurationComments(t.fallback,e),n;r=t}if(r){this._parseDurationComments(r,e);let t=new Blob([r],{type:`text/html`}),i=URL.createObjectURL(t);n.src=i,this.trackBlobUrl(i)}else this.log.warn(`No HTML for widget ${e.id}`),n.srcdoc=`<div style="padding:20px;">Widget content unavailable</div>`;return n}_renderUnsupportedPlaceholder(e,t){let n=document.createElement(`div`);return n.className=`renderer-lite-widget`,n.style.width=`100%`,n.style.height=`100%`,n.style.display=`flex`,n.style.alignItems=`center`,n.style.justifyContent=`center`,n.style.backgroundColor=`#111`,n.style.color=`#666`,n.style.fontSize=`14px`,n.textContent=`Unsupported: ${e.type}`,n}_scheduleNextLayoutPreload(e){this.preloadTimer&&=(clearTimeout(this.preloadTimer),null),this._preloadRetryTimer&&=(clearTimeout(this._preloadRetryTimer),null);let t=e.duration||60,n=t*1e3*.75,r=t*1e3*.9;this.log.info(`Scheduling next layout preload in ${(n/1e3).toFixed(1)}s (75% of ${t}s)`),this.preloadTimer=setTimeout(()=>{this.preloadTimer=null,this.emit(`request-next-layout-preload`)},n),this._preloadRetryTimer=setTimeout(()=>{this._preloadRetryTimer=null,this.emit(`request-next-layout-preload`)},r)}hasPreloadedLayout(e){return this.layoutPool.has(e)}async preloadLayout(e,t){return this.layoutPool.has(t)?(this.log.info(`Layout ${t} already in preload pool, skipping`),!0):this.currentLayoutId===t?(this.log.info(`Layout ${t} is current, skipping preload`),!0):this._preloadingLayoutId===t&&this._preloadingPromise?(this.log.info(`Layout ${t} preload in-flight, waiting for it...`),this._preloadingPromise):(this._preloadingPromise=this._doPreloadLayout(e,t),this._preloadingPromise)}async _doPreloadLayout(e,t){try{this.log.info(`Preloading layout ${t} into pool...`);let n=this.parseXlf(e);this.calculateScale(n);let r=document.createElement(`div`);if(r.id=`preload_layout_${t}`,r.className=`renderer-lite-preload-wrapper`,r.style.position=`absolute`,r.style.top=`0`,r.style.left=`0`,r.style.width=`100%`,r.style.height=`100%`,r.style.visibility=`hidden`,r.style.zIndex=`-1`,r.style.backgroundColor=n.bgcolor,n.background){let e=this.options.fileIdToSaveAs?.get(String(n.background))||n.background;this._applyBackgroundImage(r,this._mediaFileUrl(e))}let i=this.currentLayoutId,a=new Map;for(let e of n.regions){let n=this._createRegionEntry(e,`preload_region_${t}_${e.id}`,r);a.set(e.id,n)}let o=new Set,s=this.layoutBlobUrls;this.layoutBlobUrls=new Map,this.layoutBlobUrls.set(t,o),this._preloadingLayoutId=t;for(let[e,n]of a)for(let r=0;r<n.widgets.length;r++){let i=n.widgets[r];i.layoutId=t,i.regionId=e;try{let e=await this.createWidgetElement(i,n);this._positionWidgetElement(e),n.element.appendChild(e),n.widgetElements.set(i.id,e)}catch(e){this.log.error(`Preload: Failed to create widget ${i.id}:`,e)}}return this.currentLayoutId=i,r.querySelectorAll(`video`).forEach(e=>e.pause()),(this.layoutBlobUrls.get(t)||new Set).forEach(e=>o.add(e)),this.layoutBlobUrls=s,this.container.appendChild(r),this.layoutPool.add(t,{container:r,layout:n,regions:a,blobUrls:o}),this.log.info(`Layout ${t} preloaded into pool (${a.size} regions)`),!0}catch(e){return this.log.error(`Preload failed for layout ${t}:`,e),!1}finally{this._preloadingLayoutId===t&&(this._preloadingLayoutId=null,this._preloadingPromise=null)}}async _swapToPreloadedLayout(e){let t=this.layoutPool.get(e);if(!t){this.log.error(`Cannot swap: layout ${e} not in pool`);return}let n=this._resolveLayoutTransition(t.layout);return n.type===`instant`?this._swapToPreloadedLayoutInstant(e,t):this._swapToPreloadedLayoutWithTransition(e,t,n)}async _swapToPreloadedLayoutInstant(e,t){this.removeActionListeners(),this._clearLayoutTimers();let n=this.currentLayoutId,r=this.layoutEndEmitted;if(this.layoutEndEmitted=!1,n&&this.layoutPool.has(n))this._clearRegionTimers(this.regions),this._stopAllRegionWidgets(this.regions,this._stopWidgetBound),this.layoutPool.evict(n);else{this._clearRegionTimers(this.regions),this._stopAllRegionWidgets(this.regions,this._stopWidgetBound);for(let[,e]of this.regions)if(l.releaseMediaElements(e.element),e.config?.exitTransition){let t=u.apply(e.element,e.config.exitTransition,!1,e.width,e.height);if(t){let n=e.element;t.onfinish=()=>n.remove()}else e.element.remove()}else e.element.remove();n&&this.revokeBlobUrlsForLayout(n)}this.currentLayout=null,this.currentLayoutId=null,this.regions.clear(),this._activatePreloadedLayout(e,t,n,r),this.log.info(`Swapped to preloaded layout ${e} (instant transition)`),this._logResourceStats(e)}async _swapToPreloadedLayoutWithTransition(e,t,n){this.removeActionListeners(),this._clearLayoutTimers();let r=this.currentLayoutId,i=this.layoutEndEmitted;this.layoutEndEmitted=!1;let a=this.regions,o=r!==null&&this.layoutPool.has(r),s=o?this.layoutPool.get(r).container:null;this._clearRegionTimers(a),this._stopAllRegionWidgets(a,this._stopWidgetBound),this.currentLayout=null,this.currentLayoutId=null,this.regions=new Map,t.container.style.visibility=`visible`,t.container.style.zIndex=`1`,n.type===`fade`&&(t.container.style.opacity=`0`),this._activatePreloadedLayout(e,t,r,i);let c=t.layout.width,l=t.layout.height,d=u.apply(t.container,n,!0,c,l),f=null;s&&(n.type===`fade`||n.type===`slide`)&&(f=u.apply(s,n,!1,c,l));let p=()=>{if(t.container.style.zIndex=`0`,t.container.style.opacity=``,this._teardownOldLayoutAfterTransition(r,a,o),f)try{f.cancel()}catch{}this.log.info(`Swapped to preloaded layout ${e} (${n.type} transition, ${n.duration}ms)`),this._logResourceStats(e)};d?(d.onfinish=p,setTimeout(p,Math.ceil(n.duration*1.5))):p()}_activatePreloadedLayout(e,t,n,r){if(t.container.style.visibility=`visible`,t.container.style.zIndex!==`1`&&(t.container.style.zIndex=`0`),this.layoutPool.setHot(e),this.currentLayout=t.layout,this.currentLayoutId=e,this.regions=t.regions,n&&!r&&this.emit(`layoutEnd`,n),this.container.style.backgroundColor=t.layout.bgcolor,t.container.style.backgroundImage)for(let e of[`backgroundImage`,`backgroundSize`,`backgroundPosition`,`backgroundRepeat`])this.container.style[e]=t.container.style[e];else this.container.style.backgroundImage=``;this.calculateScale(t.layout),this.attachActionListeners(t.layout),this.emit(`layoutStart`,e,t.layout);for(let[e,t]of this.regions)t.currentIndex=0,t.complete=!1,this.startRegion(e);this.updateLayoutDuration(),this.startLayoutTimerWhenReady(e,t.layout),this.preloadTimer||this._scheduleNextLayoutPreload(t.layout)}_teardownOldLayoutAfterTransition(e,t,n){if(n&&e!==null){this.layoutPool.evict(e);return}for(let[,e]of t)if(l.releaseMediaElements(e.element),e.config?.exitTransition){let t=u.apply(e.element,e.config.exitTransition,!1,e.width,e.height);if(t){let n=e.element;t.onfinish=()=>n.remove()}else e.element.remove()}else e.element.remove();e&&this.revokeBlobUrlsForLayout(e)}_logResourceStats(e){let t=document.querySelectorAll(`*`).length,n=document.querySelectorAll(`video`).length,r=document.querySelectorAll(`video[src]`).length,i=document.querySelectorAll(`canvas`).length,a=document.querySelectorAll(`iframe`).length,o=document.querySelectorAll(`img`).length,s=this.layoutPool?this.layoutPool.size:0,c=this.regions?this.regions.size:0,l=[...this.regions?.values()||[]].reduce((e,t)=>e+(t.widgetElements?.size||0),0),u=performance?.memory?{used:Math.round(performance.memory.usedJSHeapSize/1048576),total:Math.round(performance.memory.totalJSHeapSize/1048576),limit:Math.round(performance.memory.jsHeapSizeLimit/1048576)}:null,d=this._blobUrls?[...this._blobUrls.values()].reduce((e,t)=>e+t.size,0):0,f=this._blobUrls?this._blobUrls.size:0,p=document.querySelectorAll(`.renderer-lite-preload-wrapper`).length,m=document.querySelectorAll(`audio`).length,h=u?`heap=${u.used}/${u.total}MB (limit ${u.limit}MB)`:`heap=N/A`;this.log.info(`[Resources] layout=${e} dom=${t} videos=${n}(src=${r}) canvas=${i} iframe=${a} img=${o} audio=${m} pool=${s} preloadWrappers=${p} regions=${c} widgets=${l} blobs=${d}(${f} layouts) ${h}`)}getCurrentLayoutId(){return this.currentLayoutId}showLayout(e){if(e===void 0&&(e=this.layoutPool.getLatest(),e===void 0)){this.log.warn(`showLayout: no preloaded layout to show`);return}if(this.currentLayoutId===e){this.log.info(`showLayout: layout ${e} already showing`);return}if(!this.layoutPool.has(e)){this.log.warn(`showLayout: layout ${e} not in preload pool`);return}this._swapToPreloadedLayout(e)}hasActiveLayoutTimer(){return this.layoutTimer!==null||this._deferredTimerLayoutId!==null}checkLayoutComplete(){let e=!0;for(let[t,n]of this.regions)if(n.widgets.length>1&&!n.complete){e=!1;break}e&&this.currentLayoutId&&this.log.info(`All multi-widget regions completed one cycle`)}_clearLayoutTimers(){this.layoutTimer&&=(clearTimeout(this.layoutTimer),null),this.preloadTimer&&=(clearTimeout(this.preloadTimer),null),this._preloadRetryTimer&&=(clearTimeout(this._preloadRetryTimer),null)}stopCurrentLayout(){if(!this.currentLayout)return;this.log.info(`Stopping layout ${this.currentLayoutId}`);let e=this.currentLayoutId,t=e&&!this.layoutEndEmitted;if(this.layoutEndEmitted=!1,this._deferredTimerLayoutId=null,this._deferredTimerFallback&&=(clearTimeout(this._deferredTimerFallback),null),this.currentLayout=null,this.currentLayoutId=null,this._clearLayoutTimers(),this.removeActionListeners(),e&&this.layoutPool.has(e))this.layoutPool.evict(e);else{e&&this.revokeBlobUrlsForLayout(e),this._clearRegionTimers(this.regions),this._stopAllRegionWidgets(this.regions,this._stopWidgetBound);for(let[,e]of this.regions)if(l.releaseMediaElements(e.element),e.config?.exitTransition){let t=u.apply(e.element,e.config.exitTransition,!1,e.width,e.height);if(t){let n=e.element;t.onfinish=()=>n.remove()}else e.element.remove()}else e.element.remove()}this.regions.clear(),t&&this.emit(`layoutEnd`,e)}async renderOverlay(e,t,n=0){try{if(this.log.info(`Rendering overlay ${t} (priority ${n})`),this.activeOverlays.has(t)){this.log.warn(`Overlay ${t} already active, skipping`);return}let r=this.parseXlf(e),i=document.createElement(`div`);i.id=`overlay_${t}`,i.className=`renderer-lite-overlay`,i.style.position=`absolute`,i.style.top=`0`,i.style.left=`0`,i.style.width=`100%`,i.style.height=`100%`,i.style.zIndex=String(1e3+n),i.style.pointerEvents=`auto`,i.style.backgroundColor=r.bgcolor,this.calculateScale(r);let a=new Map;for(let e of r.regions){let n=this._createRegionEntry(e,`overlay_${t}_region_${e.id}`,i,{className:`renderer-lite-region overlay-region`,isCanvas:e.isCanvas||!1});a.set(e.id,n)}for(let[e,n]of a)for(let r of n.widgets){r.layoutId=t,r.regionId=e;try{let e=await this.createWidgetElement(r,n);this._positionWidgetElement(e),n.element.appendChild(e),n.widgetElements.set(r.id,e)}catch(e){this.log.error(`Failed to pre-create overlay widget ${r.id}:`,e)}}this.overlayContainer.appendChild(i),this.activeOverlays.set(t,{container:i,layout:r,regions:a,timer:null,priority:n}),this.emit(`overlayStart`,t,r);for(let[e,n]of a)this.startOverlayRegion(t,e);if(r.duration>0){let e=r.duration*1e3,n=this.activeOverlays.get(t);n&&(n.timer=setTimeout(()=>{this.log.info(`Overlay ${t} duration expired (${r.duration}s)`),this.emit(`overlayEnd`,t)},e))}this.log.info(`Overlay ${t} started`)}catch(e){throw this.log.error(`Error rendering overlay:`,e),this.emit(`error`,{type:`overlayError`,error:e,layoutId:t}),e}}startOverlayRegion(e,t){let n=this.activeOverlays.get(e);if(!n)return;let r=n.regions.get(t);this._startRegionCycle(r,t,(t,n)=>this.renderOverlayWidget(e,t,n),(t,n)=>this.stopOverlayWidget(e,t,n),()=>this.log.info(`Overlay ${e} region ${t} completed one full cycle`))}async renderOverlayWidget(e,t,n){let r=this.activeOverlays.get(e);if(!r)return;let i=r.regions.get(t);if(i)try{let r=await this._showWidget(i,n);r&&(this.log.info(`Showing overlay widget ${r.type} (${r.id}) in overlay ${e} region ${t}`),this._startedWidgets.add(`overlay:${e}:${t}:${n}`),this.emit(`overlayWidgetStart`,{overlayId:e,widgetId:r.id,regionId:t,type:r.type,duration:r.duration}))}catch(r){this.log.error(`Error rendering overlay widget:`,r),this.emit(`error`,{type:`overlayWidgetError`,error:r,widgetId:i.widgets[n]?.id,regionId:t,overlayId:e})}}async stopOverlayWidget(e,t,n){let r=`overlay:${e}:${t}:${n}`;if(!this._startedWidgets.delete(r))return;let i=this.activeOverlays.get(e);if(!i)return;let a=i.regions.get(t);if(!a)return;let{widget:o,animPromise:s}=this._hideWidget(a,n);o&&this.emit(`overlayWidgetEnd`,{overlayId:e,widgetId:o.id,regionId:t,type:o.type}),s&&await s}stopOverlay(e){let t=this.activeOverlays.get(e);if(!t){this.log.warn(`Overlay ${e} not active`);return}this.log.info(`Stopping overlay ${e}`),t.timer&&=(clearTimeout(t.timer),null);for(let[,e]of t.regions)e.timer&&=(clearTimeout(e.timer),null);this._stopAllRegionWidgets(t.regions,(t,n)=>this.stopOverlayWidget(e,t,n)),t.container&&t.container.remove(),this.revokeBlobUrlsForLayout(e),this.activeOverlays.delete(e),this.emit(`overlayEnd`,e),this.log.info(`Overlay ${e} stopped`)}stopAllOverlays(){let e=Array.from(this.activeOverlays.keys());for(let t of e)this.stopOverlay(t);this.log.info(`All overlays stopped`)}getActiveOverlays(){return Array.from(this.activeOverlays.keys())}pause(){if(!this._paused){this._paused=!0;for(let[,e]of this.regions)e.timer&&=(clearTimeout(e.timer),null);this._forEachMedia(e=>e.pause()),this.emit(`paused`),this.log.info(`Playback paused (layout timer continues)`)}}isPaused(){return this._paused}resume(){if(this._paused){this._paused=!1,this._forEachMedia(e=>e.play().catch(()=>{}));for(let[e]of this.regions)this.startRegion(e);this.emit(`resumed`),this.log.info(`Playback resumed`)}}_forEachMedia(e){for(let[,t]of this.regions)t.element?.querySelectorAll(`video, audio`).forEach(e)}cleanup(){this.stopAllOverlays(),this.stopCurrentLayout(),this._startedWidgets.clear();for(let e of this.audioOverlays.keys())this._stopAudioOverlays(e);this.layoutPool.clear(),this.preloadTimer&&=(clearTimeout(this.preloadTimer),null),this._preloadRetryTimer&&=(clearTimeout(this._preloadRetryTimer),null),this.resizeObserver&&=(this.resizeObserver.disconnect(),null),this.container.innerHTML=``,this.log.info(`Cleaned up`)}},f=i(`Layout`),p=/^(#[0-9a-fA-F]{3,8}|rgba?\(\s*[\d.,\s%]+\)|[a-zA-Z]{1,20}|transparent|inherit)$/,m=e=>p.test(e)?e:`#000000`,h=e=>JSON.stringify(e),g=class{constructor(e){this.xmds=e}async translateXLF(e,t){let n=new DOMParser().parseFromString(t,`text/xml`),r=n.querySelector(`layout`);if(!r)throw Error(`Invalid XLF: no <layout> element`);let i=parseInt(r.getAttribute(`width`)||`1920`),a=parseInt(r.getAttribute(`height`)||`1080`),o=m(r.getAttribute(`bgcolor`)||`#000000`),s=[];for(let t of n.querySelectorAll(`region`))s.push(await this.translateRegion(e,t));return this.generateHTML(i,a,o,s)}async translateRegion(e,t){let n=t.getAttribute(`id`),r=parseInt(t.getAttribute(`width`)),i=parseInt(t.getAttribute(`height`)),a=parseInt(t.getAttribute(`top`)),o=parseInt(t.getAttribute(`left`)),s=parseInt(t.getAttribute(`zindex`)||`0`),c=[];for(let r of t.querySelectorAll(`media`))c.push(await this.translateMedia(e,n,r));return{id:n,width:r,height:i,top:a,left:o,zindex:s,media:c}}async translateMedia(e,t,n){let i=n.getAttribute(`type`),o=parseInt(n.getAttribute(`duration`)||`10`),s=n.getAttribute(`id`),c=n.querySelector(`options`),l=n.querySelector(`raw`),u={};if(c)for(let e of c.children)u[e.tagName]=e.textContent;let d={in:null,out:null},p=n.querySelector(`options > transIn`),m=n.querySelector(`options > transOut`),h=n.querySelector(`options > transInDuration`),g=n.querySelector(`options > transOutDuration`),_=n.querySelector(`options > transInDirection`),v=n.querySelector(`options > transOutDirection`);p&&p.textContent&&(d.in={type:p.textContent,duration:parseInt(h?.textContent||`1000`),direction:_?.textContent||`N`}),m&&m.textContent&&(d.out={type:m.textContent,duration:parseInt(g?.textContent||`1000`),direction:v?.textContent||`N`});let y=l?l.textContent:``;if([`clock`,`clock-digital`,`clock-analogue`,`calendar`,`weather`,`currencies`,`stocks`,`twitter`,`global`,`embedded`,`text`,`ticker`].some(e=>i.includes(e))){let n=null;for(let r=1;r<=3;r++)try{f.info(`Fetching resource for ${i} widget (layout=${e}, region=${t}, media=${s}) - attempt ${r}/3`),y=await this.xmds.getResource(e,t,s),f.info(`Got resource HTML (${y.length} chars)`),u.widgetCacheKey=await a(e,t,s,y);break}catch(e){if(n=e,f.warn(`Failed to get resource (attempt ${r}/3):`,e.message),r<3){let e=r*2e3;f.info(`Retrying in ${e}ms...`),await new Promise(t=>setTimeout(t,e))}}if(!y&&n){f.warn(`All retries failed, checking for cached widget HTML...`);try{let n=await fetch(`/store${r}/widgets/${e}/${t}/${s}`);n.ok?(y=await n.text(),u.widgetCacheKey=`${r}/widgets/${e}/${t}/${s}`,f.info(`Using stored widget HTML (${y.length} chars) - CMS update pending`)):(f.error(`No stored version available for widget ${s}`),y=`<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#999;font-size:18px;">Content updating...</div>`)}catch(e){f.error(`Store fallback failed:`,e),y=`<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#999;font-size:18px;">Content updating...</div>`}}}return{type:i,duration:o,id:s,options:u,raw:y,transitions:d}}generateHTML(e,t,n,r){return`<!DOCTYPE html>
|
|
2
|
+
import{t as e}from"./preload-helper-Chd9yIcd.js";import{c as t,d as n,t as r,u as i}from"./src-BtVLiVYZ.js";import{n as a}from"./src-B_BNICay.js";import{r as o}from"./src-CROvYSP8.js";var s={name:`@xiboplayer/renderer`,version:`0.7.21`,description:`RendererLite - Fast, efficient XLF layout rendering engine`,type:`module`,main:`./src/index.js`,types:`./src/index.d.ts`,exports:{".":`./src/index.js`,"./renderer-lite":`./src/renderer-lite.js`,"./layout":`./src/layout.js`},scripts:{test:`vitest run`,"test:watch":`vitest`,"test:coverage":`vitest run --coverage`},dependencies:{"@xiboplayer/cache":`workspace:*`,"@xiboplayer/schedule":`workspace:*`,"@xiboplayer/utils":`workspace:*`,"pdfjs-dist":`^5.6.205`},devDependencies:{jsdom:`^29.0.2`,vitest:`^4.1.2`},keywords:[`xibo`,`digital-signage`,`renderer`,`xlf`,`layout`],author:`Pau Aliagas <linuxnow@gmail.com>`,license:`AGPL-3.0-or-later`,repository:{type:`git`,url:`git+https://github.com/xibo-players/xiboplayer.git`,directory:`packages/renderer`},homepage:`https://xiboplayer.org`},c=i(`LayoutPool`),l=class e{constructor(e=2){this.layouts=new Map,this.maxSize=e,this.hotLayoutId=null}has(e){return this.layouts.has(e)}get(e){return this.layouts.get(e)}add(e,t){if(this.layouts.has(e)){let n=this.layouts.get(e);Object.assign(n,t),n.lastAccess=Date.now();return}this.layouts.size>=this.maxSize&&this.evictLRU(),t.status=`warm`,t.lastAccess=Date.now(),this.layouts.set(e,t),c.info(`Added layout ${e} to pool (size: ${this.layouts.size}/${this.maxSize})`)}setHot(e){if(this.hotLayoutId!==null&&this.layouts.has(this.hotLayoutId)&&(this.layouts.get(this.hotLayoutId).status=`warm`),this.layouts.has(e)){let t=this.layouts.get(e);t.status=`hot`,t.lastAccess=Date.now()}this.hotLayoutId=e}evict(t){let n=this.layouts.get(t);if(n){if(c.info(`Evicting layout ${t} from pool`),n.regions)for(let[e,t]of n.regions)t.timer&&=(clearTimeout(t.timer),null);if(n.container&&e.releaseMediaElements(n.container),n.blobUrls&&n.blobUrls.size>0&&(n.blobUrls.forEach(e=>{URL.revokeObjectURL(e)}),c.info(`Revoked ${n.blobUrls.size} blob URLs for layout ${t}`)),n.mediaUrlCache)for(let[e,t]of n.mediaUrlCache)t&&typeof t==`string`&&t.startsWith(`blob:`)&&URL.revokeObjectURL(t);n.container&&n.container.parentNode&&n.container.remove(),this.layouts.delete(t),this.hotLayoutId===t&&(this.hotLayoutId=null)}}static releaseMediaElements(t){requestAnimationFrame(()=>e._releaseMediaElementsSync(t))}static _releaseMediaElementsSync(e){let t=0,n=0;e.querySelectorAll(`video`).forEach(e=>{e._hlsInstance&&(e._hlsInstance.destroy(),e._hlsInstance=null,n++),e._mediaStream&&(e._mediaStream.getTracks().forEach(e=>e.stop()),e._mediaStream=null,e.srcObject=null),e.pause(),e.removeAttribute(`src`),e.load(),t++}),e.querySelectorAll(`audio`).forEach(e=>{e.pause(),e.removeAttribute(`src`),e.load()});let r=0;e.querySelectorAll(`iframe`).forEach(e=>{try{let n=e.contentDocument||e.contentWindow?.document;n&&(n.querySelectorAll(`video`).forEach(e=>{e.pause(),e.removeAttribute(`src`),e.load(),t++}),n.querySelectorAll(`audio`).forEach(e=>{e.pause(),e.removeAttribute(`src`),e.load()}))}catch{}e.src=`about:blank`,r++}),e.querySelectorAll(`.pdf-widget`).forEach(e=>{e._pdfDestroy&&e._pdfDestroy()}),(t>0||r>0)&&c.info(`Released ${t} video(s)${n?` (${n} HLS)`:``}${r?`, ${r} iframe(s)`:``}`)}evictLRU(){let e=null,t=1/0;for(let[n,r]of this.layouts)r.status===`warm`&&r.lastAccess<t&&(e=n,t=r.lastAccess);e!==null&&this.evict(e)}clearWarm(){let e=0,t=[];for(let[e,n]of this.layouts)n.status===`warm`&&t.push(e);for(let n of t)this.evict(n),e++;return e>0&&c.info(`Cleared ${e} warm layout(s) from pool`),e}clearWarmNotIn(e){let t=0,n=[];for(let[t,r]of this.layouts)r.status===`warm`&&!e.has(t)&&n.push(t);for(let e of n)this.evict(e),t++;return t>0&&c.info(`Cleared ${t} warm layout(s) no longer in schedule`),t}getLatest(){let e;for(let t of this.layouts.keys())e=t;return e}clear(){let e=Array.from(this.layouts.keys());for(let t of e)this.evict(t);this.hotLayoutId=null}get size(){return this.layouts.size}},u={fadeIn(e,t){let n=[{opacity:0},{opacity:1}],r={duration:t,easing:`linear`,fill:`forwards`};return e.animate(n,r)},fadeOut(e,t){let n=[{opacity:1},{opacity:0}],r={duration:t,easing:`linear`,fill:`forwards`};return e.animate(n,r)},getFlyKeyframes(e,t,n,r){let i={N:{x:0,y:r?-n:n},NE:{x:r?t:-t,y:r?-n:n},E:{x:r?t:-t,y:0},SE:{x:r?t:-t,y:r?n:-n},S:{x:0,y:r?n:-n},SW:{x:r?-t:t,y:r?n:-n},W:{x:r?-t:t,y:0},NW:{x:r?-t:t,y:r?-n:n}},a=i[e]||i.N;return r?{from:{transform:`translate(${a.x}px, ${a.y}px)`,opacity:0},to:{transform:`translate(0, 0)`,opacity:1}}:{from:{transform:`translate(0, 0)`,opacity:1},to:{transform:`translate(${a.x}px, ${a.y}px)`,opacity:0}}},flyIn(e,t,n,r,i){let a=this.getFlyKeyframes(n,r,i,!0),o={duration:t,easing:`ease-out`,fill:`forwards`};return e.animate([a.from,a.to],o)},flyOut(e,t,n,r,i){let a=this.getFlyKeyframes(n,r,i,!1),o={duration:t,easing:`ease-in`,fill:`forwards`};return e.animate([a.from,a.to],o)},slideIn(e,t,n,r,i){let a={N:{x:0,y:-i},NE:{x:r,y:-i},E:{x:r,y:0},SE:{x:r,y:i},S:{x:0,y:i},SW:{x:-r,y:i},W:{x:-r,y:0},NW:{x:-r,y:-i}},o=a[n]||a.E;return e.animate([{transform:`translate(${o.x}px, ${o.y}px)`},{transform:`translate(0, 0)`}],{duration:t,easing:`ease-out`,fill:`forwards`})},slideOut(e,t,n,r,i){let a={N:{x:0,y:-i},NE:{x:r,y:-i},E:{x:r,y:0},SE:{x:r,y:i},S:{x:0,y:i},SW:{x:-r,y:i},W:{x:-r,y:0},NW:{x:-r,y:-i}},o=a[n]||a.W;return e.animate([{transform:`translate(0, 0)`},{transform:`translate(${o.x}px, ${o.y}px)`}],{duration:t,easing:`ease-in`,fill:`forwards`})},wipeIn(e,t,n){let r={E:{from:`inset(0 100% 0 0)`,to:`inset(0 0 0 0)`},W:{from:`inset(0 0 0 100%)`,to:`inset(0 0 0 0)`},S:{from:`inset(0 0 100% 0)`,to:`inset(0 0 0 0)`},N:{from:`inset(100% 0 0 0)`,to:`inset(0 0 0 0)`},SE:{from:`inset(0 100% 100% 0)`,to:`inset(0 0 0 0)`},SW:{from:`inset(0 0 100% 100%)`,to:`inset(0 0 0 0)`},NE:{from:`inset(100% 100% 0 0)`,to:`inset(0 0 0 0)`},NW:{from:`inset(100% 0 0 100%)`,to:`inset(0 0 0 0)`}},i=r[n]||r.E;return e.animate([{clipPath:i.from},{clipPath:i.to}],{duration:t,easing:`ease-out`,fill:`forwards`})},apply(e,t,n,r,i){if(!t||!t.type)return null;let a=t.type.toLowerCase(),o=t.duration||1e3,s=t.direction||`N`;switch(a){case`fade`:return n?this.fadeIn(e,o):this.fadeOut(e,o);case`fadein`:return n?this.fadeIn(e,o):null;case`fadeout`:return n?null:this.fadeOut(e,o);case`fly`:return n?this.flyIn(e,o,s,r,i):this.flyOut(e,o,s,r,i);case`flyin`:return n?this.flyIn(e,o,s,r,i):null;case`flyout`:return n?null:this.flyOut(e,o,s,r,i);case`slide`:return n?this.slideIn(e,o,s,r,i):this.slideOut(e,o,s,r,i);case`wipe`:return n?this.wipeIn(e,o,s):null;default:return null}}},d=class{constructor(e,n,r={}){this.config=e,this.container=n,this.options=r,this.log=i(`RendererLite`,r.logLevel),this.emitter=new t,this.currentLayout=null,this.currentLayoutId=null,this._preloadingLayoutId=null,this._preloadingPromise=null,this.regions=new Map,this.layoutTimer=null,this.layoutEndEmitted=!1,this._deferredTimerLayoutId=null,this._deferredTimerFallback=null,this._paused=!1,this._layoutTimerStartedAt=null,this._layoutTimerDurationMs=null,this.layoutBlobUrls=new Map,this.audioOverlays=new Map,this._stopWidgetBound=(e,t)=>this.stopWidget(e,t),this._renderWidgetBound=(e,t)=>this.renderWidget(e,t),this.scaleFactor=1,this.offsetX=0,this.offsetY=0,this.overlayContainer=null,this.activeOverlays=new Map,this._keydownHandler=null,this._keyboardActions=[],this._subPlaylistCycleIndex=new Map,this._startedWidgets=new Set,this.layoutPool=new l(2),this.preloadTimer=null,this._preloadRetryTimer=null,this.layoutTransition=this._normalizeLayoutTransition(r.layoutTransition),this.setupContainer(),this.emitter.on(`interactiveTrigger`,e=>this._handleInteractiveTrigger(e)),this.emitter.on(`widgetExpire`,e=>this._handleWidgetExpire(e)),this.emitter.on(`widgetExtendDuration`,e=>this._handleWidgetExtendDuration(e)),this.emitter.on(`widgetSetDuration`,e=>this._handleWidgetSetDuration(e)),this.log.info(`Initialized`)}setupContainer(){if(this.container.style.position=`relative`,this.container.style.width=`100%`,this.container.style.height=`100vh`,this.container.style.overflow=`hidden`,this._resizeSuppressed=!1,typeof ResizeObserver<`u`){let e=null;this.resizeObserver=new ResizeObserver(()=>{this._resizeSuppressed||(e&&clearTimeout(e),e=setTimeout(()=>this.rescaleRegions(),150))}),this.resizeObserver.observe(this.container)}this.overlayContainer=document.createElement(`div`),this.overlayContainer.id=`overlay-container`,this.overlayContainer.style.position=`absolute`,this.overlayContainer.style.top=`0`,this.overlayContainer.style.left=`0`,this.overlayContainer.style.width=`100%`,this.overlayContainer.style.height=`100%`,this.overlayContainer.style.zIndex=`1000`,this.overlayContainer.style.pointerEvents=`none`,this.container.appendChild(this.overlayContainer)}calculateScale(e){let t=this.container.clientWidth,n=this.container.clientHeight;if(!t||!n)return;let r=t/e.width,i=n/e.height;this.scaleFactor=Math.min(r,i),this.offsetX=(t-e.width*this.scaleFactor)/2,this.offsetY=(n-e.height*this.scaleFactor)/2,this.log.info(`Scale: ${this.scaleFactor.toFixed(3)} (${e.width}x${e.height} → ${t}x${n}, offset ${Math.round(this.offsetX)},${Math.round(this.offsetY)})`)}applyRegionScale(e,t){let n=this.scaleFactor;e.style.left=`${t.left*n+this.offsetX}px`,e.style.top=`${t.top*n+this.offsetY}px`,e.style.width=`${t.width*n}px`,e.style.height=`${t.height*n}px`}rescaleRegions(){if(this.currentLayout){this.calculateScale(this.currentLayout);for(let[e,t]of this.regions)this.applyRegionScale(t.element,t.config),t.width=t.config.width*this.scaleFactor,t.height=t.config.height*this.scaleFactor;for(let[e,t]of this.activeOverlays){this.calculateScale(t.layout);for(let[e,n]of t.regions)this.applyRegionScale(n.element,n.config),n.width=n.config.width*this.scaleFactor,n.height=n.config.height*this.scaleFactor}}}on(e,t){this.emitter.on(e,t)}emit(e,...t){this.emitter.emit(e,...t)}parseActions(e){let t=[];for(let n of e.children)n.tagName===`action`&&t.push({id:n.getAttribute(`id`)||``,actionType:n.getAttribute(`actionType`)||``,triggerType:n.getAttribute(`triggerType`)||``,triggerCode:n.getAttribute(`triggerCode`)||``,source:n.getAttribute(`source`)||``,sourceId:n.getAttribute(`sourceId`)||``,target:n.getAttribute(`target`)||``,targetId:n.getAttribute(`targetId`)||``,widgetId:n.getAttribute(`widgetId`)||``,layoutCode:n.getAttribute(`layoutCode`)||``,commandCode:n.getAttribute(`commandCode`)||``});return t}_normalizeLayoutTransition(e){return{type:e?.type||`instant`,duration:Number.isFinite(e?.duration)?e.duration:500,direction:e?.direction||void 0}}_resolveLayoutTransition(e){let t=e?.layoutTransitionIn;return!t||!t.type?this.layoutTransition:{type:t.type,duration:Number.isFinite(t.duration)?t.duration:this.layoutTransition.duration,direction:t.direction||this.layoutTransition.direction}}parseXlf(e){let t=new DOMParser().parseFromString(e,`text/xml`).querySelector(`layout`);if(!t)throw Error(`Invalid XLF: no <layout> element`);let n=t.getAttribute(`duration`),r=t.getAttribute(`layoutTransitionIn`),i=t.getAttribute(`layoutTransitionInDuration`),a=t.getAttribute(`layoutTransitionInDirection`),s={schemaVersion:parseInt(t.getAttribute(`schemaVersion`)||`1`),width:parseInt(t.getAttribute(`width`)||`1920`),height:parseInt(t.getAttribute(`height`)||`1080`),duration:n?parseInt(n):0,bgcolor:t.getAttribute(`backgroundColor`)||t.getAttribute(`bgcolor`)||`#000000`,background:t.getAttribute(`background`)||null,enableStat:t.getAttribute(`enableStat`)!==`0`,actions:this.parseActions(t),layoutTransitionIn:r?{type:r,duration:i?parseInt(i):void 0,direction:a||void 0}:null,regions:[]};s.schemaVersion>1&&this.log.debug(`XLF schema version: ${s.schemaVersion}`),n?this.log.info(`Layout duration from XLF: ${s.duration}s`):this.log.info(`Layout duration NOT in XLF, will calculate from widgets`);let c=t.querySelectorAll(`:scope > region, :scope > drawer`);for(let e of c){let t=e.tagName===`drawer`,n=e.getAttribute(`type`)||null,r={id:e.getAttribute(`id`),width:parseInt(e.getAttribute(`width`)||`0`),height:parseInt(e.getAttribute(`height`)||`0`),top:parseInt(e.getAttribute(`top`)||`0`),left:parseInt(e.getAttribute(`left`)||`0`),zindex:parseInt(e.getAttribute(`zindex`)||(t?`2000`:`0`)),enableStat:e.getAttribute(`enableStat`)!==`0`,actions:this.parseActions(e),exitTransition:null,transitionType:null,transitionDuration:null,transitionDirection:null,loop:!0,isDrawer:t,isCanvas:n===`canvas`,widgets:[]},i=Array.from(e.children).find(e=>e.tagName===`options`);if(i){let e=i.querySelector(`exitTransType`);if(e&&e.textContent){let t=i.querySelector(`exitTransDuration`),n=i.querySelector(`exitTransDirection`);r.exitTransition={type:e.textContent,duration:parseInt(t&&t.textContent||`1000`),direction:n&&n.textContent||`N`}}let t=i.querySelector(`loop`);t&&(r.loop=t.textContent!==`0`);let n=i.querySelector(`transitionType`);if(n&&n.textContent){r.transitionType=n.textContent;let e=i.querySelector(`transitionDuration`),t=i.querySelector(`transitionDirection`);r.transitionDuration=parseInt(e&&e.textContent||`1000`),r.transitionDirection=t&&t.textContent||`N`}}for(let t of e.children){if(t.tagName!==`media`)continue;let e=this.parseWidget(t);r.widgets.push(e)}!r.isCanvas&&r.widgets.some(e=>e.type===`global`)&&(r.isCanvas=!0),s.regions.push(r),t&&this.log.info(`Parsed drawer: id=${r.id} with ${r.widgets.length} widgets`),r.isCanvas&&this.log.info(`Parsed canvas region: id=${r.id} with ${r.widgets.length} widgets (all render simultaneously)`)}if(s.duration===0){let{duration:t,isDynamic:n}=o(e);s.duration=t,s.isDynamic=n,this.log.info(`Calculated layout duration: ${s.duration}s (not specified in XLF)${n?` [dynamic — has useDuration=0 video]`:``}`)}return s}parseWidget(e){let t=e.getAttribute(`type`),n=parseInt(e.getAttribute(`duration`)||`10`),r=parseInt(e.getAttribute(`useDuration`)||`1`),i=e.getAttribute(`id`),a=e.getAttribute(`fileId`),o={},s=e.querySelector(`options`);if(s)for(let e of s.children)o[e.tagName]=e.textContent;let c=e.querySelector(`raw`),l=c?c.textContent:``,u={in:null,out:null};o.transIn&&(u.in={type:o.transIn,duration:parseInt(o.transInDuration||`1000`),direction:o.transInDirection||`N`}),o.transOut&&(u.out={type:o.transOut,duration:parseInt(o.transOutDuration||`1000`),direction:o.transOutDirection||`N`});let d=this.parseActions(e),f=[];for(let t of e.children)if(t.tagName.toLowerCase()===`audio`){let e=t.querySelector(`uri`);e?f.push({mediaId:e.getAttribute(`mediaId`)||null,uri:e.textContent||``,volume:parseInt(e.getAttribute(`volume`)||`100`),loop:e.getAttribute(`loop`)===`1`}):f.push({mediaId:t.getAttribute(`mediaId`)||null,uri:t.getAttribute(`uri`)||``,volume:parseInt(t.getAttribute(`volume`)||`100`),loop:t.getAttribute(`loop`)===`1`})}let p=[],m=Array.from(e.children).find(e=>e.tagName===`commands`);if(m)for(let e of m.children)e.tagName===`command`&&p.push({commandCode:e.getAttribute(`commandCode`)||``,commandString:e.getAttribute(`commandString`)||``});let h=e.getAttribute(`parentWidgetId`)||null,g=parseInt(e.getAttribute(`displayOrder`)||`0`),_=e.getAttribute(`cyclePlayback`)===`1`,v=parseInt(e.getAttribute(`playCount`)||`0`),y=e.getAttribute(`isRandom`)===`1`,b=e.getAttribute(`fromDt`)||e.getAttribute(`fromdt`)||null,x=e.getAttribute(`toDt`)||e.getAttribute(`todt`)||null;return{type:t,duration:n,useDuration:r,id:i,fileId:a,render:e.getAttribute(`render`)||null,fromDt:b,toDt:x,enableStat:e.getAttribute(`enableStat`)!==`0`,webhookUrl:o.webhookUrl||null,options:o,raw:l,transitions:u,actions:d,audioNodes:f,commands:p,parentWidgetId:h,displayOrder:g,cyclePlayback:_,playCount:v,isRandom:y}}trackBlobUrl(e){let t=this._preloadingLayoutId||this.currentLayoutId||0;t||this.log.warn(`trackBlobUrl called without currentLayoutId, tracking under key 0`),this.layoutBlobUrls.has(t)||this.layoutBlobUrls.set(t,new Set),this.layoutBlobUrls.get(t).add(e)}revokeBlobUrlsForLayout(e){let t=this.layoutBlobUrls.get(e);t&&(t.forEach(e=>{URL.revokeObjectURL(e)}),this.layoutBlobUrls.delete(e),this.log.info(`Revoked ${t.size} blob URLs for layout ${e}`))}updateLayoutDuration(){if(!this.currentLayout)return;let e=0;for(let t of this.currentLayout.regions){if(t.isDrawer)continue;let n=0;for(let e of t.widgets)e.duration>0&&(n+=e.duration);e=Math.max(e,n)}if(e>0&&e!==this.currentLayout.duration){let t=this.currentLayout.duration;this.currentLayout.duration=e,this.currentLayout._durationFromMetadata=!0,this.log.info(`Layout duration updated: ${t}s → ${e}s (based on video metadata)`);let n=!this._hasUnprobedVideos();if(this.emit(`layoutDurationUpdated`,this.currentLayoutId,e,n),this._deferredTimerLayoutId===this.currentLayoutId&&!this.layoutTimer)if(this._hasUnprobedVideos())this.log.info(`Layout duration updated to ${e}s but still has unprobed videos — keeping timer deferred`);else{this._deferredTimerFallback&&=(clearTimeout(this._deferredTimerFallback),null);let t=Date.now()-(this._layoutTimerStartedAt||Date.now()),n=Math.max(1e3,e*1e3-t);this._deferredTimerLayoutId=null,this._layoutTimerDurationMs=n,this.layoutTimer=setTimeout(()=>{this.log.info(`Layout ${this.currentLayoutId} duration expired (${this.currentLayout.duration}s)`),this.currentLayoutId&&(this.layoutEndEmitted=!0,this.emit(`layoutEnd`,this.currentLayoutId))},n),this.log.info(`All video durations resolved — deferred timer started: ${(n/1e3).toFixed(1)}s remaining (waited ${(t/1e3).toFixed(1)}s for metadata)`)}else if(this.layoutTimer){clearTimeout(this.layoutTimer);let e=Date.now()-(this._layoutTimerStartedAt||Date.now()),t=Math.max(1e3,this.currentLayout.duration*1e3-e);this.layoutTimer=setTimeout(()=>{this.log.info(`Layout ${this.currentLayoutId} duration expired (${this.currentLayout.duration}s)`),this.currentLayoutId&&(this.layoutEndEmitted=!0,this.emit(`layoutEnd`,this.currentLayoutId))},t),this.log.info(`Layout timer adjusted to ${(t/1e3).toFixed(1)}s remaining (elapsed ${(e/1e3).toFixed(1)}s of ${this.currentLayout.duration}s)`)}else this.log.info(`Layout duration updated to ${e}s (timer not yet started, will use new value)`);this._scheduleNextLayoutPreload(this.currentLayout)}}attachActionListeners(e){let t=[],n=0;for(let r of e.actions||[])r.triggerType===`touch`?(this.attachTouchAction(this.container,r,null,null),n++):r.triggerType?.startsWith(`keyboard:`)&&t.push(r);for(let r of e.regions){let e=this.regions.get(r.id);if(e){for(let i of r.actions||[])i.triggerType===`touch`?(this.attachTouchAction(e.element,i,r.id,null),n++):i.triggerType.startsWith(`keyboard:`)&&t.push(i);for(let i of r.widgets){if(!i.actions||i.actions.length===0)continue;let a=e.widgetElements.get(i.id);if(a)for(let e of i.actions)e.triggerType===`touch`?(this.attachTouchAction(a,e,r.id,i.id),n++):e.triggerType.startsWith(`keyboard:`)&&t.push(e)}}}this.setupKeyboardListener(t),(n>0||t.length>0)&&this.log.info(`Actions attached: ${n} touch, ${t.length} keyboard`)}attachTouchAction(e,t,n,r){e.style.cursor=`pointer`;let i=e=>{e.stopPropagation();let i=r?`widget ${r}`:`region ${n}`;this.log.info(`Touch action fired on ${i}: ${t.actionType}`),this.emit(`action-trigger`,{actionType:t.actionType,triggerType:`touch`,triggerCode:t.triggerCode,layoutCode:t.layoutCode,targetId:t.targetId,commandCode:t.commandCode,source:{regionId:n,widgetId:r}})};e.addEventListener(`click`,i),e._actionHandlers||=[],e._actionHandlers.push(i)}setupKeyboardListener(e){this.removeKeyboardListener(),this._keyboardActions=e,e.length!==0&&(this._keydownHandler=e=>{let t=e.key;for(let e of this._keyboardActions)if(t===e.triggerType.substring(9)){this.log.info(`Keyboard action (key: ${t}): ${e.actionType}`),this.emit(`action-trigger`,{actionType:e.actionType,triggerType:e.triggerType,triggerCode:e.triggerCode,layoutCode:e.layoutCode,targetId:e.targetId,commandCode:e.commandCode,source:{key:t}});break}},document.addEventListener(`keydown`,this._keydownHandler))}removeKeyboardListener(){this._keydownHandler&&=(document.removeEventListener(`keydown`,this._keydownHandler),null),this._keyboardActions=[]}removeActionListeners(){for(let[,e]of this.regions){this._cleanElementActionHandlers(e.element);for(let[,t]of e.widgetElements)this._cleanElementActionHandlers(t)}this.removeKeyboardListener()}_cleanElementActionHandlers(e){if(e._actionHandlers){for(let t of e._actionHandlers)e.removeEventListener(`click`,t);delete e._actionHandlers,e.style.cursor=``}}_findRegionByWidgetId(e){for(let[t,n]of this.regions){let r=n.widgets.findIndex(t=>t.id===e);if(r!==-1)return{regionId:t,region:n,widget:n.widgets[r],widgetIndex:r,regionMap:this.regions}}for(let t of this.activeOverlays.values())if(t.regions)for(let[n,r]of t.regions){let i=r.widgets.findIndex(t=>t.id===e);if(i!==-1)return{regionId:n,region:r,widget:r.widgets[i],widgetIndex:i,regionMap:t.regions}}return null}_advanceRegion(e,t){let n=t.get(e);if(!n)return;n.currentIndex=(n.currentIndex+1)%n.widgets.length;let r=t===this.regions;this._startRegionCycle(n,e,this._renderWidgetBound,this._stopWidgetBound,r?()=>this.checkLayoutComplete():void 0)}_handleInteractiveTrigger({targetId:e,triggerCode:t}){this.log.info(`XIC interactiveTrigger: target=${e} code=${t}`),this._findRegionByWidgetId(e)?this.navigateToWidget(e):this.log.warn(`XIC interactiveTrigger: widget ${e} not found`)}_handleWidgetExpire({widgetId:e}){let t=this._findRegionByWidgetId(e);if(!t){this.log.warn(`XIC widgetExpire: widget ${e} not found`);return}let{regionId:n,region:r,widgetIndex:i,regionMap:a}=t;this.log.info(`XIC widgetExpire: widget=${e} region=${n}`),r.timer&&=(clearTimeout(r.timer),null),this.stopWidget(n,i),this._advanceRegion(n,a)}_handleWidgetExtendDuration({widgetId:e,duration:t}){let n=this._findRegionByWidgetId(e);if(!n){this.log.warn(`XIC widgetExtendDuration: widget ${e} not found`);return}let{regionId:r,region:i}=n;this.log.info(`XIC widgetExtendDuration: widget=${e} +${t}s`),i.timer&&=(clearTimeout(i.timer),null),i.timer=setTimeout(()=>{this.stopWidget(r,i.currentIndex),this._advanceRegion(r,n.regionMap)},t*1e3)}_handleWidgetSetDuration({widgetId:e,duration:t}){let n=this._findRegionByWidgetId(e);if(!n){this.log.warn(`XIC widgetSetDuration: widget ${e} not found`);return}let{regionId:r,region:i}=n;this.log.info(`XIC widgetSetDuration: widget=${e} ${t}s`),i.timer&&=(clearTimeout(i.timer),null),i.timer=setTimeout(()=>{this.stopWidget(r,i.currentIndex),this._advanceRegion(r,n.regionMap)},t*1e3)}navigateToWidget(e){for(let[t,n]of this.regions){let r=n.widgets.findIndex(t=>t.id===e);if(r!==-1){if(this.log.info(`Navigating to widget ${e} in region ${t} (index ${r})`),n.isDrawer&&n.element.style.display===`none`&&(n.element.style.display=``,this.log.info(`Drawer region ${t} revealed`)),n.timer&&=(clearTimeout(n.timer),null),this.stopWidget(t,n.currentIndex),n.currentIndex=r,this.renderWidget(t,r),n.widgets.length>1){let e=n.widgets[r].duration*1e3;n.timer=setTimeout(()=>{this.stopWidget(t,r);let e=(r+1)%n.widgets.length;n.currentIndex=e,n.isDrawer&&e===0?(n.element.style.display=`none`,this.log.info(`Drawer region ${t} hidden (cycle complete)`)):n.isDrawer?this.navigateToWidget(n.widgets[e].id):this.startRegion(t)},e)}else if(n.isDrawer){let e=n.widgets[r].duration*1e3;n.timer=setTimeout(()=>{this.stopWidget(t,r),n.element.style.display=`none`,this.log.info(`Drawer region ${t} hidden (single widget done)`)},e)}return}}this.log.warn(`Target widget ${e} not found in any region`)}nextWidget(e){let t=e?this.regions.get(e):this.regions.values().next().value;if(!t||t.widgets.length<=1)return;let n=(t.currentIndex+1)%t.widgets.length,r=t.widgets[n];this.log.info(`nextWidget → index ${n} (widget ${r.id})`),this.navigateToWidget(r.id)}previousWidget(e){let t=e?this.regions.get(e):this.regions.values().next().value;if(!t||t.widgets.length<=1)return;let n=(t.currentIndex-1+t.widgets.length)%t.widgets.length,r=t.widgets[n];this.log.info(`previousWidget → index ${n} (widget ${r.id})`),this.navigateToWidget(r.id)}_mediaFileUrl(e){return`${window.location.origin}${r}/media/file/${e}`}_positionWidgetElement(e){Object.assign(e.style,{position:`absolute`,top:`0`,left:`0`,width:`100%`,height:`100%`,visibility:`hidden`,opacity:`0`})}_applyBackgroundImage(e,t){Object.assign(e.style,{backgroundImage:`url(${t})`,backgroundSize:`cover`,backgroundPosition:`center`,backgroundRepeat:`no-repeat`})}_clearRegionTimers(e){for(let[,t]of e)t.timer&&=(clearTimeout(t.timer),null)}async renderLayout(e,t){try{if(this.log.info(`Rendering layout ${t}`),this.currentLayoutId===t){this.log.info(`Replaying layout ${t} - reusing elements (no recreation!)`),this._clearRegionTimers(this.regions),this._stopAllRegionWidgets(this.regions,this._stopWidgetBound);for(let[,e]of this.regions)e.currentIndex=0,e.complete=!1;this.layoutTimer&&=(clearTimeout(this.layoutTimer),null),this.layoutEndEmitted=!1,this._deferredTimerLayoutId=null,this._deferredTimerFallback&&=(clearTimeout(this._deferredTimerFallback),null),this.emit(`layoutStart`,t,this.currentLayout);for(let[e,t]of this.regions)t.isDrawer||this.startRegion(e);this.startLayoutTimerWhenReady(t,this.currentLayout),this.log.info(`Layout ${t} restarted (reused elements)`),this._scheduleNextLayoutPreload(this.currentLayout);return}if(this.layoutPool.has(t)){this.log.info(`Layout ${t} found in preload pool - instant swap!`),await this._swapToPreloadedLayout(t);return}this.log.info(`Switching to new layout ${t}`),this.stopCurrentLayout();let n=this.parseXlf(e);if(this.currentLayout=n,this.currentLayoutId=t,this.calculateScale(n),this.container.style.backgroundColor=n.bgcolor,this.container.style.backgroundImage=``,n.background){let e=this.options.fileIdToSaveAs?.get(String(n.background))||n.background;this._applyBackgroundImage(this.container,this._mediaFileUrl(e)),this.log.info(`Background image set: ${n.background} → ${e}`)}for(let e of n.regions)await this.createRegion(e);this.log.info(`Pre-creating widget elements for instant transitions...`);for(let[e,t]of this.regions)for(let n=0;n<t.widgets.length;n++){let r=t.widgets[n];r.layoutId=this.currentLayoutId,r.regionId=e;try{let e=await this.createWidgetElement(r,t);this._positionWidgetElement(e),t.element.appendChild(e),t.widgetElements.set(r.id,e)}catch(e){this.log.error(`Failed to pre-create widget ${r.id}:`,e)}}if(this.log.info(`All widget elements pre-created`),this.attachActionListeners(n),this.emit(`layoutStart`,t,n),n.duration>0){let e=!this._hasUnprobedVideos();this.emit(`layoutDurationUpdated`,t,n.duration,e)}for(let[e,t]of this.regions)t.isDrawer||this.startRegion(e);this.startLayoutTimerWhenReady(t,n),this._scheduleNextLayoutPreload(n),this.log.info(`Layout ${t} started`)}catch(e){throw this.log.error(`Error rendering layout:`,e),this.emit(`error`,{type:`layoutError`,error:e,layoutId:t}),e}}_createRegionEntry(e,t,n,r={}){let{className:i=`renderer-lite-region`,...a}=r,o=document.createElement(`div`);o.id=t,o.className=i,o.style.position=`absolute`,o.style.zIndex=String(e.zindex),o.style.overflow=`hidden`,this.applyRegionScale(o,e),n.appendChild(o);let s=this.scaleFactor;return{element:o,config:e,widgets:e.widgets,currentIndex:0,timer:null,width:e.width*s,height:e.height*s,complete:!1,widgetElements:new Map,...a}}async createRegion(e){let t=this._createRegionEntry(e,`region_${e.id}`,this.container,{isDrawer:e.isDrawer||!1,isCanvas:e.isCanvas||!1});e.isDrawer&&(t.element.style.display=`none`);let n=e.widgets.filter(e=>this._isWidgetActive(e));n.some(e=>e.cyclePlayback)&&(n=this._applyCyclePlayback(n)),t.widgets=n,this.regions.set(e.id,t)}startRegion(e){let t=this.regions.get(e);this._startRegionCycle(t,e,this._renderWidgetBound,this._stopWidgetBound,()=>{this.log.info(`Region ${e} completed one full cycle`),this.checkLayoutComplete()})}async createWidgetElement(e,t){if(e.render===`html`&&e.type!==`pdf`)return await this.renderGenericWidget(e,t);switch(e.type){case`image`:return await this.renderImage(e,t);case`video`:return await this.renderVideo(e,t);case`audio`:return await this.renderAudio(e,t);case`text`:case`ticker`:return await this.renderTextWidget(e,t);case`pdf`:return await this.renderPdf(e,t);case`webpage`:return await this.renderWebpage(e,t);case`localvideo`:return await this.renderVideo(e,t);case`videoin`:return await this.renderVideoIn(e,t);case`powerpoint`:case`flash`:return this.log.warn(`Widget type '${e.type}' is not supported on web players (widget ${e.id})`),this._renderUnsupportedPlaceholder(e,t);default:return await this.renderGenericWidget(e,t)}}findMediaElement(e,t){return e.tagName===t?e:e.querySelector(t.toLowerCase())}updateMediaElement(e,t){let n=this.findMediaElement(e,`VIDEO`)||this.findMediaElement(e,`AUDIO`);if(n){if(n.tagName===`VIDEO`&&n._mediaConstraints&&!n._mediaStream){navigator.mediaDevices.getUserMedia(n._mediaConstraints).then(e=>{n.srcObject=e,n._mediaStream=e,this.log.info(`Webcam stream re-acquired for widget ${t.id}`)}).catch(e=>{this.log.warn(`Failed to re-acquire webcam stream:`,e.message)});return}this._restartMediaElement(n),this.log.info(`${n.tagName===`VIDEO`?`Video`:`Audio`} restarted: ${t.fileId||t.id}`)}}_restartMediaElement(e){e.currentTime=0;let t=()=>{e.removeEventListener(`seeked`,t),e.play().catch(()=>{})};e.addEventListener(`seeked`,t),e.play().catch(()=>{})}waitForWidgetReady(e,t){let n=1e4,r=this.findMediaElement(e,`VIDEO`);if(r)return!r.paused&&r.readyState>=3?Promise.resolve():new Promise(e=>{let i=setTimeout(()=>{this.log.warn(`Video ready timeout (${n}ms) for widget ${t.id}`),e()},n),a=()=>{r.removeEventListener(`playing`,a),clearTimeout(i),this.log.info(`Video widget ${t.id} ready (playing)`),e()};r.addEventListener(`playing`,a)});let i=this.findMediaElement(e,`AUDIO`);if(i)return!i.paused&&i.readyState>=3?Promise.resolve():new Promise(e=>{let r=setTimeout(()=>{this.log.warn(`Audio ready timeout (${n}ms) for widget ${t.id}`),e()},n),a=()=>{i.removeEventListener(`playing`,a),clearTimeout(r),this.log.info(`Audio widget ${t.id} ready (playing)`),e()};i.addEventListener(`playing`,a)});let a=this.findMediaElement(e,`IMG`);return a?a.complete&&a.naturalWidth>0?Promise.resolve():new Promise(e=>{let r=()=>{a.removeEventListener(`load`,r),clearTimeout(i),e()},i=setTimeout(()=>{a.removeEventListener(`load`,r),this.log.warn(`Image ready timeout for widget ${t.id}`),e()},n);a.addEventListener(`load`,r)}):Promise.resolve()}async startLayoutTimerWhenReady(e,t){if(!t||t.duration<=0)return;let n=[];for(let[e,t]of this.regions){if(t.widgets.length===0)continue;let e=t.widgets[t.currentIndex||0],r=t.widgetElements.get(e.id);r&&n.push(this.waitForWidgetReady(r,e))}if(n.length>0&&(this.log.info(`Waiting for ${n.length} widget(s) to be ready before starting layout timer...`),await Promise.all(n),this.log.info(`All widgets ready — starting layout timer`)),this.currentLayoutId!==e){this.log.warn(`Layout changed while waiting for widgets — skipping timer for ${e}`);return}if(t.isDynamic&&!t._durationFromMetadata&&this._hasUnprobedVideos()){this._deferredTimerLayoutId=e,this._layoutTimerStartedAt=Date.now(),this.log.info(`Layout ${e} has unprobed videos — deferring timer until metadata loads`),this._deferredTimerFallback=setTimeout(()=>{this._deferredTimerFallback=null,this._deferredTimerLayoutId===e&&!this.layoutTimer&&(this.log.warn(`Layout ${e}: metadata timeout after 30s — starting timer with ${t.duration}s estimate`),this._deferredTimerLayoutId=null,this._startLayoutTimer(e,t))},3e4);return}this._startLayoutTimer(e,t)}_hasUnprobedVideos(){for(let[,e]of this.regions)for(let t of e.widgets)if(t.type===`video`&&t.useDuration===0&&t._probed)return!1;for(let[,e]of this.regions)for(let t of e.widgets)if(t.type===`video`&&t.useDuration===0)return!0;return!1}_startLayoutTimer(e,t){this._deferredTimerLayoutId=null,this._deferredTimerFallback&&=(clearTimeout(this._deferredTimerFallback),null);let n=t.duration*1e3;this.log.info(`Layout ${e} will end after ${t.duration}s`),this._layoutTimerStartedAt=Date.now(),this._layoutTimerDurationMs=n,this.layoutTimer=setTimeout(()=>{this.log.info(`Layout ${e} duration expired (${t.duration}s)`),this.currentLayoutId&&(this.layoutEndEmitted=!0,this.emit(`layoutEnd`,this.currentLayoutId))},n)}async _showWidget(e,t){let n=e.widgets[t];if(!n)return null;let r=e.widgetElements.get(n.id);if(r||(this.log.warn(`Widget ${n.id} not pre-created, creating now`),r=await this.createWidgetElement(n,e),r.style.position=`absolute`,r.style.top=`0`,r.style.left=`0`,r.style.width=`100%`,r.style.height=`100%`,e.widgetElements.set(n.id,r),e.element.appendChild(r)),!e.isCanvas)for(let[t,r]of e.widgetElements)t!==n.id&&(r.getAnimations?.().forEach(e=>e.cancel()),r.style.visibility=`hidden`,r.style.opacity=`0`);return this.updateMediaElement(r,n),r.getAnimations?.().forEach(e=>e.cancel()),r.style.visibility=`visible`,n.transitions.in?u.apply(r,n.transitions.in,!0,e.width,e.height):r.style.opacity=`1`,r._pdfResume&&r._pdfResume(),this._startAudioOverlays(n),n}_startAudioOverlays(e){if(!e.audioNodes||e.audioNodes.length===0)return;this._stopAudioOverlays(e.id);let t=[];for(let n of e.audioNodes){if(!n.uri)continue;let r=document.createElement(`audio`);r.autoplay=!0,r.loop=n.loop,r.volume=Math.max(0,Math.min(1,n.volume/100)),r.src=n.uri?this._mediaFileUrl(n.uri):``,r.style.display=`none`,this.container.appendChild(r);let i=r.play();i&&i.catch&&i.catch(()=>{}),t.push(r),this.log.info(`Audio overlay started for widget ${e.id}: ${n.uri} (loop=${n.loop}, vol=${n.volume})`)}t.length>0&&this.audioOverlays.set(e.id,t)}_stopAudioOverlays(e){let t=this.audioOverlays.get(e);if(t){for(let e of t)e.pause(),e.removeAttribute(`src`),e.load(),e.parentNode&&e.parentNode.removeChild(e);this.audioOverlays.delete(e),this.log.info(`Audio overlays stopped for widget ${e}`)}}_hideWidget(e,t){let n=e.widgets[t];if(!n)return{widget:null,animPromise:null};let r=e.widgetElements.get(n.id);if(!r)return{widget:null,animPromise:null};let i=null;if(n.transitions.out){let t=u.apply(r,n.transitions.out,!1,e.width,e.height);t&&(i=new Promise(e=>{t.onfinish=e}))}let a=r.querySelector(`video`);if(a&&(a.pause(),a._mediaStream&&(a._mediaStream.getTracks().forEach(e=>e.stop()),a._mediaStream=null,a.srcObject=null),a._hlsInstance&&=(a._hlsInstance.destroy(),null),a.removeAttribute(`src`),a.load(),a._eventCleanup)){for(let[e,t]of a._eventCleanup)a.removeEventListener(e,t);a._eventCleanup=null}let o=r.querySelector(`audio`);if(o&&n.options.loop!==`1`&&o.pause(),o?._eventCleanup){for(let[e,t]of o._eventCleanup)o.removeEventListener(e,t);o._eventCleanup=null}this._stopAudioOverlays(n.id),r._pdfCleanup&&r._pdfCleanup();let s=r.querySelectorAll(`iframe`);for(let e of s){try{let t=e.contentDocument||e.contentWindow?.document;t&&(t.querySelectorAll(`video`).forEach(e=>{e.pause(),e.removeAttribute(`src`),e.load()}),t.querySelectorAll(`audio`).forEach(e=>{e.pause(),e.removeAttribute(`src`),e.load()}))}catch{}e.src=`about:blank`}return{widget:n,animPromise:i}}_isWidgetActive(e){let t=new Date;return!(e.fromDt&&t<new Date(e.fromDt)||e.toDt&&t>new Date(e.toDt))}_parseDurationComments(e,t){let n=t.duration,r=e.match(/<!--\s*DURATION=(\d+)\s*-->/);if(r){let e=parseInt(r[1],10);if(e>0){this.log.info(`Widget ${t.id}: DURATION comment overrides duration ${t.duration}→${e}s`),t.duration=e,t.duration!==n&&this.updateLayoutDuration();return}}let i=e.match(/<!--\s*NUMITEMS=(\d+)\s*-->/);if(i){let e=parseInt(i[1],10);if(e>0&&t.duration>0){let n=e*t.duration;this.log.info(`Widget ${t.id}: NUMITEMS=${e} × ${t.duration}s = ${n}s`),t.duration=n}}t.duration!==n&&this.updateLayoutDuration()}_applyCyclePlayback(e){this._subPlaylistCycleIndex||=new Map;let t=new Map,n=[];for(let r of e)r.parentWidgetId&&r.cyclePlayback?(t.has(r.parentWidgetId)||t.set(r.parentWidgetId,[]),t.get(r.parentWidgetId).push(r)):n.push({type:`direct`,widget:r});for(let[e,r]of t){r.sort((e,t)=>e.displayOrder-t.displayOrder);let t;if(r.some(e=>e.isRandom))t=r[Math.floor(Math.random()*r.length)];else{let n=this._subPlaylistCycleIndex.get(e)||{widgetIdx:0,playsDone:0};t=r[n.widgetIdx%r.length];let i=t.playCount||1;n.playsDone++,n.playsDone>=i&&(n.widgetIdx++,n.playsDone=0),this._subPlaylistCycleIndex.set(e,n)}this.log.info(`Sub-playlist cycle: group ${e} selected widget ${t.id} (${r.length} in group)`),n.push({type:`direct`,widget:t})}return n.map(e=>e.widget)}_startRegionCycle(e,t,n,r,i){if(!e||e.widgets.length===0)return;if(e.isCanvas){this._startCanvasRegion(e,t,n,i);return}if(e.widgets.length===1){n(t,0);return}let a=()=>{let o=e.currentIndex,s=e.widgets[o];n(t,o);let c=s.duration*1e3;this.log.info(`Region ${t} widget ${s.id} (${s.type}) playing for ${s.duration}s (useDuration=${s.useDuration}, index ${o}/${e.widgets.length})`),e.timer=setTimeout(()=>{this._handleWidgetCycleEnd(s,e,t,o,n,r,i,a)},c)};a()}_startCanvasRegion(e,t,n,r){for(let r=0;r<e.widgets.length;r++)n(t,r);let i=Math.max(...e.widgets.map(e=>e.duration))*1e3;i>0?e.timer=setTimeout(()=>{e.complete||(e.complete=!0,r?.())},i):(e.complete=!0,r?.())}_handleWidgetCycleEnd(e,t,n,r,i,a,o,s){e.webhookUrl&&this.emit(`widgetAction`,{type:`durationEnd`,widgetId:e.id,layoutId:this.currentLayoutId,regionId:n,url:e.webhookUrl}),a(n,r);let c=(t.currentIndex+1)%t.widgets.length;if(c===0&&!t.complete&&(t.complete=!0,o?.()),c===0&&t.config?.loop===!1&&t.widgets.length===1){i(n,0);return}this.layoutEndEmitted||(t.currentIndex=c,s())}async renderWidget(e,t){let n=this.regions.get(e);if(n)try{let r=await this._showWidget(n,t);if(r&&(this.log.info(`Showing widget ${r.type} (${r.id}) in region ${e}`),this._startedWidgets.add(`${e}:${t}`),this.emit(`widgetStart`,{widgetId:r.id,regionId:e,layoutId:this.currentLayoutId,mediaId:parseInt(r.fileId||r.id)||null,type:r.type,duration:r.duration,enableStat:r.enableStat}),r.commands&&r.commands.length>0))for(let t of r.commands)this.emit(`widgetCommand`,{commandCode:t.commandCode,commandString:t.commandString,widgetId:r.id,regionId:e,layoutId:this.currentLayoutId})}catch(r){this.log.error(`Error rendering widget:`,r),this.emit(`error`,{type:`widgetError`,error:r,widgetId:n.widgets[t]?.id,regionId:e})}}async stopWidget(e,t){let n=`${e}:${t}`;if(!this._startedWidgets.delete(n))return;let r=this.regions.get(e);if(!r)return;let{widget:i,animPromise:a}=this._hideWidget(r,t);i&&this.emit(`widgetEnd`,{widgetId:i.id,regionId:e,layoutId:this.currentLayoutId,mediaId:parseInt(i.fileId||i.id)||null,type:i.type,enableStat:i.enableStat}),a&&await a}_stopAllRegionWidgets(e,t){for(let[n,r]of e)if(r.isCanvas)for(let e=0;e<r.widgets.length;e++)t(n,e);else r.widgets.length>0&&t(n,r.currentIndex)}async renderImage(e,t){let n=document.createElement(`img`);n.className=`renderer-lite-widget`,n.style.width=`100%`,n.style.height=`100%`;let r=e.options.scaleType,i={stretch:`fill`,center:`contain`,fit:`cover`};n.style.objectFit=i[r]||`contain`;let a={left:`left`,center:`center`,right:`right`},o={top:`top`,middle:`center`,bottom:`bottom`},s=a[e.options.alignId]||`center`,c=o[e.options.valignId]||`center`;return n.style.objectPosition=`${s} ${c}`,n.style.opacity=`0`,n.src=e.options.uri?this._mediaFileUrl(e.options.uri):``,n}async renderVideo(t,n){let r=document.createElement(`video`);r.className=`renderer-lite-widget`,r.style.width=`100%`,r.style.height=`100%`;let i=t.options.scaleType,a={stretch:`fill`,center:`none`,fit:`contain`};r.style.objectFit=a[i]||`contain`,r.style.opacity=`1`,r.autoplay=!0,r.preload=`auto`,r.muted=t.options.mute===`1`,r.loop=!1,r.controls=!1,r.playsInline=!0;let o=t.options.uri||``,s=t.fileId||t.id,c=()=>{t.options.loop===`1`?(r.currentTime=0,this.log.info(`Video ${o} ended - reset to start, waiting for widget cycle to replay`)):this.log.info(`Video ${o} ended - paused on last frame`)};r.addEventListener(`ended`,c);let l=o?this._mediaFileUrl(o):``;if(l.includes(`.m3u8`))if(r.canPlayType(`application/vnd.apple.mpegurl`))this.log.info(`HLS stream (native): ${s}`),r.src=l;else try{let{default:t}=await e(async()=>{let{default:e}=await import(`hls.js`);return{default:e}},[],import.meta.url);if(t.isSupported()){let e=new t({enableWorker:!0,lowLatencyMode:!0});e.loadSource(l),e.attachMedia(r),r._hlsInstance=e,e.on(t.Events.ERROR,(t,n)=>{n.fatal&&(this.log.error(`HLS fatal error: ${n.type}`,n.details),e.destroy(),r._hlsInstance=null)}),this.log.info(`HLS stream (hls.js): ${s}`)}else this.log.warn(`HLS not supported on this browser for ${s}`),r.src=l}catch(e){this.log.warn(`hls.js not available, falling back to native: ${e.message}`),r.src=l}else r.src=l;let u=this._preloadingLayoutId||this.currentLayoutId,d=()=>{let e=r.duration;this.log.info(`Video ${o} duration detected: ${e}s`),(t.duration===0||t.useDuration===0)&&(t.duration=e,t._probed=!0,this.log.info(`Updated widget ${t.id} duration to ${e}s (useDuration=0)`),this.currentLayoutId===u?this.updateLayoutDuration():this.log.info(`Video ${o} duration set but layout timer not updated (preloaded for layout ${u}, current is ${this.currentLayoutId})`))};r.addEventListener(`loadedmetadata`,d);let f=()=>{this.log.info(`Video loaded and ready:`,o)};r.addEventListener(`loadeddata`,f);let p=()=>{let e=r.error,n=e?.code,i=e?.message||`Unknown error`;this.log.warn(`Video error: ${o}, code: ${n}, time: ${r.currentTime.toFixed(1)}s, message: ${i}`),t.useDuration===0&&t.duration===0&&(t.duration=60,this.log.info(`Set fallback duration 60s for errored widget ${t.id}`),this.currentLayoutId===u&&this.updateLayoutDuration()),this.emit(`videoError`,{storedAs:o,fileId:s,errorCode:n,errorMessage:i,currentTime:r.currentTime})};r.addEventListener(`error`,p);let m=()=>{this.log.info(`Video playing:`,o)};return r.addEventListener(`playing`,m),r._eventCleanup=[[`ended`,c],[`loadedmetadata`,d],[`loadeddata`,f],[`error`,p],[`playing`,m]],this.log.info(`Video element created:`,o,r.src),r}async renderVideoIn(e,t){let n=document.createElement(`video`);n.className=`renderer-lite-widget`,n.style.width=`100%`,n.style.height=`100%`,n.style.objectFit=e.options.showFullScreen===`1`?`cover`:`contain`,n.autoplay=!0,n.playsInline=!0,n.controls=!1,n.muted=e.options.mute!==`0`,e.options.mirror===`1`&&(n.style.transform=`scaleX(-1)`);let r={width:{ideal:t.width},height:{ideal:t.height}},i=e.options.sourceId||e.options.deviceId;i?r.deviceId={exact:i}:r.facingMode=e.options.facingMode||`environment`;let a={video:r,audio:e.options.captureAudio===`1`};n._mediaConstraints=a;try{let t=await navigator.mediaDevices.getUserMedia(a);n.srcObject=t,n._mediaStream=t,this.log.info(`Webcam stream acquired for widget ${e.id} (tracks: ${t.getTracks().length})`)}catch(n){return this.log.warn(`getUserMedia failed for widget ${e.id}: ${n.message}`),this._renderUnsupportedPlaceholder({...e,type:`Camera unavailable`},t)}return n}async renderAudio(e,t){let n=document.createElement(`div`);n.className=`renderer-lite-widget audio-widget`,n.style.width=`100%`,n.style.height=`100%`,n.style.display=`flex`,n.style.flexDirection=`column`,n.style.alignItems=`center`,n.style.justifyContent=`center`,n.style.background=`linear-gradient(135deg, #667eea 0%, #764ba2 100%)`,n.style.opacity=`0`;let r=document.createElement(`audio`);r.autoplay=!0,r.loop=e.options.loop===`1`,r.volume=parseFloat(e.options.volume||`100`)/100;let i=e.options.uri||``;e.fileId||e.id,r.src=i?this._mediaFileUrl(i):``;let a=()=>{e.options.loop===`1`?(r.currentTime=0,this.log.info(`Audio ${i} ended - reset to start, waiting for widget cycle to replay`)):this.log.info(`Audio ${i} ended - playback complete`)};r.addEventListener(`ended`,a);let o=this._preloadingLayoutId||this.currentLayoutId,s=()=>{let t=Math.floor(r.duration);this.log.info(`Audio ${i} duration detected: ${t}s`),(e.duration===0||e.useDuration===0)&&(e.duration=t,this.log.info(`Updated widget ${e.id} duration to ${t}s (useDuration=0)`),this.currentLayoutId===o?this.updateLayoutDuration():this.log.info(`Audio ${i} duration set but layout timer not updated (preloaded for layout ${o}, current is ${this.currentLayoutId})`))};r.addEventListener(`loadedmetadata`,s);let c=()=>{let e=r.error;this.log.warn(`Audio error (non-fatal): ${i}, code: ${e?.code}, message: ${e?.message||`Unknown`}`)};r.addEventListener(`error`,c),r._eventCleanup=[[`ended`,a],[`loadedmetadata`,s],[`error`,c]];let l=document.createElement(`div`);l.innerHTML=`♪`,l.style.fontSize=`120px`,l.style.color=`white`,l.style.marginBottom=`20px`;let u=document.createElement(`div`);u.style.color=`white`,u.style.fontSize=`24px`,u.textContent=`Playing Audio`;let d=document.createElement(`div`);return d.style.color=`rgba(255,255,255,0.7)`,d.style.fontSize=`16px`,d.style.marginTop=`10px`,d.textContent=e.options.uri,n.appendChild(r),n.appendChild(l),n.appendChild(u),n.appendChild(d),n}async renderTextWidget(e,t){return await this._renderIframeWidget(e,t)}async renderPdf(t,r){let i=document.createElement(`div`);if(i.className=`renderer-lite-widget pdf-widget`,i.style.width=`100%`,i.style.height=`100%`,i.style.backgroundColor=`transparent`,i.style.opacity=`0`,i.style.position=`relative`,window.pdfjsLib===void 0)try{let t=await e(()=>import(`./pdf-Bxz9Nzto.js`),__vite__mapDeps([0,1]),import.meta.url);window.pdfjsLib=t;let n=window.location.pathname.replace(/\/[^/]*$/,`/`);window.pdfjsLib.GlobalWorkerOptions.workerSrc=`${window.location.origin}${n}pdf.worker.min.mjs`}catch(e){return this.log.error(`PDF.js not available:`,e),i.innerHTML=`<div style="color:white;padding:20px;text-align:center;">PDF viewer unavailable</div>`,i.style.opacity=`1`,i}let a=t.options.uri?this._mediaFileUrl(t.options.uri):``;try{let e=await window.pdfjsLib.getDocument(a).promise,o=e.numPages,s=t.duration||60,c=s*1e3/o;this.log.info(`[pdf] PDF loaded: ${o} pages, ${s}s duration, ${(c/1e3).toFixed(1)}s/page`);let l=await e.getPage(1),u=l.getViewport({scale:1}),d=Math.min(r.width/u.width,r.height/u.height);l.cleanup();let f=document.createElement(`canvas`);f.className=`pdf-page`,f.width=Math.floor(u.width*d),f.height=Math.floor(u.height*d),f.style.cssText=`display:block;margin:auto;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);`;let p=f.getContext(`2d`);i.appendChild(f);let m=document.createElement(`div`);m.style.cssText=`position:absolute;bottom:10px;right:10px;background:rgba(0,0,0,0.7);color:white;padding:8px 12px;border-radius:4px;font:14px system-ui;z-index:1;`,n()||(m.style.display=`none`),i.appendChild(m);let h=1,g=null,_=null,v=!1,y=async()=>{if(v)return;m.textContent=`Page ${h} / ${o}`;let t=await e.getPage(h),n=t.getViewport({scale:d});p.clearRect(0,0,f.width,f.height),_=t.render({canvasContext:p,viewport:n});try{await _.promise}catch(e){if(v)return;throw e}_=null,t.cleanup(),o>1&&!v&&(g=setTimeout(()=>{h=h>=o?1:h+1,y()},c))};await y();let b=null;i._pdfCleanup=()=>{if(v=!0,g&&clearTimeout(g),g=null,_){let e=_;_=null,e.cancel(),b=e.promise.catch(()=>{})}},i._pdfResume=async()=>{i._pdfCleanup(),b&&=(await b,null),v=!1,h=1,y()},i._pdfDestroy=()=>{i._pdfCleanup(),f.width=0,f.height=0,e.destroy()}}catch(e){this.log.error(`PDF render failed:`,e),i.innerHTML=`<div style="color:white;padding:20px;text-align:center;">Failed to load PDF</div>`}return i.style.opacity=`1`,i}async renderWebpage(e,t){if(parseInt(e.options.modeId||`1`)===0)return await this.renderGenericWidget(e,t);let n=document.createElement(`iframe`);return n.className=`renderer-lite-widget`,n.style.width=`100%`,n.style.height=`100%`,n.style.border=`none`,n.style.opacity=`0`,n.src=decodeURIComponent(e.options.uri||``),n}async renderGenericWidget(e,t){return await this._renderIframeWidget(e,t)}async _renderIframeWidget(e,t){let n=document.createElement(`iframe`);n.className=`renderer-lite-widget`,n.style.width=`100%`,n.style.height=`100%`,n.style.border=`none`,n.style.opacity=`0`;let r=e.raw;if(this.options.getWidgetHtml){let t=await this.options.getWidgetHtml(e);if(t&&typeof t==`object`&&t.url)return n.src=t.url,t.fallback&&this._parseDurationComments(t.fallback,e),n;r=t}if(r){this._parseDurationComments(r,e);let t=new Blob([r],{type:`text/html`}),i=URL.createObjectURL(t);n.src=i,this.trackBlobUrl(i)}else this.log.warn(`No HTML for widget ${e.id}`),n.srcdoc=`<div style="padding:20px;">Widget content unavailable</div>`;return n}_renderUnsupportedPlaceholder(e,t){let n=document.createElement(`div`);return n.className=`renderer-lite-widget`,n.style.width=`100%`,n.style.height=`100%`,n.style.display=`flex`,n.style.alignItems=`center`,n.style.justifyContent=`center`,n.style.backgroundColor=`#111`,n.style.color=`#666`,n.style.fontSize=`14px`,n.textContent=`Unsupported: ${e.type}`,n}_scheduleNextLayoutPreload(e){this.preloadTimer&&=(clearTimeout(this.preloadTimer),null),this._preloadRetryTimer&&=(clearTimeout(this._preloadRetryTimer),null);let t=e.duration||60,n=t*1e3*.75,r=t*1e3*.9;this.log.info(`Scheduling next layout preload in ${(n/1e3).toFixed(1)}s (75% of ${t}s)`),this.preloadTimer=setTimeout(()=>{this.preloadTimer=null,this.emit(`request-next-layout-preload`)},n),this._preloadRetryTimer=setTimeout(()=>{this._preloadRetryTimer=null,this.emit(`request-next-layout-preload`)},r)}hasPreloadedLayout(e){return this.layoutPool.has(e)}async preloadLayout(e,t){return this.layoutPool.has(t)?(this.log.info(`Layout ${t} already in preload pool, skipping`),!0):this.currentLayoutId===t?(this.log.info(`Layout ${t} is current, skipping preload`),!0):this._preloadingLayoutId===t&&this._preloadingPromise?(this.log.info(`Layout ${t} preload in-flight, waiting for it...`),this._preloadingPromise):(this._preloadingPromise=this._doPreloadLayout(e,t),this._preloadingPromise)}async _doPreloadLayout(e,t){try{this.log.info(`Preloading layout ${t} into pool...`);let n=this.parseXlf(e);this.calculateScale(n);let r=document.createElement(`div`);if(r.id=`preload_layout_${t}`,r.className=`renderer-lite-preload-wrapper`,r.style.position=`absolute`,r.style.top=`0`,r.style.left=`0`,r.style.width=`100%`,r.style.height=`100%`,r.style.visibility=`hidden`,r.style.zIndex=`-1`,r.style.backgroundColor=n.bgcolor,n.background){let e=this.options.fileIdToSaveAs?.get(String(n.background))||n.background;this._applyBackgroundImage(r,this._mediaFileUrl(e))}let i=this.currentLayoutId,a=new Map;for(let e of n.regions){let n=this._createRegionEntry(e,`preload_region_${t}_${e.id}`,r);a.set(e.id,n)}let o=new Set,s=this.layoutBlobUrls;this.layoutBlobUrls=new Map,this.layoutBlobUrls.set(t,o),this._preloadingLayoutId=t;for(let[e,n]of a)for(let r=0;r<n.widgets.length;r++){let i=n.widgets[r];i.layoutId=t,i.regionId=e;try{let e=await this.createWidgetElement(i,n);this._positionWidgetElement(e),n.element.appendChild(e),n.widgetElements.set(i.id,e)}catch(e){this.log.error(`Preload: Failed to create widget ${i.id}:`,e)}}return this.currentLayoutId=i,r.querySelectorAll(`video`).forEach(e=>e.pause()),(this.layoutBlobUrls.get(t)||new Set).forEach(e=>o.add(e)),this.layoutBlobUrls=s,this.container.appendChild(r),this.layoutPool.add(t,{container:r,layout:n,regions:a,blobUrls:o}),this.log.info(`Layout ${t} preloaded into pool (${a.size} regions)`),!0}catch(e){return this.log.error(`Preload failed for layout ${t}:`,e),!1}finally{this._preloadingLayoutId===t&&(this._preloadingLayoutId=null,this._preloadingPromise=null)}}async _swapToPreloadedLayout(e){let t=this.layoutPool.get(e);if(!t){this.log.error(`Cannot swap: layout ${e} not in pool`);return}let n=this._resolveLayoutTransition(t.layout);return n.type===`instant`?this._swapToPreloadedLayoutInstant(e,t):this._swapToPreloadedLayoutWithTransition(e,t,n)}async _swapToPreloadedLayoutInstant(e,t){this.removeActionListeners(),this._clearLayoutTimers();let n=this.currentLayoutId,r=this.layoutEndEmitted;if(this.layoutEndEmitted=!1,n&&this.layoutPool.has(n))this._clearRegionTimers(this.regions),this._stopAllRegionWidgets(this.regions,this._stopWidgetBound),this.layoutPool.evict(n);else{this._clearRegionTimers(this.regions),this._stopAllRegionWidgets(this.regions,this._stopWidgetBound);for(let[,e]of this.regions)if(l.releaseMediaElements(e.element),e.config?.exitTransition){let t=u.apply(e.element,e.config.exitTransition,!1,e.width,e.height);if(t){let n=e.element;t.onfinish=()=>n.remove()}else e.element.remove()}else e.element.remove();n&&this.revokeBlobUrlsForLayout(n)}this.currentLayout=null,this.currentLayoutId=null,this.regions.clear(),this._activatePreloadedLayout(e,t,n,r),this.log.info(`Swapped to preloaded layout ${e} (instant transition)`),this._logResourceStats(e)}async _swapToPreloadedLayoutWithTransition(e,t,n){this.removeActionListeners(),this._clearLayoutTimers();let r=this.currentLayoutId,i=this.layoutEndEmitted;this.layoutEndEmitted=!1;let a=this.regions,o=r!==null&&this.layoutPool.has(r),s=o?this.layoutPool.get(r).container:null;this._clearRegionTimers(a),this._stopAllRegionWidgets(a,this._stopWidgetBound),this.currentLayout=null,this.currentLayoutId=null,this.regions=new Map,t.container.style.visibility=`visible`,t.container.style.zIndex=`1`,n.type===`fade`&&(t.container.style.opacity=`0`),this._activatePreloadedLayout(e,t,r,i);let c=t.layout.width,l=t.layout.height,d=u.apply(t.container,n,!0,c,l),f=null;s&&(n.type===`fade`||n.type===`slide`)&&(f=u.apply(s,n,!1,c,l));let p=()=>{if(t.container.style.zIndex=`0`,t.container.style.opacity=``,this._teardownOldLayoutAfterTransition(r,a,o),f)try{f.cancel()}catch{}this.log.info(`Swapped to preloaded layout ${e} (${n.type} transition, ${n.duration}ms)`),this._logResourceStats(e)};d?(d.onfinish=p,setTimeout(p,Math.ceil(n.duration*1.5))):p()}_activatePreloadedLayout(e,t,n,r){if(t.container.style.visibility=`visible`,t.container.style.zIndex!==`1`&&(t.container.style.zIndex=`0`),this.layoutPool.setHot(e),this.currentLayout=t.layout,this.currentLayoutId=e,this.regions=t.regions,n&&!r&&this.emit(`layoutEnd`,n),this.container.style.backgroundColor=t.layout.bgcolor,t.container.style.backgroundImage)for(let e of[`backgroundImage`,`backgroundSize`,`backgroundPosition`,`backgroundRepeat`])this.container.style[e]=t.container.style[e];else this.container.style.backgroundImage=``;this.calculateScale(t.layout),this.attachActionListeners(t.layout),this.emit(`layoutStart`,e,t.layout);for(let[e,t]of this.regions)t.currentIndex=0,t.complete=!1,this.startRegion(e);this.updateLayoutDuration(),this.startLayoutTimerWhenReady(e,t.layout),this.preloadTimer||this._scheduleNextLayoutPreload(t.layout)}_teardownOldLayoutAfterTransition(e,t,n){if(n&&e!==null){this.layoutPool.evict(e);return}for(let[,e]of t)if(l.releaseMediaElements(e.element),e.config?.exitTransition){let t=u.apply(e.element,e.config.exitTransition,!1,e.width,e.height);if(t){let n=e.element;t.onfinish=()=>n.remove()}else e.element.remove()}else e.element.remove();e&&this.revokeBlobUrlsForLayout(e)}_logResourceStats(e){let t=document.querySelectorAll(`*`).length,n=document.querySelectorAll(`video`).length,r=document.querySelectorAll(`video[src]`).length,i=document.querySelectorAll(`canvas`).length,a=document.querySelectorAll(`iframe`).length,o=document.querySelectorAll(`img`).length,s=this.layoutPool?this.layoutPool.size:0,c=this.regions?this.regions.size:0,l=[...this.regions?.values()||[]].reduce((e,t)=>e+(t.widgetElements?.size||0),0),u=performance?.memory?{used:Math.round(performance.memory.usedJSHeapSize/1048576),total:Math.round(performance.memory.totalJSHeapSize/1048576),limit:Math.round(performance.memory.jsHeapSizeLimit/1048576)}:null,d=this._blobUrls?[...this._blobUrls.values()].reduce((e,t)=>e+t.size,0):0,f=this._blobUrls?this._blobUrls.size:0,p=document.querySelectorAll(`.renderer-lite-preload-wrapper`).length,m=document.querySelectorAll(`audio`).length,h=u?`heap=${u.used}/${u.total}MB (limit ${u.limit}MB)`:`heap=N/A`;this.log.info(`[Resources] layout=${e} dom=${t} videos=${n}(src=${r}) canvas=${i} iframe=${a} img=${o} audio=${m} pool=${s} preloadWrappers=${p} regions=${c} widgets=${l} blobs=${d}(${f} layouts) ${h}`)}getCurrentLayoutId(){return this.currentLayoutId}showLayout(e){if(e===void 0&&(e=this.layoutPool.getLatest(),e===void 0)){this.log.warn(`showLayout: no preloaded layout to show`);return}if(this.currentLayoutId===e){this.log.info(`showLayout: layout ${e} already showing`);return}if(!this.layoutPool.has(e)){this.log.warn(`showLayout: layout ${e} not in preload pool`);return}this._swapToPreloadedLayout(e)}hasActiveLayoutTimer(){return this.layoutTimer!==null||this._deferredTimerLayoutId!==null}checkLayoutComplete(){let e=!0;for(let[t,n]of this.regions)if(n.widgets.length>1&&!n.complete){e=!1;break}e&&this.currentLayoutId&&this.log.info(`All multi-widget regions completed one cycle`)}_clearLayoutTimers(){this.layoutTimer&&=(clearTimeout(this.layoutTimer),null),this.preloadTimer&&=(clearTimeout(this.preloadTimer),null),this._preloadRetryTimer&&=(clearTimeout(this._preloadRetryTimer),null)}stopCurrentLayout(){if(!this.currentLayout)return;this.log.info(`Stopping layout ${this.currentLayoutId}`);let e=this.currentLayoutId,t=e&&!this.layoutEndEmitted;if(this.layoutEndEmitted=!1,this._deferredTimerLayoutId=null,this._deferredTimerFallback&&=(clearTimeout(this._deferredTimerFallback),null),this.currentLayout=null,this.currentLayoutId=null,this._clearLayoutTimers(),this.removeActionListeners(),e&&this.layoutPool.has(e))this.layoutPool.evict(e);else{e&&this.revokeBlobUrlsForLayout(e),this._clearRegionTimers(this.regions),this._stopAllRegionWidgets(this.regions,this._stopWidgetBound);for(let[,e]of this.regions)if(l.releaseMediaElements(e.element),e.config?.exitTransition){let t=u.apply(e.element,e.config.exitTransition,!1,e.width,e.height);if(t){let n=e.element;t.onfinish=()=>n.remove()}else e.element.remove()}else e.element.remove()}this.regions.clear(),t&&this.emit(`layoutEnd`,e)}async renderOverlay(e,t,n=0){try{if(this.log.info(`Rendering overlay ${t} (priority ${n})`),this.activeOverlays.has(t)){this.log.warn(`Overlay ${t} already active, skipping`);return}let r=this.parseXlf(e),i=document.createElement(`div`);i.id=`overlay_${t}`,i.className=`renderer-lite-overlay`,i.style.position=`absolute`,i.style.top=`0`,i.style.left=`0`,i.style.width=`100%`,i.style.height=`100%`,i.style.zIndex=String(1e3+n),i.style.pointerEvents=`auto`,i.style.backgroundColor=r.bgcolor,this.calculateScale(r);let a=new Map;for(let e of r.regions){let n=this._createRegionEntry(e,`overlay_${t}_region_${e.id}`,i,{className:`renderer-lite-region overlay-region`,isCanvas:e.isCanvas||!1});a.set(e.id,n)}for(let[e,n]of a)for(let r of n.widgets){r.layoutId=t,r.regionId=e;try{let e=await this.createWidgetElement(r,n);this._positionWidgetElement(e),n.element.appendChild(e),n.widgetElements.set(r.id,e)}catch(e){this.log.error(`Failed to pre-create overlay widget ${r.id}:`,e)}}this.overlayContainer.appendChild(i),this.activeOverlays.set(t,{container:i,layout:r,regions:a,timer:null,priority:n}),this.emit(`overlayStart`,t,r);for(let[e,n]of a)this.startOverlayRegion(t,e);if(r.duration>0){let e=r.duration*1e3,n=this.activeOverlays.get(t);n&&(n.timer=setTimeout(()=>{this.log.info(`Overlay ${t} duration expired (${r.duration}s)`),this.emit(`overlayEnd`,t)},e))}this.log.info(`Overlay ${t} started`)}catch(e){throw this.log.error(`Error rendering overlay:`,e),this.emit(`error`,{type:`overlayError`,error:e,layoutId:t}),e}}startOverlayRegion(e,t){let n=this.activeOverlays.get(e);if(!n)return;let r=n.regions.get(t);this._startRegionCycle(r,t,(t,n)=>this.renderOverlayWidget(e,t,n),(t,n)=>this.stopOverlayWidget(e,t,n),()=>this.log.info(`Overlay ${e} region ${t} completed one full cycle`))}async renderOverlayWidget(e,t,n){let r=this.activeOverlays.get(e);if(!r)return;let i=r.regions.get(t);if(i)try{let r=await this._showWidget(i,n);r&&(this.log.info(`Showing overlay widget ${r.type} (${r.id}) in overlay ${e} region ${t}`),this._startedWidgets.add(`overlay:${e}:${t}:${n}`),this.emit(`overlayWidgetStart`,{overlayId:e,widgetId:r.id,regionId:t,type:r.type,duration:r.duration}))}catch(r){this.log.error(`Error rendering overlay widget:`,r),this.emit(`error`,{type:`overlayWidgetError`,error:r,widgetId:i.widgets[n]?.id,regionId:t,overlayId:e})}}async stopOverlayWidget(e,t,n){let r=`overlay:${e}:${t}:${n}`;if(!this._startedWidgets.delete(r))return;let i=this.activeOverlays.get(e);if(!i)return;let a=i.regions.get(t);if(!a)return;let{widget:o,animPromise:s}=this._hideWidget(a,n);o&&this.emit(`overlayWidgetEnd`,{overlayId:e,widgetId:o.id,regionId:t,type:o.type}),s&&await s}stopOverlay(e){let t=this.activeOverlays.get(e);if(!t){this.log.warn(`Overlay ${e} not active`);return}this.log.info(`Stopping overlay ${e}`),t.timer&&=(clearTimeout(t.timer),null);for(let[,e]of t.regions)e.timer&&=(clearTimeout(e.timer),null);this._stopAllRegionWidgets(t.regions,(t,n)=>this.stopOverlayWidget(e,t,n)),t.container&&t.container.remove(),this.revokeBlobUrlsForLayout(e),this.activeOverlays.delete(e),this.emit(`overlayEnd`,e),this.log.info(`Overlay ${e} stopped`)}stopAllOverlays(){let e=Array.from(this.activeOverlays.keys());for(let t of e)this.stopOverlay(t);this.log.info(`All overlays stopped`)}getActiveOverlays(){return Array.from(this.activeOverlays.keys())}pause(){if(!this._paused){this._paused=!0;for(let[,e]of this.regions)e.timer&&=(clearTimeout(e.timer),null);this._forEachMedia(e=>e.pause()),this.emit(`paused`),this.log.info(`Playback paused (layout timer continues)`)}}isPaused(){return this._paused}resume(){if(this._paused){this._paused=!1,this._forEachMedia(e=>e.play().catch(()=>{}));for(let[e]of this.regions)this.startRegion(e);this.emit(`resumed`),this.log.info(`Playback resumed`)}}_forEachMedia(e){for(let[,t]of this.regions)t.element?.querySelectorAll(`video, audio`).forEach(e)}cleanup(){this.stopAllOverlays(),this.stopCurrentLayout(),this._startedWidgets.clear();for(let e of this.audioOverlays.keys())this._stopAudioOverlays(e);this.layoutPool.clear(),this.preloadTimer&&=(clearTimeout(this.preloadTimer),null),this._preloadRetryTimer&&=(clearTimeout(this._preloadRetryTimer),null),this.resizeObserver&&=(this.resizeObserver.disconnect(),null),this.container.innerHTML=``,this.log.info(`Cleaned up`)}},f=i(`Layout`),p=/^(#[0-9a-fA-F]{3,8}|rgba?\(\s*[\d.,\s%]+\)|[a-zA-Z]{1,20}|transparent|inherit)$/,m=e=>p.test(e)?e:`#000000`,h=e=>JSON.stringify(e),g=class{constructor(e){this.xmds=e}async translateXLF(e,t){let n=new DOMParser().parseFromString(t,`text/xml`),r=n.querySelector(`layout`);if(!r)throw Error(`Invalid XLF: no <layout> element`);let i=parseInt(r.getAttribute(`width`)||`1920`),a=parseInt(r.getAttribute(`height`)||`1080`),o=m(r.getAttribute(`bgcolor`)||`#000000`),s=[];for(let t of n.querySelectorAll(`region`))s.push(await this.translateRegion(e,t));return this.generateHTML(i,a,o,s)}async translateRegion(e,t){let n=t.getAttribute(`id`),r=parseInt(t.getAttribute(`width`)),i=parseInt(t.getAttribute(`height`)),a=parseInt(t.getAttribute(`top`)),o=parseInt(t.getAttribute(`left`)),s=parseInt(t.getAttribute(`zindex`)||`0`),c=[];for(let r of t.querySelectorAll(`media`))c.push(await this.translateMedia(e,n,r));return{id:n,width:r,height:i,top:a,left:o,zindex:s,media:c}}async translateMedia(e,t,n){let i=n.getAttribute(`type`),o=parseInt(n.getAttribute(`duration`)||`10`),s=n.getAttribute(`id`),c=n.querySelector(`options`),l=n.querySelector(`raw`),u={};if(c)for(let e of c.children)u[e.tagName]=e.textContent;let d={in:null,out:null},p=n.querySelector(`options > transIn`),m=n.querySelector(`options > transOut`),h=n.querySelector(`options > transInDuration`),g=n.querySelector(`options > transOutDuration`),_=n.querySelector(`options > transInDirection`),v=n.querySelector(`options > transOutDirection`);p&&p.textContent&&(d.in={type:p.textContent,duration:parseInt(h?.textContent||`1000`),direction:_?.textContent||`N`}),m&&m.textContent&&(d.out={type:m.textContent,duration:parseInt(g?.textContent||`1000`),direction:v?.textContent||`N`});let y=l?l.textContent:``;if([`clock`,`clock-digital`,`clock-analogue`,`calendar`,`weather`,`currencies`,`stocks`,`twitter`,`global`,`embedded`,`text`,`ticker`].some(e=>i.includes(e))){let n=null;for(let r=1;r<=3;r++)try{f.info(`Fetching resource for ${i} widget (layout=${e}, region=${t}, media=${s}) - attempt ${r}/3`),y=await this.xmds.getResource(e,t,s),f.info(`Got resource HTML (${y.length} chars)`),u.widgetCacheKey=await a(e,t,s,y);break}catch(e){if(n=e,f.warn(`Failed to get resource (attempt ${r}/3):`,e.message),r<3){let e=r*2e3;f.info(`Retrying in ${e}ms...`),await new Promise(t=>setTimeout(t,e))}}if(!y&&n){f.warn(`All retries failed, checking for cached widget HTML...`);try{let n=await fetch(`/store${r}/widgets/${e}/${t}/${s}`);n.ok?(y=await n.text(),u.widgetCacheKey=`${r}/widgets/${e}/${t}/${s}`,f.info(`Using stored widget HTML (${y.length} chars) - CMS update pending`)):(f.error(`No stored version available for widget ${s}`),y=`<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#999;font-size:18px;">Content updating...</div>`)}catch(e){f.error(`Store fallback failed:`,e),y=`<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#999;font-size:18px;">Content updating...</div>`}}}return{type:i,duration:o,id:s,options:u,raw:y,transitions:d}}generateHTML(e,t,n,r){return`<!DOCTYPE html>
|
|
3
3
|
<html>
|
|
4
4
|
<head>
|
|
5
5
|
<meta charset="utf-8">
|
|
@@ -626,4 +626,4 @@ ${t}
|
|
|
626
626
|
stop: ${c},
|
|
627
627
|
duration: ${i}
|
|
628
628
|
}`}},_=s.version;export{l as LayoutPool,g as LayoutTranslator,d as RendererLite,_ as VERSION};
|
|
629
|
-
//# sourceMappingURL=src-
|
|
629
|
+
//# sourceMappingURL=src-CKpVxGpH.js.map
|