@xiboplayer/pwa 0.1.0

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.
Files changed (34) hide show
  1. package/README.md +60 -0
  2. package/dist/assets/cache-proxy-Cx4Z8XMC.js +2 -0
  3. package/dist/assets/cache-proxy-Cx4Z8XMC.js.map +1 -0
  4. package/dist/assets/cms-api-kzy_Sw-u.js +2 -0
  5. package/dist/assets/cms-api-kzy_Sw-u.js.map +1 -0
  6. package/dist/assets/html2canvas.esm-CBrSDip1.js +23 -0
  7. package/dist/assets/html2canvas.esm-CBrSDip1.js.map +1 -0
  8. package/dist/assets/index-BEhNaWZ4.js +2 -0
  9. package/dist/assets/index-BEhNaWZ4.js.map +1 -0
  10. package/dist/assets/index-BPNsrSEv.js +2 -0
  11. package/dist/assets/index-BPNsrSEv.js.map +1 -0
  12. package/dist/assets/index-BY2j60YZ.js +2 -0
  13. package/dist/assets/index-BY2j60YZ.js.map +1 -0
  14. package/dist/assets/index-CTmjUTVM.js +8 -0
  15. package/dist/assets/index-CTmjUTVM.js.map +1 -0
  16. package/dist/assets/index-_q2HbdAU.js +2 -0
  17. package/dist/assets/index-_q2HbdAU.js.map +1 -0
  18. package/dist/assets/index-_uzldOpz.js +16 -0
  19. package/dist/assets/index-_uzldOpz.js.map +1 -0
  20. package/dist/assets/main-C4ABDfkq.js +27 -0
  21. package/dist/assets/main-C4ABDfkq.js.map +1 -0
  22. package/dist/assets/modulepreload-polyfill-B5Qt9EMX.js +2 -0
  23. package/dist/assets/modulepreload-polyfill-B5Qt9EMX.js.map +1 -0
  24. package/dist/assets/pdf-BnPRJEQ6.js +13 -0
  25. package/dist/assets/pdf-BnPRJEQ6.js.map +1 -0
  26. package/dist/assets/setup-rqZh5qYs.js +2 -0
  27. package/dist/assets/setup-rqZh5qYs.js.map +1 -0
  28. package/dist/index.html +130 -0
  29. package/dist/setup.html +371 -0
  30. package/dist/sw-pwa.js +2 -0
  31. package/dist/sw-pwa.js.map +1 -0
  32. package/dist/sw-utils.js +210 -0
  33. package/dist/sw.test.js +271 -0
  34. package/package.json +39 -0
@@ -0,0 +1,16 @@
1
+ import{f as m,c as f}from"./cms-api-kzy_Sw-u.js";function p(y){const t=[];for(const e of y.querySelectorAll(":scope > criteria"))t.push({metric:e.getAttribute("metric")||"",condition:e.getAttribute("condition")||"",type:e.getAttribute("type")||"string",value:e.textContent||""});return t}function A(y){const e=new DOMParser().parseFromString(y,"text/xml"),s={default:null,layouts:[],campaigns:[],overlays:[],actions:[],commands:[],dataConnectors:[]},n=e.querySelector("default");n&&(s.default=n.getAttribute("file"));for(const r of e.querySelectorAll("campaign")){const o={id:r.getAttribute("id"),priority:parseInt(r.getAttribute("priority")||"0"),fromdt:r.getAttribute("fromdt"),todt:r.getAttribute("todt"),scheduleid:r.getAttribute("scheduleid"),layouts:[]};for(const l of r.querySelectorAll("layout")){const c=l.getAttribute("file");o.layouts.push({id:String(c),file:c,fromdt:l.getAttribute("fromdt")||o.fromdt,todt:l.getAttribute("todt")||o.todt,scheduleid:o.scheduleid,priority:o.priority,campaignId:o.id,maxPlaysPerHour:parseInt(l.getAttribute("maxPlaysPerHour")||"0"),isGeoAware:l.getAttribute("isGeoAware")==="1",geoLocation:l.getAttribute("geoLocation")||"",syncEvent:l.getAttribute("syncEvent")==="1",shareOfVoice:parseInt(l.getAttribute("shareOfVoice")||"0"),criteria:p(l)})}s.campaigns.push(o)}for(const r of e.querySelectorAll("schedule > layout")){const o=r.getAttribute("file");s.layouts.push({id:String(o),file:o,fromdt:r.getAttribute("fromdt"),todt:r.getAttribute("todt"),scheduleid:r.getAttribute("scheduleid"),priority:parseInt(r.getAttribute("priority")||"0"),campaignId:null,maxPlaysPerHour:parseInt(r.getAttribute("maxPlaysPerHour")||"0"),isGeoAware:r.getAttribute("isGeoAware")==="1",geoLocation:r.getAttribute("geoLocation")||"",syncEvent:r.getAttribute("syncEvent")==="1",shareOfVoice:parseInt(r.getAttribute("shareOfVoice")||"0"),criteria:p(r)})}const i=e.querySelector("overlays");if(i)for(const r of i.querySelectorAll("overlay")){const o=r.getAttribute("file");s.overlays.push({id:String(o),duration:parseInt(r.getAttribute("duration")||"60"),file:o,fromDt:r.getAttribute("fromdt"),toDt:r.getAttribute("todt"),priority:parseInt(r.getAttribute("priority")||"0"),scheduleId:r.getAttribute("scheduleid"),isGeoAware:r.getAttribute("isGeoAware")==="1",geoLocation:r.getAttribute("geoLocation")||"",syncEvent:r.getAttribute("syncEvent")==="1",maxPlaysPerHour:parseInt(r.getAttribute("maxPlaysPerHour")||"0"),criteria:p(r)})}const a=e.querySelector("actions");if(a)for(const r of a.querySelectorAll("action"))s.actions.push({actionType:r.getAttribute("actionType")||"",triggerCode:r.getAttribute("triggerCode")||"",layoutCode:r.getAttribute("layoutCode")||"",commandCode:r.getAttribute("commandCode")||"",duration:parseInt(r.getAttribute("duration")||"0"),fromDt:r.getAttribute("fromdt"),toDt:r.getAttribute("todt"),priority:parseInt(r.getAttribute("priority")||"0"),scheduleId:r.getAttribute("scheduleid"),isGeoAware:r.getAttribute("isGeoAware")==="1",geoLocation:r.getAttribute("geoLocation")||""});for(const r of e.querySelectorAll("schedule > command"))s.commands.push({code:r.getAttribute("command")||"",date:r.getAttribute("date")||""});for(const r of e.querySelectorAll("dataconnector"))s.dataConnectors.push({id:r.getAttribute("id")||"",dataConnectorId:r.getAttribute("dataConnectorId")||"",dataKey:r.getAttribute("dataKey")||"",url:r.getAttribute("url")||"",updateInterval:parseInt(r.getAttribute("updateInterval")||"300",10)});return s}const d=f("REST");class w{constructor(t){this.config=t,this.schemaVersion=7,this.retryOptions=t.retryOptions||{maxRetries:2,baseDelayMs:2e3},this._etags=new Map,this._responseCache=new Map,d.info("Using REST transport")}getRestBaseUrl(){return(this.config.restApiUrl||`${this.config.cmsAddress}/pwa`).replace(/\/+$/,"")}async restGet(t,e={}){const s=new URL(`${this.getRestBaseUrl()}${t}`);s.searchParams.set("serverKey",this.config.cmsKey),s.searchParams.set("hardwareKey",this.config.hardwareKey),s.searchParams.set("v",String(this.schemaVersion));for(const[u,h]of Object.entries(e))s.searchParams.set(u,String(h));const n=t,i={},a=this._etags.get(n);a&&(i["If-None-Match"]=a),d.debug(`GET ${t}`,e);const r=await m(s.toString(),{method:"GET",headers:i},this.retryOptions);if(r.status===304){const u=this._responseCache.get(n);if(u)return d.debug(`${t} → 304 (using cache)`),u}if(!r.ok){const u=await r.text().catch(()=>"");throw new Error(`REST GET ${t} failed: ${r.status} ${r.statusText} ${u}`)}const o=r.headers.get("ETag");o&&this._etags.set(n,o);const l=r.headers.get("Content-Type")||"";let c;return l.includes("application/json")?c=await r.json():c=await r.text(),this._responseCache.set(n,c),c}async restSend(t,e,s={}){const n=new URL(`${this.getRestBaseUrl()}${e}`);n.searchParams.set("v",String(this.schemaVersion)),d.debug(`${t} ${e}`);const i=await m(n.toString(),{method:t,headers:{"Content-Type":"application/json"},body:JSON.stringify({serverKey:this.config.cmsKey,hardwareKey:this.config.hardwareKey,...s})},this.retryOptions);if(!i.ok){const r=await i.text().catch(()=>"");throw new Error(`REST ${t} ${e} failed: ${i.status} ${i.statusText} ${r}`)}return(i.headers.get("Content-Type")||"").includes("application/json")?await i.json():await i.text()}async registerDisplay(){const t=typeof navigator<"u"?`${navigator.platform} ${navigator.userAgent}`:"unknown",e=await this.restSend("POST","/register",{displayName:this.config.displayName,clientType:"chromeOS",clientVersion:"0.1.0",clientCode:1,operatingSystem:t,macAddress:this.config.macAddress||"n/a",xmrChannel:this.config.xmrChannel,xmrPubKey:""});return this._parseRegisterDisplayJson(e)}_parseRegisterDisplayJson(t){const e=t.display||t,s=e["@attributes"]||{},n=s.code||e.code,i=s.message||e.message||"";if(n!=="READY")return{code:n,message:i,settings:null};const a={};for(const[c,u]of Object.entries(e))c==="@attributes"||c==="commands"||c==="file"||(a[c]=typeof u=="object"?JSON.stringify(u):String(u));const r=s.checkRf||"",o=s.checkSchedule||"",l=e.syncGroup?{syncGroup:String(e.syncGroup),syncPublisherPort:parseInt(e.syncPublisherPort||"9590",10),syncSwitchDelay:parseInt(e.syncSwitchDelay||"750",10),syncVideoPauseDelay:parseInt(e.syncVideoPauseDelay||"100",10),isLead:String(e.syncGroup)==="lead"}:null;return{code:n,message:i,settings:a,checkRf:r,checkSchedule:o,syncConfig:l}}async requiredFiles(){const t=await this.restGet("/requiredFiles");return this._parseRequiredFilesJson(t)}_parseRequiredFilesJson(t){const e=[];let s=t.file||[];Array.isArray(s)||(s=[s]);for(const n of s){const i=n["@attributes"]||n,a=i.path||null;e.push({type:i.type||null,id:i.id||null,size:parseInt(i.size||"0"),md5:i.md5||null,download:i.download||null,path:a,code:i.code||null,layoutid:i.layoutid||null,regionid:i.regionid||null,mediaid:i.mediaid||null})}return e}async schedule(){const t=await this.restGet("/schedule");return A(t)}async getResource(t,e,s){return this.restGet("/getResource",{layoutId:String(t),regionId:String(e),mediaId:String(s)})}async notifyStatus(t){var e;if(typeof navigator<"u"&&((e=navigator.storage)!=null&&e.estimate))try{const s=await navigator.storage.estimate();t.availableSpace=s.quota-s.usage,t.totalSpace=s.quota}catch{}return!t.timeZone&&typeof Intl<"u"&&(t.timeZone=Intl.DateTimeFormat().resolvedOptions().timeZone),this.restSend("PUT","/status",{statusData:t})}async mediaInventory(t){const e=Array.isArray(t)?{inventoryItems:t}:{inventory:t};return this.restSend("POST","/mediaInventory",e)}async blackList(t,e,s){return d.warn(`BlackList not available via REST (${e}/${t}: ${s})`),!1}async submitLog(t){const e=Array.isArray(t)?{logs:t}:{logXml:t},s=await this.restSend("POST","/log",e);return(s==null?void 0:s.success)===!0}async submitScreenShot(t){const e=await this.restSend("POST","/screenshot",{screenshot:t});return(e==null?void 0:e.success)===!0}async submitStats(t){try{const e=Array.isArray(t)?{stats:t}:{statXml:t},s=await this.restSend("POST","/stats",e),n=(s==null?void 0:s.success)===!0;return d.info(`SubmitStats result: ${n}`),n}catch(e){throw d.error("SubmitStats failed:",e),e}}}const g=f("XMDS");class S{constructor(t){this.config=t,this.schemaVersion=5,this.retryOptions=t.retryOptions||{maxRetries:2,baseDelayMs:2e3}}buildEnvelope(t,e){const s=Object.entries(e).map(([n,i])=>{const a=String(i).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;");return`<${n} xsi:type="xsd:string">${a}</${n}>`}).join(`
2
+ `);return`<?xml version="1.0" encoding="UTF-8"?>
3
+ <soap:Envelope
4
+ xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
5
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
6
+ xmlns:tns="urn:xmds"
7
+ xmlns:types="urn:xmds/encodedTypes"
8
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
9
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema">
10
+ <soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
11
+ <tns:${t}>
12
+ ${s}
13
+ </tns:${t}>
14
+ </soap:Body>
15
+ </soap:Envelope>`}rewriteXmdsUrl(t){var e;return typeof window<"u"&&((e=window.electronAPI)!=null&&e.isElectron||window.location.hostname==="localhost"&&window.location.port==="8765")?`/xmds-proxy?cms=${encodeURIComponent(t)}`:`${t}/xmds.php`}async call(t,e={}){const s=this.rewriteXmdsUrl(this.config.cmsAddress),n=`${s}${s.includes("?")?"&":"?"}v=${this.schemaVersion}`,i=this.buildEnvelope(t,e);g.debug(`${t}`,e),g.debug(`URL: ${n}`);const a=await m(n,{method:"POST",headers:{"Content-Type":"text/xml; charset=utf-8"},body:i},this.retryOptions);if(!a.ok)throw new Error(`XMDS ${t} failed: ${a.status} ${a.statusText}`);const r=await a.text();return this.parseResponse(r,t)}parseResponse(t,e){var l,c;const n=new DOMParser().parseFromString(t,"text/xml");let i=n.querySelector("Fault");if(i||(i=Array.from(n.querySelectorAll("*")).find(u=>u.localName==="Fault"||u.tagName.endsWith(":Fault"))),i){const u=((l=i.querySelector("faultstring"))==null?void 0:l.textContent)||((c=Array.from(i.querySelectorAll("*")).find(h=>h.localName==="faultstring"))==null?void 0:c.textContent)||"Unknown SOAP fault";throw new Error(`SOAP Fault: ${u}`)}const a=`${e}Response`;let r=n.querySelector(a);if(r||(r=Array.from(n.querySelectorAll("*")).find(u=>u.localName===a||u.tagName.endsWith(":"+a))),!r)throw new Error(`No ${a} element in SOAP response`);const o=r.firstElementChild;return o?o.textContent:null}async registerDisplay(){const t=`${navigator.platform} ${navigator.userAgent}`,e=await this.call("RegisterDisplay",{serverKey:this.config.cmsKey,hardwareKey:this.config.hardwareKey,displayName:this.config.displayName,clientType:"chromeOS",clientVersion:"0.1.0",clientCode:"1",operatingSystem:t,macAddress:this.config.macAddress||"n/a",xmrChannel:this.config.xmrChannel,xmrPubKey:""});return this.parseRegisterDisplayResponse(e)}parseRegisterDisplayResponse(t){const n=new DOMParser().parseFromString(t,"text/xml").querySelector("display");if(!n)throw new Error("Invalid RegisterDisplay response: no <display> element");const i=n.getAttribute("code"),a=n.getAttribute("message");if(i!=="READY")return{code:i,message:a,settings:null};const r={};for(const c of n.children)["commands","file"].includes(c.tagName.toLowerCase())||(r[c.tagName]=c.textContent);const o=n.getAttribute("checkRf")||"",l=n.getAttribute("checkSchedule")||"";return{code:i,message:a,settings:r,checkRf:o,checkSchedule:l}}async requiredFiles(){const t=await this.call("RequiredFiles",{serverKey:this.config.cmsKey,hardwareKey:this.config.hardwareKey});return this.parseRequiredFilesResponse(t)}parseRequiredFilesResponse(t){const s=new DOMParser().parseFromString(t,"text/xml"),n=[];for(const i of s.querySelectorAll("file"))n.push({type:i.getAttribute("type"),id:i.getAttribute("id"),size:parseInt(i.getAttribute("size")||"0"),md5:i.getAttribute("md5"),download:i.getAttribute("download"),path:i.getAttribute("path"),code:i.getAttribute("code"),layoutid:i.getAttribute("layoutid"),regionid:i.getAttribute("regionid"),mediaid:i.getAttribute("mediaid")});return n}async schedule(){const t=await this.call("Schedule",{serverKey:this.config.cmsKey,hardwareKey:this.config.hardwareKey});return A(t)}async getResource(t,e,s){return await this.call("GetResource",{serverKey:this.config.cmsKey,hardwareKey:this.config.hardwareKey,layoutId:String(t),regionId:String(e),mediaId:String(s)})}async notifyStatus(t){var e;if(typeof navigator<"u"&&((e=navigator.storage)!=null&&e.estimate))try{const s=await navigator.storage.estimate();t.availableSpace=s.quota-s.usage,t.totalSpace=s.quota}catch{}return!t.timeZone&&typeof Intl<"u"&&(t.timeZone=Intl.DateTimeFormat().resolvedOptions().timeZone),await this.call("NotifyStatus",{serverKey:this.config.cmsKey,hardwareKey:this.config.hardwareKey,status:JSON.stringify(t)})}async mediaInventory(t){return await this.call("MediaInventory",{serverKey:this.config.cmsKey,hardwareKey:this.config.hardwareKey,mediaInventory:t})}async blackList(t,e,s){try{const n=await this.call("BlackList",{serverKey:this.config.cmsKey,hardwareKey:this.config.hardwareKey,mediaId:String(t),type:e||"media",reason:s||"Failed to render"});return g.info(`BlackListed ${e}/${t}: ${s}`),n==="true"}catch(n){return g.warn("BlackList failed:",n),!1}}async submitLog(t){return await this.call("SubmitLog",{serverKey:this.config.cmsKey,hardwareKey:this.config.hardwareKey,logXml:t})==="true"}async submitScreenShot(t){return await this.call("SubmitScreenShot",{serverKey:this.config.cmsKey,hardwareKey:this.config.hardwareKey,screenShot:t})==="true"}async submitStats(t){try{const s=await this.call("SubmitStats",{serverKey:this.config.cmsKey,hardwareKey:this.config.hardwareKey,statXml:t})==="true";return g.info(`SubmitStats result: ${s}`),s}catch(e){throw g.error("SubmitStats failed:",e),e}}}export{w as RestClient,S as XmdsClient,A as parseScheduleResponse};
16
+ //# sourceMappingURL=index-_uzldOpz.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-_uzldOpz.js","sources":["../../node_modules/.pnpm/@xiboplayer+xmds@0.1.0/node_modules/@xiboplayer/xmds/src/schedule-parser.js","../../node_modules/.pnpm/@xiboplayer+xmds@0.1.0/node_modules/@xiboplayer/xmds/src/rest-client.js","../../node_modules/.pnpm/@xiboplayer+xmds@0.1.0/node_modules/@xiboplayer/xmds/src/xmds-client.js"],"sourcesContent":["/**\n * Shared schedule XML parser used by both RestClient and XmdsClient.\n *\n * Both transports return the same XML structure for the Schedule endpoint,\n * so the parsing logic lives here to avoid duplication.\n */\n\n/**\n * Parse criteria child elements from a layout/overlay element.\n * Criteria are conditions that must be met for the item to display.\n *\n * XML format: <criteria metric=\"dayOfWeek\" condition=\"equals\" type=\"string\">Monday</criteria>\n *\n * @param {Element} parentEl - Parent XML element containing <criteria> children\n * @returns {Array<{metric: string, condition: string, type: string, value: string}>}\n */\nfunction parseCriteria(parentEl) {\n const criteria = [];\n for (const criteriaEl of parentEl.querySelectorAll(':scope > criteria')) {\n criteria.push({\n metric: criteriaEl.getAttribute('metric') || '',\n condition: criteriaEl.getAttribute('condition') || '',\n type: criteriaEl.getAttribute('type') || 'string',\n value: criteriaEl.textContent || ''\n });\n }\n return criteria;\n}\n\n/**\n * Parse Schedule XML response into a normalized schedule object.\n *\n * @param {string} xml - Raw XML string from CMS schedule endpoint\n * @returns {Object} Parsed schedule with default, layouts, campaigns, overlays, actions, commands, dataConnectors\n */\nexport function parseScheduleResponse(xml) {\n const parser = new DOMParser();\n const doc = parser.parseFromString(xml, 'text/xml');\n\n const schedule = {\n default: null,\n layouts: [],\n campaigns: [],\n overlays: [],\n actions: [],\n commands: [],\n dataConnectors: []\n };\n\n const defaultEl = doc.querySelector('default');\n if (defaultEl) {\n schedule.default = defaultEl.getAttribute('file');\n }\n\n // Parse campaigns (groups of layouts with shared priority)\n for (const campaignEl of doc.querySelectorAll('campaign')) {\n const campaign = {\n id: campaignEl.getAttribute('id'),\n priority: parseInt(campaignEl.getAttribute('priority') || '0'),\n fromdt: campaignEl.getAttribute('fromdt'),\n todt: campaignEl.getAttribute('todt'),\n scheduleid: campaignEl.getAttribute('scheduleid'),\n layouts: []\n };\n\n // Parse layouts within this campaign\n for (const layoutEl of campaignEl.querySelectorAll('layout')) {\n const fileId = layoutEl.getAttribute('file');\n campaign.layouts.push({\n id: String(fileId), // Normalized string ID for consistent type usage\n file: fileId,\n // Layouts in campaigns inherit timing from campaign level\n fromdt: layoutEl.getAttribute('fromdt') || campaign.fromdt,\n todt: layoutEl.getAttribute('todt') || campaign.todt,\n scheduleid: campaign.scheduleid,\n priority: campaign.priority, // Priority at campaign level\n campaignId: campaign.id,\n maxPlaysPerHour: parseInt(layoutEl.getAttribute('maxPlaysPerHour') || '0'),\n isGeoAware: layoutEl.getAttribute('isGeoAware') === '1',\n geoLocation: layoutEl.getAttribute('geoLocation') || '',\n syncEvent: layoutEl.getAttribute('syncEvent') === '1',\n shareOfVoice: parseInt(layoutEl.getAttribute('shareOfVoice') || '0'),\n criteria: parseCriteria(layoutEl)\n });\n }\n\n schedule.campaigns.push(campaign);\n }\n\n // Parse standalone layouts (not in campaigns)\n for (const layoutEl of doc.querySelectorAll('schedule > layout')) {\n const fileId = layoutEl.getAttribute('file');\n schedule.layouts.push({\n id: String(fileId), // Normalized string ID for consistent type usage\n file: fileId,\n fromdt: layoutEl.getAttribute('fromdt'),\n todt: layoutEl.getAttribute('todt'),\n scheduleid: layoutEl.getAttribute('scheduleid'),\n priority: parseInt(layoutEl.getAttribute('priority') || '0'),\n campaignId: null, // Standalone layout\n maxPlaysPerHour: parseInt(layoutEl.getAttribute('maxPlaysPerHour') || '0'),\n isGeoAware: layoutEl.getAttribute('isGeoAware') === '1',\n geoLocation: layoutEl.getAttribute('geoLocation') || '',\n syncEvent: layoutEl.getAttribute('syncEvent') === '1',\n shareOfVoice: parseInt(layoutEl.getAttribute('shareOfVoice') || '0'),\n criteria: parseCriteria(layoutEl)\n });\n }\n\n // Parse overlay layouts (appear on top of main layouts)\n const overlaysContainer = doc.querySelector('overlays');\n if (overlaysContainer) {\n for (const overlayEl of overlaysContainer.querySelectorAll('overlay')) {\n const fileId = overlayEl.getAttribute('file');\n schedule.overlays.push({\n id: String(fileId), // Normalized string ID for consistent type usage\n duration: parseInt(overlayEl.getAttribute('duration') || '60'),\n file: fileId,\n fromDt: overlayEl.getAttribute('fromdt'),\n toDt: overlayEl.getAttribute('todt'),\n priority: parseInt(overlayEl.getAttribute('priority') || '0'),\n scheduleId: overlayEl.getAttribute('scheduleid'),\n isGeoAware: overlayEl.getAttribute('isGeoAware') === '1',\n geoLocation: overlayEl.getAttribute('geoLocation') || '',\n syncEvent: overlayEl.getAttribute('syncEvent') === '1',\n maxPlaysPerHour: parseInt(overlayEl.getAttribute('maxPlaysPerHour') || '0'),\n criteria: parseCriteria(overlayEl)\n });\n }\n }\n\n // Parse action events (scheduled triggers)\n const actionsContainer = doc.querySelector('actions');\n if (actionsContainer) {\n for (const actionEl of actionsContainer.querySelectorAll('action')) {\n schedule.actions.push({\n actionType: actionEl.getAttribute('actionType') || '',\n triggerCode: actionEl.getAttribute('triggerCode') || '',\n layoutCode: actionEl.getAttribute('layoutCode') || '',\n commandCode: actionEl.getAttribute('commandCode') || '',\n duration: parseInt(actionEl.getAttribute('duration') || '0'),\n fromDt: actionEl.getAttribute('fromdt'),\n toDt: actionEl.getAttribute('todt'),\n priority: parseInt(actionEl.getAttribute('priority') || '0'),\n scheduleId: actionEl.getAttribute('scheduleid'),\n isGeoAware: actionEl.getAttribute('isGeoAware') === '1',\n geoLocation: actionEl.getAttribute('geoLocation') || ''\n });\n }\n }\n\n // Parse server commands (remote control)\n for (const cmdEl of doc.querySelectorAll('schedule > command')) {\n schedule.commands.push({\n code: cmdEl.getAttribute('command') || '',\n date: cmdEl.getAttribute('date') || ''\n });\n }\n\n // Parse data connectors (real-time data sources for widgets)\n for (const dcEl of doc.querySelectorAll('dataconnector')) {\n schedule.dataConnectors.push({\n id: dcEl.getAttribute('id') || '',\n dataConnectorId: dcEl.getAttribute('dataConnectorId') || '',\n dataKey: dcEl.getAttribute('dataKey') || '',\n url: dcEl.getAttribute('url') || '',\n updateInterval: parseInt(dcEl.getAttribute('updateInterval') || '300', 10)\n });\n }\n\n return schedule;\n}\n","/**\n * REST transport client for Xibo CMS.\n *\n * Uses the /pwa REST API endpoints with JSON payloads and ETag caching.\n * Lighter than SOAP — ~30% smaller payloads, standard HTTP semantics.\n *\n * Protocol: https://github.com/linuxnow/xibo_players_docs\n */\nimport { createLogger, fetchWithRetry } from '@xiboplayer/utils';\nimport { parseScheduleResponse } from './schedule-parser.js';\n\nconst log = createLogger('REST');\n\nexport class RestClient {\n constructor(config) {\n this.config = config;\n this.schemaVersion = 7;\n this.retryOptions = config.retryOptions || { maxRetries: 2, baseDelayMs: 2000 };\n\n // ETag-based HTTP caching\n this._etags = new Map(); // endpoint → ETag string\n this._responseCache = new Map(); // endpoint → cached parsed response\n\n log.info('Using REST transport');\n }\n\n // ─── Transport helpers ──────────────────────────────────────────\n\n /**\n * Get the REST API base URL.\n * Falls back to /pwa path relative to the CMS address.\n */\n getRestBaseUrl() {\n const base = this.config.restApiUrl || `${this.config.cmsAddress}/pwa`;\n return base.replace(/\\/+$/, '');\n }\n\n /**\n * Make a REST GET request with optional ETag caching.\n * Returns the parsed JSON body, or cached data on 304.\n */\n async restGet(path, queryParams = {}) {\n const url = new URL(`${this.getRestBaseUrl()}${path}`);\n url.searchParams.set('serverKey', this.config.cmsKey);\n url.searchParams.set('hardwareKey', this.config.hardwareKey);\n url.searchParams.set('v', String(this.schemaVersion));\n for (const [key, value] of Object.entries(queryParams)) {\n url.searchParams.set(key, String(value));\n }\n\n const cacheKey = path;\n const headers = {};\n const cachedEtag = this._etags.get(cacheKey);\n if (cachedEtag) {\n headers['If-None-Match'] = cachedEtag;\n }\n\n log.debug(`GET ${path}`, queryParams);\n\n const response = await fetchWithRetry(url.toString(), {\n method: 'GET',\n headers,\n }, this.retryOptions);\n\n // 304 Not Modified — return cached response\n if (response.status === 304) {\n const cached = this._responseCache.get(cacheKey);\n if (cached) {\n log.debug(`${path} → 304 (using cache)`);\n return cached;\n }\n // Cache miss despite 304 — fall through to fetch fresh\n }\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n throw new Error(`REST GET ${path} failed: ${response.status} ${response.statusText} ${errorBody}`);\n }\n\n // Store ETag for future requests\n const etag = response.headers.get('ETag');\n if (etag) {\n this._etags.set(cacheKey, etag);\n }\n\n const contentType = response.headers.get('Content-Type') || '';\n let data;\n if (contentType.includes('application/json')) {\n data = await response.json();\n } else {\n // XML or HTML — return raw text\n data = await response.text();\n }\n\n // Cache parsed response for 304 reuse\n this._responseCache.set(cacheKey, data);\n return data;\n }\n\n /**\n * Make a REST POST/PUT request with JSON body.\n * Returns the parsed JSON response.\n */\n async restSend(method, path, body = {}) {\n const url = new URL(`${this.getRestBaseUrl()}${path}`);\n url.searchParams.set('v', String(this.schemaVersion));\n\n log.debug(`${method} ${path}`);\n\n const response = await fetchWithRetry(url.toString(), {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n serverKey: this.config.cmsKey,\n hardwareKey: this.config.hardwareKey,\n ...body,\n }),\n }, this.retryOptions);\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n throw new Error(`REST ${method} ${path} failed: ${response.status} ${response.statusText} ${errorBody}`);\n }\n\n const contentType = response.headers.get('Content-Type') || '';\n if (contentType.includes('application/json')) {\n return await response.json();\n }\n return await response.text();\n }\n\n // ─── Public API ─────────────────────────────────────────────────\n\n /**\n * RegisterDisplay - authenticate and get settings\n * POST /register → JSON with display settings\n */\n async registerDisplay() {\n const os = typeof navigator !== 'undefined'\n ? `${navigator.platform} ${navigator.userAgent}`\n : 'unknown';\n\n const json = await this.restSend('POST', '/register', {\n displayName: this.config.displayName,\n clientType: 'chromeOS',\n clientVersion: '0.1.0',\n clientCode: 1,\n operatingSystem: os,\n macAddress: this.config.macAddress || 'n/a',\n xmrChannel: this.config.xmrChannel,\n xmrPubKey: '',\n });\n\n return this._parseRegisterDisplayJson(json);\n }\n\n /**\n * Parse REST JSON RegisterDisplay response into the same format as SOAP.\n */\n _parseRegisterDisplayJson(json) {\n // Handle both direct object and wrapped {display: ...} forms\n const display = json.display || json;\n const attrs = display['@attributes'] || {};\n const code = attrs.code || display.code;\n const message = attrs.message || display.message || '';\n\n if (code !== 'READY') {\n return { code, message, settings: null };\n }\n\n const settings = {};\n for (const [key, value] of Object.entries(display)) {\n if (key === '@attributes' || key === 'commands' || key === 'file') continue;\n settings[key] = typeof value === 'object' ? JSON.stringify(value) : String(value);\n }\n\n const checkRf = attrs.checkRf || '';\n const checkSchedule = attrs.checkSchedule || '';\n\n // Extract sync group config if present (multi-display sync coordination)\n // syncGroup: \"lead\" if this display is leader, or leader's LAN IP if follower\n const syncConfig = display.syncGroup ? {\n syncGroup: String(display.syncGroup),\n syncPublisherPort: parseInt(display.syncPublisherPort || '9590', 10),\n syncSwitchDelay: parseInt(display.syncSwitchDelay || '750', 10),\n syncVideoPauseDelay: parseInt(display.syncVideoPauseDelay || '100', 10),\n isLead: String(display.syncGroup) === 'lead',\n } : null;\n\n return { code, message, settings, checkRf, checkSchedule, syncConfig };\n }\n\n /**\n * RequiredFiles - get list of files to download\n * GET /requiredFiles → JSON file manifest (with ETag caching)\n */\n async requiredFiles() {\n const json = await this.restGet('/requiredFiles');\n return this._parseRequiredFilesJson(json);\n }\n\n /**\n * Parse REST JSON RequiredFiles into the same array format as SOAP.\n */\n _parseRequiredFilesJson(json) {\n const files = [];\n let fileList = json.file || [];\n\n // Normalize single item to array\n if (!Array.isArray(fileList)) {\n fileList = [fileList];\n }\n\n for (const f of fileList) {\n const attrs = f['@attributes'] || f;\n const path = attrs.path || null;\n files.push({\n type: attrs.type || null,\n id: attrs.id || null,\n size: parseInt(attrs.size || '0'),\n md5: attrs.md5 || null,\n download: attrs.download || null,\n path,\n code: attrs.code || null,\n layoutid: attrs.layoutid || null,\n regionid: attrs.regionid || null,\n mediaid: attrs.mediaid || null,\n });\n }\n\n return files;\n }\n\n /**\n * Schedule - get layout schedule\n * GET /schedule → XML (preserved for layout parser compatibility, with ETag caching)\n */\n async schedule() {\n const xml = await this.restGet('/schedule');\n return parseScheduleResponse(xml);\n }\n\n /**\n * GetResource - get rendered widget HTML\n * GET /getResource → HTML string\n */\n async getResource(layoutId, regionId, mediaId) {\n return this.restGet('/getResource', {\n layoutId: String(layoutId),\n regionId: String(regionId),\n mediaId: String(mediaId),\n });\n }\n\n /**\n * NotifyStatus - report current status\n * PUT /status → JSON acknowledgement\n * @param {Object} status - Status object with currentLayoutId, deviceName, etc.\n */\n async notifyStatus(status) {\n // Enrich with storage estimate if available\n if (typeof navigator !== 'undefined' && navigator.storage?.estimate) {\n try {\n const estimate = await navigator.storage.estimate();\n status.availableSpace = estimate.quota - estimate.usage;\n status.totalSpace = estimate.quota;\n } catch (_) { /* storage estimate not supported */ }\n }\n\n // Add timezone if not already provided\n if (!status.timeZone && typeof Intl !== 'undefined') {\n status.timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n }\n\n return this.restSend('PUT', '/status', {\n statusData: status,\n });\n }\n\n /**\n * MediaInventory - report downloaded files\n * POST /mediaInventory → JSON acknowledgement\n */\n async mediaInventory(inventoryXml) {\n // Accept array (JSON-native) or string (XML) — send under the right key\n const body = Array.isArray(inventoryXml)\n ? { inventoryItems: inventoryXml }\n : { inventory: inventoryXml };\n return this.restSend('POST', '/mediaInventory', body);\n }\n\n /**\n * BlackList - report broken media to CMS\n *\n * BlackList has no REST equivalent endpoint.\n * Log a warning and return false.\n */\n async blackList(mediaId, type, reason) {\n log.warn(`BlackList not available via REST (${type}/${mediaId}: ${reason})`);\n return false;\n }\n\n /**\n * SubmitLog - submit player logs to CMS\n * POST /log → JSON acknowledgement\n */\n async submitLog(logXml) {\n // Accept array (JSON-native) or string (XML) — send under the right key\n const body = Array.isArray(logXml) ? { logs: logXml } : { logXml };\n const result = await this.restSend('POST', '/log', body);\n return result?.success === true;\n }\n\n /**\n * SubmitScreenShot - submit screenshot to CMS\n * POST /screenshot → JSON acknowledgement\n */\n async submitScreenShot(base64Image) {\n const result = await this.restSend('POST', '/screenshot', {\n screenshot: base64Image,\n });\n return result?.success === true;\n }\n\n /**\n * SubmitStats - submit proof of play statistics\n * POST /stats → JSON acknowledgement\n */\n async submitStats(statsXml) {\n try {\n // Accept array (JSON-native) or string (XML) — send under the right key\n const body = Array.isArray(statsXml) ? { stats: statsXml } : { statXml: statsXml };\n const result = await this.restSend('POST', '/stats', body);\n const success = result?.success === true;\n log.info(`SubmitStats result: ${success}`);\n return success;\n } catch (error) {\n log.error('SubmitStats failed:', error);\n throw error;\n }\n }\n}\n","/**\n * XMDS SOAP transport client for Xibo CMS.\n *\n * Uses the traditional SOAP/XML endpoint (xmds.php) for full protocol\n * compatibility with all Xibo CMS versions.\n *\n * Protocol: https://github.com/linuxnow/xibo_players_docs\n */\nimport { createLogger, fetchWithRetry } from '@xiboplayer/utils';\nimport { parseScheduleResponse } from './schedule-parser.js';\n\nconst log = createLogger('XMDS');\n\nexport class XmdsClient {\n constructor(config) {\n this.config = config;\n this.schemaVersion = 5;\n this.retryOptions = config.retryOptions || { maxRetries: 2, baseDelayMs: 2000 };\n }\n\n // ─── SOAP transport helpers ─────────────────────────────────────\n\n /**\n * Build SOAP envelope for a given method and parameters\n */\n buildEnvelope(method, params) {\n const paramElements = Object.entries(params)\n .map(([key, value]) => {\n const escaped = String(value)\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n return `<${key} xsi:type=\"xsd:string\">${escaped}</${key}>`;\n })\n .join('\\n ');\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<soap:Envelope\n xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"\n xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"\n xmlns:tns=\"urn:xmds\"\n xmlns:types=\"urn:xmds/encodedTypes\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n <soap:Body soap:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n <tns:${method}>\n ${paramElements}\n </tns:${method}>\n </soap:Body>\n</soap:Envelope>`;\n }\n\n /**\n * Rewrite XMDS URL for Electron proxy.\n * If running inside the Electron shell, use the local proxy to avoid CORS.\n * Detection: preload.js exposes window.electronAPI.isElectron = true,\n * or fallback to checking localhost:8765 (default Electron server port).\n */\n rewriteXmdsUrl(cmsUrl) {\n if (typeof window !== 'undefined' &&\n (window.electronAPI?.isElectron ||\n (window.location.hostname === 'localhost' && window.location.port === '8765'))) {\n const encodedCmsUrl = encodeURIComponent(cmsUrl);\n return `/xmds-proxy?cms=${encodedCmsUrl}`;\n }\n\n return `${cmsUrl}/xmds.php`;\n }\n\n /**\n * Call XMDS SOAP method\n */\n async call(method, params = {}) {\n const xmdsUrl = this.rewriteXmdsUrl(this.config.cmsAddress);\n const url = `${xmdsUrl}${xmdsUrl.includes('?') ? '&' : '?'}v=${this.schemaVersion}`;\n const body = this.buildEnvelope(method, params);\n\n log.debug(`${method}`, params);\n log.debug(`URL: ${url}`);\n\n const response = await fetchWithRetry(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'text/xml; charset=utf-8'\n },\n body\n }, this.retryOptions);\n\n if (!response.ok) {\n throw new Error(`XMDS ${method} failed: ${response.status} ${response.statusText}`);\n }\n\n const xml = await response.text();\n return this.parseResponse(xml, method);\n }\n\n /**\n * Parse SOAP response\n */\n parseResponse(xml, method) {\n const parser = new DOMParser();\n const doc = parser.parseFromString(xml, 'text/xml');\n\n // Check for SOAP fault (handle namespace prefix like soap:Fault)\n let fault = doc.querySelector('Fault');\n if (!fault) {\n fault = Array.from(doc.querySelectorAll('*')).find(\n el => el.localName === 'Fault' || el.tagName.endsWith(':Fault')\n );\n }\n if (fault) {\n const faultString = fault.querySelector('faultstring')?.textContent\n || Array.from(fault.querySelectorAll('*')).find(el => el.localName === 'faultstring')?.textContent\n || 'Unknown SOAP fault';\n throw new Error(`SOAP Fault: ${faultString}`);\n }\n\n // Extract response element (handle namespace prefixes like ns1:MethodResponse)\n const responseTag = `${method}Response`;\n let responseEl = doc.querySelector(responseTag);\n if (!responseEl) {\n responseEl = Array.from(doc.querySelectorAll('*')).find(\n el => el.localName === responseTag || el.tagName.endsWith(':' + responseTag)\n );\n }\n\n if (!responseEl) {\n throw new Error(`No ${responseTag} element in SOAP response`);\n }\n\n const returnEl = responseEl.firstElementChild;\n if (!returnEl) {\n return null;\n }\n\n return returnEl.textContent;\n }\n\n // ─── Public API ─────────────────────────────────────────────────\n\n /**\n * RegisterDisplay - authenticate and get settings\n */\n async registerDisplay() {\n const os = `${navigator.platform} ${navigator.userAgent}`;\n\n const xml = await this.call('RegisterDisplay', {\n serverKey: this.config.cmsKey,\n hardwareKey: this.config.hardwareKey,\n displayName: this.config.displayName,\n clientType: 'chromeOS',\n clientVersion: '0.1.0',\n clientCode: '1',\n operatingSystem: os,\n macAddress: this.config.macAddress || 'n/a',\n xmrChannel: this.config.xmrChannel,\n xmrPubKey: ''\n });\n\n return this.parseRegisterDisplayResponse(xml);\n }\n\n /**\n * Parse RegisterDisplay XML response\n */\n parseRegisterDisplayResponse(xml) {\n const parser = new DOMParser();\n const doc = parser.parseFromString(xml, 'text/xml');\n\n const display = doc.querySelector('display');\n if (!display) {\n throw new Error('Invalid RegisterDisplay response: no <display> element');\n }\n\n const code = display.getAttribute('code');\n const message = display.getAttribute('message');\n\n if (code !== 'READY') {\n return { code, message, settings: null };\n }\n\n const settings = {};\n for (const child of display.children) {\n if (!['commands', 'file'].includes(child.tagName.toLowerCase())) {\n settings[child.tagName] = child.textContent;\n }\n }\n\n const checkRf = display.getAttribute('checkRf') || '';\n const checkSchedule = display.getAttribute('checkSchedule') || '';\n\n return { code, message, settings, checkRf, checkSchedule };\n }\n\n /**\n * RequiredFiles - get list of files to download\n */\n async requiredFiles() {\n const xml = await this.call('RequiredFiles', {\n serverKey: this.config.cmsKey,\n hardwareKey: this.config.hardwareKey\n });\n\n return this.parseRequiredFilesResponse(xml);\n }\n\n /**\n * Parse RequiredFiles XML response\n */\n parseRequiredFilesResponse(xml) {\n const parser = new DOMParser();\n const doc = parser.parseFromString(xml, 'text/xml');\n\n const files = [];\n for (const fileEl of doc.querySelectorAll('file')) {\n files.push({\n type: fileEl.getAttribute('type'),\n id: fileEl.getAttribute('id'),\n size: parseInt(fileEl.getAttribute('size') || '0'),\n md5: fileEl.getAttribute('md5'),\n download: fileEl.getAttribute('download'),\n path: fileEl.getAttribute('path'),\n code: fileEl.getAttribute('code'),\n layoutid: fileEl.getAttribute('layoutid'),\n regionid: fileEl.getAttribute('regionid'),\n mediaid: fileEl.getAttribute('mediaid')\n });\n }\n\n return files;\n }\n\n /**\n * Schedule - get layout schedule\n */\n async schedule() {\n const xml = await this.call('Schedule', {\n serverKey: this.config.cmsKey,\n hardwareKey: this.config.hardwareKey\n });\n\n return parseScheduleResponse(xml);\n }\n\n /**\n * GetResource - get rendered widget HTML\n */\n async getResource(layoutId, regionId, mediaId) {\n const xml = await this.call('GetResource', {\n serverKey: this.config.cmsKey,\n hardwareKey: this.config.hardwareKey,\n layoutId: String(layoutId),\n regionId: String(regionId),\n mediaId: String(mediaId)\n });\n\n return xml;\n }\n\n /**\n * NotifyStatus - report current status\n * @param {Object} status - Status object with currentLayoutId, deviceName, etc.\n */\n async notifyStatus(status) {\n // Enrich with storage estimate if available\n if (typeof navigator !== 'undefined' && navigator.storage?.estimate) {\n try {\n const estimate = await navigator.storage.estimate();\n status.availableSpace = estimate.quota - estimate.usage;\n status.totalSpace = estimate.quota;\n } catch (_) { /* storage estimate not supported */ }\n }\n\n // Add timezone if not already provided\n if (!status.timeZone && typeof Intl !== 'undefined') {\n status.timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n }\n\n return await this.call('NotifyStatus', {\n serverKey: this.config.cmsKey,\n hardwareKey: this.config.hardwareKey,\n status: JSON.stringify(status)\n });\n }\n\n /**\n * MediaInventory - report downloaded files\n */\n async mediaInventory(inventoryXml) {\n return await this.call('MediaInventory', {\n serverKey: this.config.cmsKey,\n hardwareKey: this.config.hardwareKey,\n mediaInventory: inventoryXml\n });\n }\n\n /**\n * BlackList - report broken media to CMS\n * @param {string} mediaId - The media file ID\n * @param {string} type - File type ('media' or 'layout')\n * @param {string} reason - Reason for blacklisting\n * @returns {Promise<boolean>}\n */\n async blackList(mediaId, type, reason) {\n try {\n const xml = await this.call('BlackList', {\n serverKey: this.config.cmsKey,\n hardwareKey: this.config.hardwareKey,\n mediaId: String(mediaId),\n type: type || 'media',\n reason: reason || 'Failed to render'\n });\n log.info(`BlackListed ${type}/${mediaId}: ${reason}`);\n return xml === 'true';\n } catch (error) {\n log.warn('BlackList failed:', error);\n return false;\n }\n }\n\n /**\n * SubmitLog - submit player logs to CMS for remote debugging\n * @param {string} logXml - XML string containing log entries\n * @returns {Promise<boolean>} - true if logs were successfully submitted\n */\n async submitLog(logXml) {\n const xml = await this.call('SubmitLog', {\n serverKey: this.config.cmsKey,\n hardwareKey: this.config.hardwareKey,\n logXml: logXml\n });\n\n return xml === 'true';\n }\n\n /**\n * SubmitScreenShot - submit screenshot to CMS for display verification\n * @param {string} base64Image - Base64-encoded PNG image data\n * @returns {Promise<boolean>} - true if screenshot was successfully submitted\n */\n async submitScreenShot(base64Image) {\n const xml = await this.call('SubmitScreenShot', {\n serverKey: this.config.cmsKey,\n hardwareKey: this.config.hardwareKey,\n screenShot: base64Image\n });\n\n return xml === 'true';\n }\n\n /**\n * SubmitStats - submit proof of play statistics\n * @param {string} statsXml - XML-encoded stats string\n * @returns {Promise<boolean>} - true if stats were successfully submitted\n */\n async submitStats(statsXml) {\n try {\n const xml = await this.call('SubmitStats', {\n serverKey: this.config.cmsKey,\n hardwareKey: this.config.hardwareKey,\n statXml: statsXml\n });\n\n const success = xml === 'true';\n log.info(`SubmitStats result: ${success}`);\n return success;\n } catch (error) {\n log.error('SubmitStats failed:', error);\n throw error;\n }\n }\n}\n"],"names":["parseCriteria","parentEl","criteria","criteriaEl","parseScheduleResponse","xml","doc","schedule","defaultEl","campaignEl","campaign","layoutEl","fileId","overlaysContainer","overlayEl","actionsContainer","actionEl","cmdEl","dcEl","log","createLogger","RestClient","config","path","queryParams","url","key","value","cacheKey","headers","cachedEtag","response","fetchWithRetry","cached","errorBody","etag","contentType","data","method","body","os","json","display","attrs","code","message","settings","checkRf","checkSchedule","syncConfig","files","fileList","f","layoutId","regionId","mediaId","status","_a","estimate","inventoryXml","type","reason","logXml","result","base64Image","statsXml","success","error","XmdsClient","params","paramElements","escaped","cmsUrl","xmdsUrl","fault","el","faultString","_b","responseTag","responseEl","returnEl","child","fileEl"],"mappings":"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","x_google_ignoreList":[0,1,2]}
@@ -0,0 +1,27 @@
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./index-BY2j60YZ.js","./cache-proxy-Cx4Z8XMC.js","./cms-api-kzy_Sw-u.js","./index-_uzldOpz.js","./index-BPNsrSEv.js","./index-_q2HbdAU.js","./index-CTmjUTVM.js","./index-BEhNaWZ4.js"])))=>i.map(i=>d[i]);
2
+ var de=Object.defineProperty;var he=(p,e,t)=>e in p?de(p,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):p[e]=t;var v=(p,e,t)=>he(p,typeof e!="symbol"?e+"":e,t);import"./modulepreload-polyfill-B5Qt9EMX.js";import{c as U,i as se,f as ue,b as ge,r as fe}from"./cms-api-kzy_Sw-u.js";import{E as ae,C as ye}from"./cache-proxy-Cx4Z8XMC.js";const pe="modulepreload",me=function(p,e){return new URL(p,e).href},V={},I=function(e,t,i){let o=Promise.resolve();if(t&&t.length>0){const n=document.getElementsByTagName("link"),s=document.querySelector("meta[property=csp-nonce]"),a=(s==null?void 0:s.nonce)||(s==null?void 0:s.getAttribute("nonce"));o=Promise.allSettled(t.map(c=>{if(c=me(c,i),c in V)return;V[c]=!0;const d=c.endsWith(".css"),g=d?'[rel="stylesheet"]':"";if(!!i)for(let y=n.length-1;y>=0;y--){const m=n[y];if(m.href===c&&(!d||m.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${c}"]${g}`))return;const u=document.createElement("link");if(u.rel=d?"stylesheet":pe,d||(u.as="script"),u.crossOrigin="",u.href=c,a&&u.setAttribute("nonce",a),document.head.appendChild(u),d)return new Promise((y,m)=>{u.addEventListener("load",y),u.addEventListener("error",()=>m(new Error(`Unable to preload CSS for ${c}`)))})}))}function r(n){const s=new Event("vite:preloadError",{cancelable:!0});if(s.payload=n,window.dispatchEvent(s),!s.defaultPrevented)throw n}return o.then(n=>{for(const s of n||[])s.status==="rejected"&&r(s.reason);return e().catch(r)})};let we=()=>({emit(p,...e){for(let t=this.events[p]||[],i=0,o=t.length;i<o;i++)t[i](...e)},events:{},on(p,e){var t;return((t=this.events)[p]||(t[p]=[])).push(e),()=>{var i;this.events[p]=(i=this.events[p])==null?void 0:i.filter(o=>e!==o)}}});const P=U("LayoutPool");class ve{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)){const i=this.layouts.get(e);Object.assign(i,t),i.lastAccess=Date.now();return}this.layouts.size>=this.maxSize&&this.evictLRU(),t.status="warm",t.lastAccess=Date.now(),this.layouts.set(e,t),P.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)){const t=this.layouts.get(e);t.status="hot",t.lastAccess=Date.now()}this.hotLayoutId=e}evict(e){const t=this.layouts.get(e);if(t){if(P.info(`Evicting layout ${e} from pool`),t.blobUrls&&t.blobUrls.size>0&&(t.blobUrls.forEach(i=>{URL.revokeObjectURL(i)}),P.info(`Revoked ${t.blobUrls.size} blob URLs for layout ${e}`)),t.mediaUrlCache)for(const[i,o]of t.mediaUrlCache)o&&typeof o=="string"&&o.startsWith("blob:")&&URL.revokeObjectURL(o);if(t.regions)for(const[i,o]of t.regions)o.timer&&(clearTimeout(o.timer),o.timer=null);t.container&&t.container.parentNode&&t.container.remove(),this.layouts.delete(e),this.hotLayoutId===e&&(this.hotLayoutId=null)}}evictLRU(){let e=null,t=1/0;for(const[i,o]of this.layouts)o.status==="warm"&&o.lastAccess<t&&(e=i,t=o.lastAccess);e!==null&&this.evict(e)}clearWarm(){let e=0;const t=[];for(const[i,o]of this.layouts)o.status==="warm"&&t.push(i);for(const i of t)this.evict(i),e++;return e>0&&P.info(`Cleared ${e} warm layout(s) from pool`),e}clearWarmNotIn(e){let t=0;const i=[];for(const[o,r]of this.layouts)r.status==="warm"&&!e.has(o)&&i.push(o);for(const o of i)this.evict(o),t++;return t>0&&P.info(`Cleared ${t} warm layout(s) no longer in schedule`),t}clear(){const e=Array.from(this.layouts.keys());for(const t of e)this.evict(t);this.hotLayoutId=null}get size(){return this.layouts.size}}const F={fadeIn(p,e){const t=[{opacity:0},{opacity:1}],i={duration:e,easing:"linear",fill:"forwards"};return p.animate(t,i)},fadeOut(p,e){const t=[{opacity:1},{opacity:0}],i={duration:e,easing:"linear",fill:"forwards"};return p.animate(t,i)},getFlyKeyframes(p,e,t,i){const o={N:{x:0,y:i?-t:t},NE:{x:i?e:-e,y:i?-t:t},E:{x:i?e:-e,y:0},SE:{x:i?e:-e,y:i?t:-t},S:{x:0,y:i?t:-t},SW:{x:i?-e:e,y:i?t:-t},W:{x:i?-e:e,y:0},NW:{x:i?-e:e,y:i?-t:t}},r=o[p]||o.N;return i?{from:{transform:`translate(${r.x}px, ${r.y}px)`,opacity:0},to:{transform:"translate(0, 0)",opacity:1}}:{from:{transform:"translate(0, 0)",opacity:1},to:{transform:`translate(${r.x}px, ${r.y}px)`,opacity:0}}},flyIn(p,e,t,i,o){const r=this.getFlyKeyframes(t,i,o,!0),n={duration:e,easing:"ease-out",fill:"forwards"};return p.animate([r.from,r.to],n)},flyOut(p,e,t,i,o){const r=this.getFlyKeyframes(t,i,o,!1),n={duration:e,easing:"ease-in",fill:"forwards"};return p.animate([r.from,r.to],n)},apply(p,e,t,i,o){if(!e||!e.type)return null;const r=e.type.toLowerCase(),n=e.duration||1e3,s=e.direction||"N";switch(r){case"fadein":return t?this.fadeIn(p,n):null;case"fadeout":return t?null:this.fadeOut(p,n);case"flyin":return t?this.flyIn(p,n,s,i,o):null;case"flyout":return t?null:this.flyOut(p,n,s,i,o);default:return null}}};class be{constructor(e,t,i={}){this.config=e,this.container=t,this.options=i,this.log=U("RendererLite",i.logLevel),this.emitter=we(),this.currentLayout=null,this.currentLayoutId=null,this.regions=new Map,this.layoutTimer=null,this.layoutEndEmitted=!1,this.widgetTimers=new Map,this.mediaUrlCache=new Map,this.layoutBlobUrls=new Map,this.scaleFactor=1,this.offsetX=0,this.offsetY=0,this.overlayContainer=null,this.activeOverlays=new Map,this._keydownHandler=null,this._keyboardActions=[],this.layoutPool=new ve(2),this.preloadTimer=null,this._preloadRetryTimer=null,this.setupContainer(),this.log.info("Initialized")}setupContainer(){this.container.style.position="relative",this.container.style.width="100%",this.container.style.height="100vh",this.container.style.overflow="hidden",typeof ResizeObserver<"u"&&(this.resizeObserver=new ResizeObserver(()=>{this.rescaleRegions()}),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){const t=this.container.clientWidth,i=this.container.clientHeight;if(!t||!i)return;const o=t/e.width,r=i/e.height;this.scaleFactor=Math.min(o,r),this.offsetX=(t-e.width*this.scaleFactor)/2,this.offsetY=(i-e.height*this.scaleFactor)/2,this.log.info(`Scale: ${this.scaleFactor.toFixed(3)} (${e.width}x${e.height} → ${t}x${i}, offset ${Math.round(this.offsetX)},${Math.round(this.offsetY)})`)}applyRegionScale(e,t){const i=this.scaleFactor;e.style.left=`${t.left*i+this.offsetX}px`,e.style.top=`${t.top*i+this.offsetY}px`,e.style.width=`${t.width*i}px`,e.style.height=`${t.height*i}px`}rescaleRegions(){if(this.currentLayout){this.calculateScale(this.currentLayout);for(const[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(const[e,t]of this.activeOverlays){this.calculateScale(t.layout);for(const[i,o]of t.regions)this.applyRegionScale(o.element,o.config),o.width=o.config.width*this.scaleFactor,o.height=o.config.height*this.scaleFactor}}}on(e,t){return this.emitter.on(e,t)}emit(e,...t){this.emitter.emit(e,...t)}parseActions(e){const t=[];for(const i of e.children)i.tagName==="action"&&t.push({actionType:i.getAttribute("actionType")||"",triggerType:i.getAttribute("triggerType")||"",triggerCode:i.getAttribute("triggerCode")||"",layoutCode:i.getAttribute("layoutCode")||"",targetId:i.getAttribute("targetId")||"",commandCode:i.getAttribute("commandCode")||""});return t}parseXlf(e){const i=new DOMParser().parseFromString(e,"text/xml"),o=i.querySelector("layout");if(!o)throw new Error("Invalid XLF: no <layout> element");const r=o.getAttribute("duration"),n={width:parseInt(o.getAttribute("width")||"1920"),height:parseInt(o.getAttribute("height")||"1080"),duration:r?parseInt(r):0,bgcolor:o.getAttribute("bgcolor")||"#000000",background:o.getAttribute("background")||null,regions:[]};r?this.log.info(`Layout duration from XLF: ${n.duration}s`):this.log.info("Layout duration NOT in XLF, will calculate from widgets");for(const s of i.querySelectorAll("region")){const a={id:s.getAttribute("id"),width:parseInt(s.getAttribute("width")),height:parseInt(s.getAttribute("height")),top:parseInt(s.getAttribute("top")),left:parseInt(s.getAttribute("left")),zindex:parseInt(s.getAttribute("zindex")||"0"),actions:this.parseActions(s),widgets:[]};for(const c of s.querySelectorAll("media")){const d=this.parseWidget(c);a.widgets.push(d)}n.regions.push(a)}if(n.duration===0){let s=0;for(const a of n.regions){let c=0;for(const d of a.widgets)if(d.duration>0)c+=d.duration;else{c=60;break}s=Math.max(s,c)}n.duration=s>0?s:60,this.log.info(`Calculated layout duration: ${n.duration}s (not specified in XLF)`)}return n}parseWidget(e){const t=e.getAttribute("type"),i=parseInt(e.getAttribute("duration")||"10"),o=parseInt(e.getAttribute("useDuration")||"1"),r=e.getAttribute("id"),n=e.getAttribute("fileId"),s={},a=e.querySelector("options");if(a)for(const u of a.children)s[u.tagName]=u.textContent;const c=e.querySelector("raw"),d=c?c.textContent:"",g={in:null,out:null};s.transIn&&(g.in={type:s.transIn,duration:parseInt(s.transInDuration||"1000"),direction:s.transInDirection||"N"}),s.transOut&&(g.out={type:s.transOut,duration:parseInt(s.transOutDuration||"1000"),direction:s.transOutDirection||"N"});const f=this.parseActions(e);return{type:t,duration:i,useDuration:o,id:r,fileId:n,options:s,raw:d,transitions:g,actions:f}}trackBlobUrl(e){this.currentLayoutId&&(this.layoutBlobUrls.has(this.currentLayoutId)||this.layoutBlobUrls.set(this.currentLayoutId,new Set),this.layoutBlobUrls.get(this.currentLayoutId).add(e))}revokeBlobUrlsForLayout(e){const t=this.layoutBlobUrls.get(e);t&&(t.forEach(i=>{URL.revokeObjectURL(i)}),this.layoutBlobUrls.delete(e),this.log.info(`Revoked ${t.size} blob URLs for layout ${e}`))}updateLayoutDuration(){if(!this.currentLayout)return;let e=0;for(const t of this.currentLayout.regions){let i=0;for(const o of t.widgets)o.duration>0&&(i+=o.duration);e=Math.max(e,i)}if(e>0&&e!==this.currentLayout.duration){const t=this.currentLayout.duration;if(this.currentLayout.duration=e,this.log.info(`Layout duration updated: ${t}s → ${e}s (based on video metadata)`),this.layoutTimer){clearTimeout(this.layoutTimer);const i=this.currentLayout.duration*1e3;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))},i),this.log.info(`Layout timer reset to ${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){const t=[];let i=0;for(const o of e.regions){const r=this.regions.get(o.id);if(r){for(const n of o.actions||[])n.triggerType==="touch"?(this.attachTouchAction(r.element,n,o.id,null),i++):n.triggerType.startsWith("keyboard:")&&t.push(n);for(const n of o.widgets){if(!n.actions||n.actions.length===0)continue;const s=r.widgetElements.get(n.id);if(s)for(const a of n.actions)a.triggerType==="touch"?(this.attachTouchAction(s,a,o.id,n.id),i++):a.triggerType.startsWith("keyboard:")&&t.push(a)}}}this.setupKeyboardListener(t),(i>0||t.length>0)&&this.log.info(`Actions attached: ${i} touch, ${t.length} keyboard`)}attachTouchAction(e,t,i,o){e.style.cursor="pointer";const r=n=>{n.stopPropagation();const s=o?`widget ${o}`:`region ${i}`;this.log.info(`Touch action fired on ${s}: ${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:i,widgetId:o}})};e.addEventListener("click",r),e._actionHandlers||(e._actionHandlers=[]),e._actionHandlers.push(r)}setupKeyboardListener(e){this.removeKeyboardListener(),this._keyboardActions=e,e.length!==0&&(this._keydownHandler=t=>{const i=t.key;for(const o of this._keyboardActions){const r=o.triggerType.substring(9);if(i===r){this.log.info(`Keyboard action (key: ${i}): ${o.actionType}`),this.emit("action-trigger",{actionType:o.actionType,triggerType:o.triggerType,triggerCode:o.triggerCode,layoutCode:o.layoutCode,targetId:o.targetId,commandCode:o.commandCode,source:{key:i}});break}}},document.addEventListener("keydown",this._keydownHandler))}removeKeyboardListener(){this._keydownHandler&&(document.removeEventListener("keydown",this._keydownHandler),this._keydownHandler=null),this._keyboardActions=[]}removeActionListeners(){for(const[,e]of this.regions){this._cleanElementActionHandlers(e.element);for(const[,t]of e.widgetElements)this._cleanElementActionHandlers(t)}this.removeKeyboardListener()}_cleanElementActionHandlers(e){if(e._actionHandlers){for(const t of e._actionHandlers)e.removeEventListener("click",t);delete e._actionHandlers,e.style.cursor=""}}navigateToWidget(e){for(const[t,i]of this.regions){const o=i.widgets.findIndex(r=>r.id===e);if(o!==-1){if(this.log.info(`Navigating to widget ${e} in region ${t} (index ${o})`),i.timer&&(clearTimeout(i.timer),i.timer=null),this.stopWidget(t,i.currentIndex),i.currentIndex=o,this.renderWidget(t,o),i.widgets.length>1){const n=i.widgets[o].duration*1e3;i.timer=setTimeout(()=>{this.stopWidget(t,o);const s=(o+1)%i.widgets.length;i.currentIndex=s,this.startRegion(t)},n)}return}}this.log.warn(`Target widget ${e} not found in any region`)}nextWidget(e){const t=e?this.regions.get(e):this.regions.values().next().value;if(!t||t.widgets.length<=1)return;const i=(t.currentIndex+1)%t.widgets.length,o=t.widgets[i];this.log.info(`nextWidget → index ${i} (widget ${o.id})`),this.navigateToWidget(o.id)}previousWidget(e){const t=e?this.regions.get(e):this.regions.values().next().value;if(!t||t.widgets.length<=1)return;const i=(t.currentIndex-1+t.widgets.length)%t.widgets.length,o=t.widgets[i];this.log.info(`previousWidget → index ${i} (widget ${o.id})`),this.navigateToWidget(o.id)}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!)`);for(const[r,n]of this.regions)n.timer&&(clearTimeout(n.timer),n.timer=null),n.currentIndex=0;this.layoutTimer&&(clearTimeout(this.layoutTimer),this.layoutTimer=null),this.layoutEndEmitted=!1,this.emit("layoutStart",t,this.currentLayout);for(const[r,n]of this.regions)this.startRegion(r);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();const o=this.parseXlf(e);if(this.currentLayout=o,this.currentLayoutId=t,this.calculateScale(o),this.container.style.backgroundColor=o.bgcolor,this.container.style.backgroundImage="",o.background&&this.options.getMediaUrl)try{const r=await this.options.getMediaUrl(parseInt(o.background));r&&(this.container.style.backgroundImage=`url(${r})`,this.container.style.backgroundSize="cover",this.container.style.backgroundPosition="center",this.container.style.backgroundRepeat="no-repeat",this.log.info(`Background image set: ${o.background}`))}catch(r){this.log.warn("Failed to load background image:",r)}if(this.options.getMediaUrl){const r=[];this.mediaUrlCache.clear();for(const n of o.regions)for(const s of n.widgets)if(s.fileId){const a=parseInt(s.fileId||s.id);this.mediaUrlCache.has(a)||r.push(this.options.getMediaUrl(a).then(c=>{this.mediaUrlCache.set(a,c)}).catch(c=>{this.log.warn(`Failed to fetch media ${a}:`,c)}))}r.length>0&&(this.log.info(`Pre-fetching ${r.length} media URLs in parallel...`),await Promise.all(r),this.log.info("All media URLs pre-fetched"))}for(const r of o.regions)await this.createRegion(r);this.log.info("Pre-creating widget elements for instant transitions...");for(const[r,n]of this.regions)for(let s=0;s<n.widgets.length;s++){const a=n.widgets[s];a.layoutId=this.currentLayoutId,a.regionId=r;try{const c=await this.createWidgetElement(a,n);c.style.visibility="hidden",c.style.opacity="0",n.element.appendChild(c),n.widgetElements.set(a.id,c)}catch(c){this.log.error(`Failed to pre-create widget ${a.id}:`,c)}}this.log.info("All widget elements pre-created"),this.attachActionListeners(o),this.emit("layoutStart",t,o);for(const[r,n]of this.regions)this.startRegion(r);this.startLayoutTimerWhenReady(t,o),this._scheduleNextLayoutPreload(o),this.log.info(`Layout ${t} started`)}catch(i){throw this.log.error("Error rendering layout:",i),this.emit("error",{type:"layoutError",error:i,layoutId:t}),i}}async createRegion(e){const t=document.createElement("div");t.id=`region_${e.id}`,t.className="renderer-lite-region",t.style.position="absolute",t.style.zIndex=e.zindex,t.style.overflow="hidden",this.applyRegionScale(t,e),this.container.appendChild(t);const i=this.scaleFactor;this.regions.set(e.id,{element:t,config:e,widgets:e.widgets,currentIndex:0,timer:null,width:e.width*i,height:e.height*i,complete:!1,widgetElements:new Map})}startRegion(e){const t=this.regions.get(e);if(!t||t.widgets.length===0)return;if(t.widgets.length===1){this.renderWidget(e,0);return}const i=()=>{const o=t.currentIndex,r=t.widgets[o];this.renderWidget(e,o);const n=r.duration*1e3;t.timer=setTimeout(()=>{this.stopWidget(e,o);const s=(t.currentIndex+1)%t.widgets.length;s===0&&!t.complete&&(t.complete=!0,this.log.info(`Region ${e} completed one full cycle`),this.checkLayoutComplete()),t.currentIndex=s,i()},n)};i()}async createWidgetElement(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);default:return await this.renderGenericWidget(e,t)}}findMediaElement(e,t){return e.tagName===t?e:e.querySelector(t.toLowerCase())}updateMediaElement(e,t){const i=this.findMediaElement(e,"VIDEO");if(i){i.currentTime=0;const r=()=>{i.removeEventListener("seeked",r),i.play().catch(()=>{})};i.addEventListener("seeked",r),i.currentTime===0&&i.readyState>=2&&(i.removeEventListener("seeked",r),i.play().catch(()=>{})),this.log.info(`Video restarted: ${t.fileId||t.id}`);return}const o=this.findMediaElement(e,"AUDIO");if(o){o.currentTime=0;const r=()=>{o.removeEventListener("seeked",r),o.play().catch(()=>{})};o.addEventListener("seeked",r),o.currentTime===0&&o.readyState>=2&&(o.removeEventListener("seeked",r),o.play().catch(()=>{})),this.log.info(`Audio restarted: ${t.fileId||t.id}`);return}}waitForWidgetReady(e,t){const o=this.findMediaElement(e,"VIDEO");if(o)return!o.paused&&o.readyState>=3?Promise.resolve():new Promise(n=>{const s=setTimeout(()=>{this.log.warn(`Video ready timeout (10000ms) for widget ${t.id}`),n()},1e4),a=()=>{o.removeEventListener("playing",a),clearTimeout(s),this.log.info(`Video widget ${t.id} ready (playing)`),n()};o.addEventListener("playing",a)});const r=this.findMediaElement(e,"IMG");return r?r.complete&&r.naturalWidth>0?Promise.resolve():new Promise(n=>{const s=setTimeout(()=>{this.log.warn(`Image ready timeout for widget ${t.id}`),n()},1e4),a=()=>{r.removeEventListener("load",a),clearTimeout(s),n()};r.addEventListener("load",a)}):Promise.resolve()}async startLayoutTimerWhenReady(e,t){if(!t||t.duration<=0)return;const i=[];for(const[r,n]of this.regions){if(n.widgets.length===0)continue;const s=n.widgets[n.currentIndex||0],a=n.widgetElements.get(s.id);a&&i.push(this.waitForWidgetReady(a,s))}if(i.length>0&&(this.log.info(`Waiting for ${i.length} widget(s) to be ready before starting layout timer...`),await Promise.all(i),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}const o=t.duration*1e3;this.log.info(`Layout ${e} will end after ${t.duration}s`),this.layoutTimer=setTimeout(()=>{this.log.info(`Layout ${e} duration expired (${t.duration}s)`),this.currentLayoutId&&(this.layoutEndEmitted=!0,this.emit("layoutEnd",this.currentLayoutId))},o)}async renderWidget(e,t){const i=this.regions.get(e);if(!i)return;const o=i.widgets[t];if(o)try{this.log.info(`Showing widget ${o.type} (${o.id}) in region ${e}`);let r=i.widgetElements.get(o.id);r||(this.log.warn(`Widget ${o.id} not pre-created, creating now`),o.layoutId=this.currentLayoutId,o.regionId=e,r=await this.createWidgetElement(o,i),i.widgetElements.set(o.id,r),i.element.appendChild(r));for(const[n,s]of i.widgetElements)n!==o.id&&(s.style.visibility="hidden",s.style.opacity="0");this.updateMediaElement(r,o),r.style.visibility="visible",o.transitions.in?F.apply(r,o.transitions.in,!0,i.width,i.height):r.style.opacity="1",this.emit("widgetStart",{widgetId:o.id,regionId:e,layoutId:this.currentLayoutId,mediaId:parseInt(o.fileId||o.id)||null,type:o.type,duration:o.duration})}catch(r){this.log.error("Error rendering widget:",r),this.emit("error",{type:"widgetError",error:r,widgetId:o.id,regionId:e})}}async stopWidget(e,t){const i=this.regions.get(e);if(!i)return;const o=i.widgets[t];if(!o)return;const r=i.widgetElements.get(o.id);if(!r)return;if(o.transitions.out){const a=F.apply(r,o.transitions.out,!1,i.width,i.height);a&&await new Promise(c=>{a.onfinish=c})}const n=r.querySelector("video");n&&o.options.loop!=="1"&&n.pause();const s=r.querySelector("audio");s&&o.options.loop!=="1"&&s.pause(),this.emit("widgetEnd",{widgetId:o.id,regionId:e,layoutId:this.currentLayoutId,mediaId:parseInt(o.fileId||o.id)||null,type:o.type})}async renderImage(e,t){const i=document.createElement("img");i.className="renderer-lite-widget",i.style.width="100%",i.style.height="100%",i.style.objectFit="contain",i.style.opacity="0";const o=parseInt(e.fileId||e.id);let r=this.mediaUrlCache.get(o);return!r&&this.options.getMediaUrl?r=await this.options.getMediaUrl(o):r||(r=`${window.location.origin}/player/cache/media/${e.options.uri}`),i.src=r,i}async renderVideo(e,t){const i=document.createElement("video");i.className="renderer-lite-widget",i.style.width="100%",i.style.height="100%",i.style.objectFit="contain",i.style.opacity="1",i.autoplay=!0,i.preload="auto",i.muted=e.options.mute==="1",i.loop=!1,i.controls=se(),i.playsInline=!0,i.addEventListener("ended",()=>{e.options.loop==="1"?(i.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`)});const o=parseInt(e.fileId||e.id);let r=this.mediaUrlCache.get(o);if(!r&&this.options.getMediaUrl?r=await this.options.getMediaUrl(o):r||(r=`${window.location.origin}/player/cache/media/${o}`),r.includes(".m3u8"))if(i.canPlayType("application/vnd.apple.mpegurl"))this.log.info(`HLS stream (native): ${o}`),i.src=r;else try{const{default:s}=await I(async()=>{const{default:a}=await import("hls.js");return{default:a}},[],import.meta.url);if(s.isSupported()){const a=new s({enableWorker:!0,lowLatencyMode:!0});a.loadSource(r),a.attachMedia(i),a.on(s.Events.ERROR,(c,d)=>{d.fatal&&(this.log.error(`HLS fatal error: ${d.type}`,d.details),a.destroy())}),this.log.info(`HLS stream (hls.js): ${o}`)}else this.log.warn(`HLS not supported on this browser for ${o}`),i.src=r}catch(s){this.log.warn(`hls.js not available, falling back to native: ${s.message}`),i.src=r}else i.src=r;return i.addEventListener("loadedmetadata",()=>{const s=Math.floor(i.duration);this.log.info(`Video ${o} duration detected: ${s}s`),(e.duration===0||e.useDuration===0)&&(e.duration=s,this.log.info(`Updated widget ${e.id} duration to ${s}s (useDuration=0)`),this.updateLayoutDuration())}),i.addEventListener("loadeddata",()=>{this.log.info("Video loaded and ready:",o)}),i.addEventListener("error",s=>{const a=i.error,c=a==null?void 0:a.code,d=(a==null?void 0:a.message)||"Unknown error";this.log.warn(`Video error (non-fatal, logged only): ${o}, code: ${c}, time: ${i.currentTime.toFixed(1)}s, message: ${d}`)}),i.addEventListener("playing",()=>{this.log.info("Video playing:",o)}),this.log.info("Video element created:",o,i.src),i}async renderAudio(e,t){const i=document.createElement("div");i.className="renderer-lite-widget audio-widget",i.style.width="100%",i.style.height="100%",i.style.display="flex",i.style.flexDirection="column",i.style.alignItems="center",i.style.justifyContent="center",i.style.background="linear-gradient(135deg, #667eea 0%, #764ba2 100%)",i.style.opacity="0";const o=document.createElement("audio");o.autoplay=!0,o.loop=e.options.loop==="1",o.volume=parseFloat(e.options.volume||"100")/100;const r=parseInt(e.fileId||e.id);let n=this.mediaUrlCache.get(r);!n&&this.options.getMediaUrl?n=await this.options.getMediaUrl(r):n||(n=`${window.location.origin}/player/cache/media/${r}`),o.src=n;const s=document.createElement("div");s.innerHTML="♪",s.style.fontSize="120px",s.style.color="white",s.style.marginBottom="20px";const a=document.createElement("div");a.style.color="white",a.style.fontSize="24px",a.textContent="Playing Audio";const c=document.createElement("div");return c.style.color="rgba(255,255,255,0.7)",c.style.fontSize="16px",c.style.marginTop="10px",c.textContent=e.options.uri,i.appendChild(o),i.appendChild(s),i.appendChild(a),i.appendChild(c),i}async renderTextWidget(e,t){const i=document.createElement("iframe");i.className="renderer-lite-widget",i.style.width="100%",i.style.height="100%",i.style.border="none",i.style.opacity="0";let o=e.raw;if(this.options.getWidgetHtml){const s=await this.options.getWidgetHtml(e);if(s&&typeof s=="object"&&s.url){if(i.src=s.url,s.fallback){const a=this;i.addEventListener("load",function(){var c;try{if(!((c=i.contentDocument)!=null&&c.querySelector("base"))){console.warn("[RendererLite] Cache URL failed (hard reload?), using original CMS URLs");const d=new Blob([s.fallback],{type:"text/html"}),g=URL.createObjectURL(d);a.trackBlobUrl(g),i.src=g}}catch{}},{once:!0})}return i}o=s}const r=new Blob([o],{type:"text/html"}),n=URL.createObjectURL(r);return i.src=n,this.trackBlobUrl(n),i}async renderPdf(e,t){const i=document.createElement("div");if(i.className="renderer-lite-widget pdf-widget",i.style.width="100%",i.style.height="100%",i.style.backgroundColor="#525659",i.style.opacity="0",i.style.position="relative",typeof window.pdfjsLib>"u")try{const n=await I(()=>import("./pdf-BnPRJEQ6.js"),[],import.meta.url);window.pdfjsLib=n,window.pdfjsLib.GlobalWorkerOptions.workerSrc=`${window.location.origin}/player/pdf.worker.min.mjs`}catch(n){return this.log.error("PDF.js not available:",n),i.innerHTML='<div style="color:white;padding:20px;text-align:center;">PDF viewer unavailable</div>',i.style.opacity="1",i}const o=parseInt(e.fileId||e.id);let r=this.mediaUrlCache.get(o);!r&&this.options.getMediaUrl?r=await this.options.getMediaUrl(o):r||(r=`${window.location.origin}/player/cache/media/${e.options.uri}`);try{const a=await(await window.pdfjsLib.getDocument(r).promise).getPage(1),c=a.getViewport({scale:1}),d=Math.min(t.width/c.width,t.height/c.height),g=a.getViewport({scale:d}),f=document.createElement("canvas");f.width=g.width,f.height=g.height,f.style.display="block",f.style.margin="auto";const u=f.getContext("2d");await a.render({canvasContext:u,viewport:g}).promise,i.appendChild(f)}catch(n){this.log.error("PDF render failed:",n),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){const i=document.createElement("iframe");return i.className="renderer-lite-widget",i.style.width="100%",i.style.height="100%",i.style.border="none",i.style.opacity="0",i.src=e.options.uri,i}async renderGenericWidget(e,t){const i=document.createElement("iframe");i.className="renderer-lite-widget",i.style.width="100%",i.style.height="100%",i.style.border="none",i.style.opacity="0";let o=e.raw;if(this.options.getWidgetHtml){const r=await this.options.getWidgetHtml(e);if(r&&typeof r=="object"&&r.url){if(i.src=r.url,r.fallback){const n=this;i.addEventListener("load",function(){var s;try{if(!((s=i.contentDocument)!=null&&s.querySelector("base"))){console.warn("[RendererLite] Cache URL failed (hard reload?), using original CMS URLs");const a=new Blob([r.fallback],{type:"text/html"}),c=URL.createObjectURL(a);n.trackBlobUrl(c),i.src=c}}catch{}},{once:!0})}return i}o=r}if(o){const r=new Blob([o],{type:"text/html"}),n=URL.createObjectURL(r);i.src=n,this.trackBlobUrl(n)}else this.log.warn(`No HTML for widget ${e.id}`),i.srcdoc='<div style="padding:20px;">Widget content unavailable</div>';return i}_scheduleNextLayoutPreload(e){this.preloadTimer&&(clearTimeout(this.preloadTimer),this.preloadTimer=null),this._preloadRetryTimer&&(clearTimeout(this._preloadRetryTimer),this._preloadRetryTimer=null);const t=e.duration||60,i=t*1e3*.75,o=t*1e3*.9;this.log.info(`Scheduling next layout preload in ${(i/1e3).toFixed(1)}s (75% of ${t}s)`),this.preloadTimer=setTimeout(()=>{this.preloadTimer=null,this.emit("request-next-layout-preload")},i),this._preloadRetryTimer=setTimeout(()=>{this._preloadRetryTimer=null,this.emit("request-next-layout-preload")},o)}async preloadLayout(e,t){if(this.layoutPool.has(t))return this.log.info(`Layout ${t} already in preload pool, skipping`),!0;if(this.currentLayoutId===t)return this.log.info(`Layout ${t} is current, skipping preload`),!0;try{this.log.info(`Preloading layout ${t} into pool...`);const i=this.parseXlf(e);this.calculateScale(i);const o=document.createElement("div");if(o.id=`preload_layout_${t}`,o.className="renderer-lite-preload-wrapper",o.style.position="absolute",o.style.top="0",o.style.left="0",o.style.width="100%",o.style.height="100%",o.style.visibility="hidden",o.style.zIndex="-1",o.style.backgroundColor=i.bgcolor,i.background&&this.options.getMediaUrl)try{const u=await this.options.getMediaUrl(parseInt(i.background));u&&(o.style.backgroundImage=`url(${u})`,o.style.backgroundSize="cover",o.style.backgroundPosition="center",o.style.backgroundRepeat="no-repeat")}catch(u){this.log.warn("Preload: Failed to load background image:",u)}const r=new Map;if(this.options.getMediaUrl){const u=[];for(const y of i.regions)for(const m of y.widgets)if(m.fileId){const L=parseInt(m.fileId||m.id);r.has(L)||u.push(this.options.getMediaUrl(L).then(w=>{r.set(L,w)}).catch(w=>{this.log.warn(`Preload: Failed to fetch media ${L}:`,w)}))}u.length>0&&(this.log.info(`Preload: fetching ${u.length} media URLs...`),await Promise.all(u))}const n=this.mediaUrlCache,s=this.currentLayoutId;this.mediaUrlCache=r;const a=new Map,c=this.scaleFactor;for(const u of i.regions){const y=document.createElement("div");y.id=`preload_region_${t}_${u.id}`,y.className="renderer-lite-region",y.style.position="absolute",y.style.zIndex=u.zindex,y.style.overflow="hidden",this.applyRegionScale(y,u),o.appendChild(y);const m={element:y,config:u,widgets:u.widgets,currentIndex:0,timer:null,width:u.width*c,height:u.height*c,complete:!1,widgetElements:new Map};a.set(u.id,m)}const d=new Set,g=this.layoutBlobUrls;this.layoutBlobUrls=new Map,this.layoutBlobUrls.set(t,d),this.currentLayoutId=t;for(const[u,y]of a)for(let m=0;m<y.widgets.length;m++){const L=y.widgets[m];L.layoutId=t,L.regionId=u;try{const w=await this.createWidgetElement(L,y);w.style.visibility="hidden",w.style.opacity="0",y.element.appendChild(w),y.widgetElements.set(L.id,w)}catch(w){this.log.error(`Preload: Failed to create widget ${L.id}:`,w)}}return this.mediaUrlCache=n,this.currentLayoutId=s,o.querySelectorAll("video").forEach(u=>u.pause()),(this.layoutBlobUrls.get(t)||new Set).forEach(u=>d.add(u)),this.layoutBlobUrls=g,this.container.appendChild(o),this.layoutPool.add(t,{container:o,layout:i,regions:a,blobUrls:d,mediaUrlCache:r}),this.log.info(`Layout ${t} preloaded into pool (${a.size} regions, ${r.size} media)`),!0}catch(i){return this.log.error(`Preload failed for layout ${t}:`,i),!1}}async _swapToPreloadedLayout(e){const t=this.layoutPool.get(e);if(!t){this.log.error(`Cannot swap: layout ${e} not in pool`);return}this.removeActionListeners(),this.layoutTimer&&(clearTimeout(this.layoutTimer),this.layoutTimer=null),this.preloadTimer&&(clearTimeout(this.preloadTimer),this.preloadTimer=null),this._preloadRetryTimer&&(clearTimeout(this._preloadRetryTimer),this._preloadRetryTimer=null);const i=this.currentLayoutId;if(i&&this.layoutPool.has(i))this.layoutPool.evict(i);else{for(const[o,r]of this.regions)r.timer&&(clearTimeout(r.timer),r.timer=null),r.element.querySelectorAll("video").forEach(n=>{n.pause(),n.removeAttribute("src"),n.load()}),r.element.remove();i&&this.revokeBlobUrlsForLayout(i);for(const[o,r]of this.mediaUrlCache)r&&typeof r=="string"&&r.startsWith("blob:")&&URL.revokeObjectURL(r)}i&&!this.layoutEndEmitted&&this.emit("layoutEnd",i),this.regions.clear(),this.mediaUrlCache.clear(),t.container.style.visibility="visible",t.container.style.zIndex="0",this.layoutPool.setHot(e),this.currentLayout=t.layout,this.currentLayoutId=e,this.regions=t.regions,this.mediaUrlCache=t.mediaUrlCache||new Map,this.layoutEndEmitted=!1,this.container.style.backgroundColor=t.layout.bgcolor,t.container.style.backgroundImage?(this.container.style.backgroundImage=t.container.style.backgroundImage,this.container.style.backgroundSize=t.container.style.backgroundSize,this.container.style.backgroundPosition=t.container.style.backgroundPosition,this.container.style.backgroundRepeat=t.container.style.backgroundRepeat):this.container.style.backgroundImage="",this.calculateScale(t.layout),this.attachActionListeners(t.layout),this.emit("layoutStart",e,t.layout);for(const[o,r]of this.regions)r.currentIndex=0,r.complete=!1,this.startRegion(o);this.updateLayoutDuration(),this.startLayoutTimerWhenReady(e,t.layout),this._scheduleNextLayoutPreload(t.layout),this.log.info(`Swapped to preloaded layout ${e} (instant transition)`)}checkLayoutComplete(){let e=!0;for(const[t,i]of this.regions)if(i.widgets.length>1&&!i.complete){e=!1;break}e&&this.currentLayoutId&&this.log.info("All multi-widget regions completed one cycle")}stopCurrentLayout(){if(this.currentLayout){if(this.log.info(`Stopping layout ${this.currentLayoutId}`),this.removeActionListeners(),this.layoutTimer&&(clearTimeout(this.layoutTimer),this.layoutTimer=null),this.preloadTimer&&(clearTimeout(this.preloadTimer),this.preloadTimer=null),this._preloadRetryTimer&&(clearTimeout(this._preloadRetryTimer),this._preloadRetryTimer=null),this.currentLayoutId&&this.layoutPool.has(this.currentLayoutId))this.layoutPool.evict(this.currentLayoutId);else{this.currentLayoutId&&this.revokeBlobUrlsForLayout(this.currentLayoutId);for(const[e,t]of this.regions)t.timer&&(clearTimeout(t.timer),t.timer=null),t.widgets.length>0&&this.stopWidget(e,t.currentIndex),t.element.remove();for(const[e,t]of this.mediaUrlCache)t&&t.startsWith("blob:")&&URL.revokeObjectURL(t)}this.regions.clear(),this.mediaUrlCache.clear(),this.currentLayoutId&&!this.layoutEndEmitted&&this.emit("layoutEnd",this.currentLayoutId),this.layoutEndEmitted=!1,this.currentLayout=null,this.currentLayoutId=null}}async renderOverlay(e,t,i=0){try{if(this.log.info(`Rendering overlay ${t} (priority ${i})`),this.activeOverlays.has(t)){this.log.warn(`Overlay ${t} already active, skipping`);return}const o=this.parseXlf(e),r=document.createElement("div");if(r.id=`overlay_${t}`,r.className="renderer-lite-overlay",r.style.position="absolute",r.style.top="0",r.style.left="0",r.style.width="100%",r.style.height="100%",r.style.zIndex=String(1e3+i),r.style.pointerEvents="auto",r.style.backgroundColor=o.bgcolor,this.options.getMediaUrl){const a=[];for(const c of o.regions)for(const d of c.widgets)if(d.fileId){const g=parseInt(d.fileId||d.id);this.mediaUrlCache.has(g)||a.push(this.options.getMediaUrl(g).then(f=>{this.mediaUrlCache.set(g,f)}).catch(f=>{this.log.warn(`Failed to fetch overlay media ${g}:`,f)}))}a.length>0&&(this.log.info(`Pre-fetching ${a.length} overlay media URLs...`),await Promise.all(a))}this.calculateScale(o);const n=new Map,s=this.scaleFactor;for(const a of o.regions){const c=document.createElement("div");c.id=`overlay_${t}_region_${a.id}`,c.className="renderer-lite-region overlay-region",c.style.position="absolute",c.style.zIndex=String(a.zindex),c.style.overflow="hidden",this.applyRegionScale(c,a),r.appendChild(c),n.set(a.id,{element:c,config:a,widgets:a.widgets,currentIndex:0,timer:null,width:a.width*s,height:a.height*s,complete:!1,widgetElements:new Map})}for(const[a,c]of n)for(const d of c.widgets){d.layoutId=t,d.regionId=a;try{const g=await this.createWidgetElement(d,c);g.style.visibility="hidden",g.style.opacity="0",c.element.appendChild(g),c.widgetElements.set(d.id,g)}catch(g){this.log.error(`Failed to pre-create overlay widget ${d.id}:`,g)}}this.overlayContainer.appendChild(r),this.activeOverlays.set(t,{container:r,layout:o,regions:n,timer:null,priority:i}),this.emit("overlayStart",t,o);for(const[a,c]of n)this.startOverlayRegion(t,a);if(o.duration>0){const a=o.duration*1e3,c=this.activeOverlays.get(t);c&&(c.timer=setTimeout(()=>{this.log.info(`Overlay ${t} duration expired (${o.duration}s)`),this.emit("overlayEnd",t)},a))}this.log.info(`Overlay ${t} started`)}catch(o){throw this.log.error("Error rendering overlay:",o),this.emit("error",{type:"overlayError",error:o,layoutId:t}),o}}startOverlayRegion(e,t){const i=this.activeOverlays.get(e);if(!i)return;const o=i.regions.get(t);if(!o||o.widgets.length===0)return;if(o.widgets.length===1){this.renderOverlayWidget(e,t,0);return}const r=()=>{const n=o.currentIndex,s=o.widgets[n];this.renderOverlayWidget(e,t,n);const a=s.duration*1e3;o.timer=setTimeout(()=>{this.stopOverlayWidget(e,t,n);const c=(o.currentIndex+1)%o.widgets.length;c===0&&!o.complete&&(o.complete=!0,this.log.info(`Overlay ${e} region ${t} completed one full cycle`)),o.currentIndex=c,r()},a)};r()}async renderOverlayWidget(e,t,i){const o=this.activeOverlays.get(e);if(!o)return;const r=o.regions.get(t);if(!r)return;const n=r.widgets[i];if(n)try{this.log.info(`Showing overlay widget ${n.type} (${n.id}) in overlay ${e} region ${t}`);let s=r.widgetElements.get(n.id);s||(this.log.warn(`Overlay widget ${n.id} not pre-created, creating now`),s=await this.createWidgetElement(n,r),r.widgetElements.set(n.id,s),r.element.appendChild(s));for(const[a,c]of r.widgetElements)a!==n.id&&(c.style.visibility="hidden",c.style.opacity="0");this.updateMediaElement(s,n),s.style.visibility="visible",n.transitions.in?F.apply(s,n.transitions.in,!0,r.width,r.height):s.style.opacity="1",this.emit("overlayWidgetStart",{overlayId:e,widgetId:n.id,regionId:t,type:n.type,duration:n.duration})}catch(s){this.log.error("Error rendering overlay widget:",s),this.emit("error",{type:"overlayWidgetError",error:s,widgetId:n.id,regionId:t,overlayId:e})}}async stopOverlayWidget(e,t,i){const o=this.activeOverlays.get(e);if(!o)return;const r=o.regions.get(t);if(!r)return;const n=r.widgets[i];if(!n)return;const s=r.widgetElements.get(n.id);if(!s)return;if(n.transitions.out){const d=F.apply(s,n.transitions.out,!1,r.width,r.height);d&&await new Promise(g=>{d.onfinish=g})}const a=s.querySelector("video");a&&n.options.loop!=="1"&&a.pause();const c=s.querySelector("audio");c&&n.options.loop!=="1"&&c.pause(),this.emit("overlayWidgetEnd",{overlayId:e,widgetId:n.id,regionId:t,type:n.type})}stopOverlay(e){const 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),t.timer=null);for(const[i,o]of t.regions)o.timer&&(clearTimeout(o.timer),o.timer=null),o.widgets.length>0&&this.stopOverlayWidget(e,i,o.currentIndex);t.container&&t.container.remove(),this.revokeBlobUrlsForLayout(e),this.activeOverlays.delete(e),this.emit("overlayEnd",e),this.log.info(`Overlay ${e} stopped`)}stopAllOverlays(){const e=Array.from(this.activeOverlays.keys());for(const t of e)this.stopOverlay(t);this.log.info("All overlays stopped")}getActiveOverlays(){return Array.from(this.activeOverlays.keys())}cleanup(){this.stopAllOverlays(),this.stopCurrentLayout(),this.layoutPool.clear(),this.preloadTimer&&(clearTimeout(this.preloadTimer),this.preloadTimer=null),this._preloadRetryTimer&&(clearTimeout(this._preloadRetryTimer),this._preloadRetryTimer=null),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.container.innerHTML="",this.log.info("Cleaned up")}}const S=U("DataConnector");class Le extends ae{constructor(){super(),this.connectors=new Map}setConnectors(e){if(this.stopPolling(),this.connectors.clear(),!e||e.length===0){S.debug("No data connectors configured");return}for(const t of e){if(!t.dataKey||!t.url){S.warn("Skipping data connector with missing dataKey or url:",t);continue}this.connectors.set(t.dataKey,{config:t,data:null,timer:null,lastFetch:null}),S.info(`Registered data connector: ${t.dataKey} (interval: ${t.updateInterval}s)`)}S.info(`${this.connectors.size} data connector(s) configured`)}startPolling(){for(const[e,t]of this.connectors.entries()){const{config:i}=t,o=(i.updateInterval||300)*1e3;this.fetchData(t).catch(r=>{S.error(`Initial fetch failed for ${e}:`,r)}),t.timer=setInterval(()=>{this.fetchData(t).catch(r=>{S.error(`Polling fetch failed for ${e}:`,r)})},o),S.debug(`Started polling for ${e} every ${i.updateInterval}s`)}}stopPolling(){for(const[e,t]of this.connectors.entries())t.timer&&(clearInterval(t.timer),t.timer=null,S.debug(`Stopped polling for ${e}`))}getData(e){const t=this.connectors.get(e);return t?t.data:(S.debug(`No data connector found for key: ${e}`),null)}getAvailableKeys(){const e=[];for(const[t,i]of this.connectors.entries())i.data!==null&&e.push(t);return e}async fetchData(e){const{config:t}=e,{dataKey:i,url:o}=t;S.debug(`Fetching data for ${i}: ${o}`);try{const r=await ue(o,{method:"GET",headers:{Accept:"application/json"}},{maxRetries:2,baseDelayMs:2e3});if(!r.ok){S.warn(`Data connector ${i} returned ${r.status}: ${r.statusText}`);return}const n=r.headers.get("Content-Type")||"";let s;n.includes("application/json")?s=await r.json():s=await r.text();const a=e.data;e.data=s,e.lastFetch=Date.now(),S.debug(`Data updated for ${i} (fetched at ${new Date(e.lastFetch).toISOString()})`),this.emit("data-updated",i,s),JSON.stringify(a)!==JSON.stringify(s)&&this.emit("data-changed",i,s)}catch(r){S.error(`Failed to fetch data for ${i}:`,r),this.emit("fetch-error",i,r)}}cleanup(){this.stopPolling(),this.connectors.clear(),this.removeAllListeners(),S.debug("DataConnectorManager cleaned up")}}const h=U("PlayerCore"),Se="xibo-offline-cache",xe=1,T="cache";function G(){return new Promise((p,e)=>{const t=indexedDB.open(Se,xe);t.onupgradeneeded=()=>{const i=t.result;i.objectStoreNames.contains(T)||i.createObjectStore(T)},t.onsuccess=()=>p(t.result),t.onerror=()=>e(t.error)})}class Ce extends ae{constructor(e){super(),this.config=e.config,this.xmds=e.xmds,this.cache=e.cache,this.schedule=e.schedule,this.renderer=e.renderer,this.XmrWrapper=e.xmrWrapper,this.statsCollector=e.statsCollector,this.displaySettings=e.displaySettings,this.dataConnectorManager=new Le,this.xmr=null,this.currentLayoutId=null,this.collecting=!1,this.collectionInterval=null,this.pendingLayouts=new Map,this.offlineMode=!1,this._lastCheckRf=null,this._lastCheckSchedule=null,this._layoutOverride=null,this._lastRequiredFiles=[],this._currentLayoutIndex=0,this.syncConfig=null,this.syncManager=null,this._offlineCache={schedule:null,settings:null,requiredFiles:null},this._offlineDbReady=this._initOfflineCache()}async _initOfflineCache(){try{const e=await G(),i=e.transaction(T,"readonly").objectStore(T),[o,r,n]=await Promise.all([new Promise(s=>{const a=i.get("schedule");a.onsuccess=()=>s(a.result??null),a.onerror=()=>s(null)}),new Promise(s=>{const a=i.get("settings");a.onsuccess=()=>s(a.result??null),a.onerror=()=>s(null)}),new Promise(s=>{const a=i.get("requiredFiles");a.onsuccess=()=>s(a.result??null),a.onerror=()=>s(null)})]);this._offlineCache={schedule:o,settings:r,requiredFiles:n},e.close(),console.log("[PlayerCore] Offline cache loaded from IndexedDB",o?"(has schedule)":"(empty)")}catch(e){console.warn("[PlayerCore] Failed to load offline cache from IndexedDB:",e)}}async _offlineSave(e,t){this._offlineCache[e]=t;try{const i=await G(),o=i.transaction(T,"readwrite");o.objectStore(T).put(t,e),await new Promise((r,n)=>{o.oncomplete=r,o.onerror=()=>n(o.error)}),i.close()}catch(i){console.warn("[PlayerCore] Failed to save offline cache:",e,i)}}hasCachedData(){return this._offlineCache.schedule!==null}isOffline(){return typeof navigator<"u"&&navigator.onLine===!1}isInOfflineMode(){return this.offlineMode}collectOffline(){if(console.warn("[PlayerCore] Offline mode — using cached schedule"),this.offlineMode||(this.offlineMode=!0,this.emit("offline-mode",!0)),!this.collectionInterval){const i=this._offlineCache.settings;i!=null&&i.settings&&this.setupCollectionInterval(i.settings)}const e=this._offlineCache.schedule;e&&(this.schedule.setSchedule(e),this.emit("schedule-received",e));const t=this.schedule.getCurrentLayouts();if(h.info("Offline layouts:",t),this.emit("layouts-scheduled",t),t.length>0)if(this.currentLayoutId)if(t.some(o=>parseInt(String(o).replace(".xlf",""),10)===this.currentLayoutId)){const o=t.findIndex(r=>parseInt(String(r).replace(".xlf",""),10)===this.currentLayoutId);o>=0&&(this._currentLayoutIndex=o),h.debug(`Layout ${this.currentLayoutId} still in schedule (offline), continuing playback`),this.emit("layout-already-playing",this.currentLayoutId)}else{this._currentLayoutIndex=0;const o=this.getNextLayout();o&&(h.info(`Offline: switching to layout ${o.layoutId}`),this.emit("layout-prepare-request",o.layoutId))}else{this._currentLayoutIndex=0;const i=this.getNextLayout();i&&(h.info(`Offline: switching to layout ${i.layoutId}`),this.emit("layout-prepare-request",i.layoutId))}else h.info("Offline: no layouts in cached schedule"),this.emit("no-layouts-scheduled");this.emit("collection-complete")}async collectNow(){return this._lastCheckRf=null,this._lastCheckSchedule=null,this.collect()}async collect(){var e,t,i;if(this.collecting){h.debug("Collection already in progress, skipping");return}this.collecting=!0;try{if(await this._offlineDbReady,h.info("Starting collection cycle..."),this.emit("collection-start"),this.isOffline()){if(this.hasCachedData())return this.collectOffline();throw new Error("Offline with no cached data — cannot start playback")}h.debug("Collection step: registerDisplay");const o=await this.xmds.registerDisplay();if(h.info("Display registered:",o),this._offlineSave("settings",o),this.offlineMode&&(this.offlineMode=!1,console.log("[PlayerCore] Back online — resuming normal collection"),this.emit("offline-mode",!1)),this.displaySettings&&o.settings){const a=this.displaySettings.applySettings(o.settings);a.changed.includes("collectInterval")&&this.updateCollectionInterval(a.settings.collectInterval),o.settings.logLevel&&ge(o.settings.logLevel)&&(h.info("Log level updated from CMS:",o.settings.logLevel),this.emit("log-level-changed",o.settings.logLevel))}o.syncConfig&&(this.syncConfig=o.syncConfig,h.info("Sync group:",o.syncConfig.isLead?"LEAD":`follower → ${o.syncConfig.syncGroup}`,`(switchDelay: ${o.syncConfig.syncSwitchDelay}ms, videoPauseDelay: ${o.syncConfig.syncVideoPauseDelay}ms)`),this.emit("sync-config",o.syncConfig)),this.emit("register-complete",o),h.debug("Collection step: initializeXmr"),await this.initializeXmr(o);const r=o.checkRf||"",n=o.checkSchedule||"";if(!this._lastCheckRf||this._lastCheckRf!==r){h.debug("Collection step: requiredFiles");const a=await this.xmds.requiredFiles(),c=a.filter(u=>u.type==="purge"),d=a.filter(u=>u.type!=="purge");if(h.info("Required files:",d.length,c.length>0?`(+ ${c.length} purge)`:""),this._lastCheckRf=r,this.emit("files-received",d),this._offlineSave("requiredFiles",a),c.length>0&&this.emit("purge-request",c),!this._lastCheckSchedule||this._lastCheckSchedule!==n){h.debug("Collection step: schedule");const u=await this.xmds.schedule();h.info("Schedule received"),this._lastCheckSchedule=n,h.debug("Collection step: processing schedule"),this.emit("schedule-received",u),this.schedule.setSchedule(u),this.updateDataConnectors(),this._offlineSave("schedule",u)}h.debug("Collection step: download-request + mediaInventory");const g=this.schedule.getCurrentLayouts(),f=this.prioritizeFilesByLayout(d,g);this._lastRequiredFiles=d,this.emit("download-request",f),this.submitMediaInventory(d)}else if(r&&h.info("RequiredFiles CRC unchanged, skipping download check"),this._lastCheckSchedule!==n){const a=await this.xmds.schedule();h.info("Schedule received (RF unchanged but schedule changed)"),this._lastCheckSchedule=n,this.emit("schedule-received",a),this.schedule.setSchedule(a),this.updateDataConnectors(),this._offlineSave("schedule",a)}else n&&h.info("Schedule CRC unchanged, skipping");h.debug("Collection step: evaluateSchedule");const s=this.schedule.getCurrentLayouts();if(h.info("Current layouts:",s),this.emit("layouts-scheduled",s),s.length>0)if(this.currentLayoutId)if(s.some(c=>parseInt(String(c).replace(".xlf",""),10)===this.currentLayoutId)){const c=s.findIndex(d=>parseInt(String(d).replace(".xlf",""),10)===this.currentLayoutId);c>=0&&(this._currentLayoutIndex=c),h.debug(`Layout ${this.currentLayoutId} still in schedule, continuing playback`),this.emit("layout-already-playing",this.currentLayoutId)}else{this._currentLayoutIndex=0;const c=this.getNextLayout();c&&(h.info(`Switching to layout ${c.layoutId} (from ${this.currentLayoutId})`),this.emit("layout-prepare-request",c.layoutId))}else{this._currentLayoutIndex=0;const a=this.getNextLayout();a&&(h.info(`Switching to layout ${a.layoutId}`),this.emit("layout-prepare-request",a.layoutId))}else if(h.info("No layouts scheduled, falling back to default"),this.emit("no-layouts-scheduled"),this.currentLayoutId&&((e=this.schedule.schedule)!=null&&e.default)){const a=parseInt(this.schedule.schedule.default.replace(".xlf",""),10);h.info(`Current layout filtered by schedule, switching to default layout ${a}`),this.currentLayoutId=null,this.emit("layout-prepare-request",a)}(((t=o.settings)==null?void 0:t.statsEnabled)==="On"||((i=o.settings)==null?void 0:i.statsEnabled)==="1")&&(this.statsCollector?(h.info("Stats enabled, submitting proof of play"),this.emit("submit-stats-request")):h.warn("Stats enabled but no StatsCollector provided")),this.emit("submit-logs-request"),!this.collectionInterval&&o.settings&&this.setupCollectionInterval(o.settings),this.emit("collection-complete")}catch(o){if(this.hasCachedData())return console.warn("[PlayerCore] Collection failed, falling back to cached data:",(o==null?void 0:o.message)||o),this.emit("collection-error",o),this.collectOffline();throw h.error("Collection error:",o),this.emit("collection-error",o),o}finally{this.collecting=!1}}async initializeXmr(e){var o,r,n,s;const t=((o=e.settings)==null?void 0:o.xmrWebSocketAddress)||((r=e.settings)==null?void 0:r.xmrNetworkAddress);if(!t){h.warn("XMR not configured: no xmrWebSocketAddress or xmrNetworkAddress in CMS settings"),this.emit("xmr-misconfigured",{reason:"missing",message:"XMR address not configured in CMS. Go to CMS Admin → Settings → Configuration → XMR and set the WebSocket address."});return}if(t.startsWith("tcp://")){h.warn(`XMR address uses tcp:// protocol which is not supported by PWA players: ${t}`),h.warn("Configure XMR_WS_ADDRESS in CMS Admin → Settings → Configuration → XMR (e.g. wss://your-domain/xmr)"),this.emit("xmr-misconfigured",{reason:"wrong-protocol",url:t,message:"XMR uses tcp:// protocol (not supported by PWA). Set XMR WebSocket Address to wss://your-domain/xmr in CMS Settings."});return}if(/example\.(org|com|net)/i.test(t)){h.warn(`XMR address contains placeholder domain: ${t}`),h.warn("Configure the real XMR address in CMS Admin → Settings → Configuration → XMR"),this.emit("xmr-misconfigured",{reason:"placeholder",url:t,message:`XMR address is still the default placeholder (${t}). Update it in CMS Settings.`});return}const i=((n=e.settings)==null?void 0:n.xmrCmsKey)||((s=e.settings)==null?void 0:s.serverKey)||this.config.serverKey;h.debug("XMR CMS Key:",i?"present":"missing"),this.xmr?this.xmr.isConnected()?h.debug("XMR already connected"):(h.info("XMR disconnected, attempting to reconnect..."),this.xmr.reconnectAttempts=0,await this.xmr.start(t,i),this.emit("xmr-reconnected",t)):(h.info("Initializing XMR WebSocket:",t),this.xmr=new this.XmrWrapper(this.config,this),await this.xmr.start(t,i),this.emit("xmr-connected",t))}setupCollectionInterval(e){const t=this.displaySettings?this.displaySettings.getCollectInterval():parseInt(e.collectInterval||"300",10),i=t*1e3;h.info(`Setting up collection interval: ${t}s`),this.collectionInterval=setInterval(()=>{h.debug("Running scheduled collection cycle..."),this.collect().catch(o=>{h.error("Collection error:",o),this.emit("collection-error",o)})},i),this.emit("collection-interval-set",t)}updateCollectionInterval(e){if(this.collectionInterval){clearInterval(this.collectionInterval),h.info(`Updating collection interval: ${e}s`);const t=e*1e3;this.collectionInterval=setInterval(()=>{h.debug("Running scheduled collection cycle..."),this.collect().catch(i=>{h.error("Collection error:",i),this.emit("collection-error",i)})},t),this.emit("collection-interval-updated",e)}}async requestLayoutChange(e){h.info(`Layout change requested: ${e}`),this.currentLayoutId=null,this.emit("layout-change-requested",e)}setCurrentLayout(e){this.currentLayoutId=e,this.pendingLayouts.delete(e),this.emit("layout-current",e)}setPendingLayout(e,t){this.pendingLayouts.set(e,t),this.emit("layout-pending",e,t)}clearCurrentLayout(){this.currentLayoutId=null,this.emit("layout-cleared")}getNextLayout(){const e=this.schedule.getCurrentLayouts();if(e.length===0)return null;this._currentLayoutIndex>=e.length&&(this._currentLayoutIndex=0);const t=e[this._currentLayoutIndex];return{layoutId:parseInt(t.replace(".xlf",""),10),layoutFile:t}}peekNextLayout(){const e=this.schedule.getCurrentLayouts();if(e.length<=1)return null;const t=(this._currentLayoutIndex+1)%e.length,i=e[t],o=parseInt(i.replace(".xlf",""),10);return o===this.currentLayoutId?null:{layoutId:o,layoutFile:i}}advanceToNextLayout(){if(this._layoutOverride){h.info("Layout override active, not advancing schedule");return}const e=this.schedule.getCurrentLayouts();if(h.info(`Advancing schedule: ${e.length} layout(s) available, current index ${this._currentLayoutIndex}`),e.length===0){if(this.currentLayoutId){h.info(`No layouts available (all rate-limited), replaying ${this.currentLayoutId} to avoid blank screen`);const o=this.currentLayoutId;this.currentLayoutId=null,this.emit("layout-prepare-request",o)}else h.info("No layouts scheduled during advance"),this.emit("no-layouts-scheduled");return}this._currentLayoutIndex=(this._currentLayoutIndex+1)%e.length;const t=e[this._currentLayoutIndex],i=parseInt(t.replace(".xlf",""),10);if(this.syncManager&&this.schedule.isSyncEvent(t))if(this.isSyncLead()){h.info(`[Sync] Lead requesting coordinated layout change: ${i}`),this.syncManager.requestLayoutChange(i).catch(o=>{h.error("[Sync] Layout change failed:",o),this.emit("layout-prepare-request",i)});return}else{h.info("[Sync] Follower waiting for lead signal (not advancing independently)");return}i===this.currentLayoutId&&(h.info(`Next layout ${i} is same as current, triggering replay`),this.currentLayoutId=null),h.info(`Advancing to layout ${i} (index ${this._currentLayoutIndex}/${e.length})`),this.emit("layout-prepare-request",i)}notifyMediaReady(e,t="media"){h.debug(`File ${e} ready (${t})`);for(const[i,o]of this.pendingLayouts.entries()){const r=t==="layout"&&i===parseInt(e),n=t==="media"&&o.includes(parseInt(e));(r||n)&&(h.debug(`${t} ${e} was needed by pending layout ${i}, checking if ready...`),this.emit("check-pending-layout",i,o))}}async notifyLayoutStatus(e){try{await this.xmds.notifyStatus({currentLayoutId:e}),this.emit("status-notified",e)}catch(t){h.warn("Failed to notify status:",t),this.emit("status-notify-failed",e,t)}}async captureScreenshot(){h.info("Screenshot requested"),this.emit("screenshot-request")}async changeLayout(e){h.info("Layout change requested via XMR:",e),this._layoutOverride={layoutId:parseInt(e,10),type:"change"},this.currentLayoutId=null,this.emit("layout-prepare-request",parseInt(e,10))}async overlayLayout(e){h.info("Overlay layout requested via XMR:",e),this._layoutOverride={layoutId:parseInt(e,10),type:"overlay"},this.emit("overlay-layout-request",parseInt(e,10))}async revertToSchedule(){h.info("Reverting to scheduled content"),this._layoutOverride=null,this.currentLayoutId=null,this.emit("revert-to-schedule");const e=this.schedule.getCurrentLayouts();if(e.length>0){const t=e[0],i=parseInt(t.replace(".xlf",""),10);this.emit("layout-prepare-request",i)}else this.emit("no-layouts-scheduled")}async purgeAll(){return h.info("Purge all cache requested via XMR"),this._lastCheckRf=null,this._lastCheckSchedule=null,this.emit("purge-all-request"),this.collectNow()}async executeCommand(e,t){if(h.info("Execute command requested:",e),!t||!t[e]){h.warn("Unknown command code:",e),this.emit("command-result",{code:e,success:!1,reason:"Unknown command"});return}const i=t[e],o=i.commandString||i.value||"";if(o.startsWith("http|")){const r=o.split("|"),n=r[1],s=r[2]||"application/json";try{const a=await fetch(n,{method:"POST",headers:{"Content-Type":s}}),c=a.ok;h.info(`HTTP command ${e} result: ${a.status}`),this.emit("command-result",{code:e,success:c,status:a.status})}catch(a){h.error(`HTTP command ${e} failed:`,a),this.emit("command-result",{code:e,success:!1,reason:a.message})}}else h.warn("Non-HTTP commands not supported in browser:",e),this.emit("command-result",{code:e,success:!1,reason:"Only HTTP commands supported in browser"})}triggerWebhook(e){h.info("Webhook trigger from XMR:",e),this.handleTrigger(e)}refreshDataConnectors(){h.info("Data connector refresh requested via XMR"),this.dataConnectorManager.refreshAll(),this.emit("data-connectors-refreshed")}async submitMediaInventory(e){if(!(!e||e.length===0))try{const t=Math.floor(Date.now()/1e3),o=`<files>${e.filter(r=>r.type==="media"||r.type==="layout").map(r=>`<file type="${r.type}" id="${r.id}" complete="1" md5="${r.md5||""}" lastChecked="${t}"/>`).join("")}</files>`;await this.xmds.mediaInventory(o),h.info(`Media inventory submitted: ${e.length} files`),this.emit("media-inventory-submitted",e.length)}catch(t){h.warn("MediaInventory submission failed:",t)}}async blackList(e,t,i){try{await this.xmds.blackList(e,t,i),this.emit("media-blacklisted",{mediaId:e,type:t,reason:i})}catch(o){h.warn("BlackList failed:",o)}}isLayoutOverridden(){return this._layoutOverride!==null}handleTrigger(e){const t=this.schedule.findActionByTrigger(e);if(!t){h.debug("No scheduled action matches trigger:",e);return}switch(h.info(`Action triggered: ${t.actionType} (trigger: ${e})`),t.actionType){case"navLayout":case"navigateToLayout":t.layoutCode&&this.changeLayout(t.layoutCode);break;case"navWidget":case"navigateToWidget":this.emit("navigate-to-widget",t);break;case"command":this.emit("execute-command",t.commandCode);break;default:h.warn("Unknown action type:",t.actionType)}}updateDataConnectors(){const e=this.schedule.getDataConnectors();e.length>0&&h.info(`Configuring ${e.length} data connector(s)`),this.dataConnectorManager.setConnectors(e),e.length>0&&(this.dataConnectorManager.startPolling(),this.emit("data-connectors-started",e.length))}getDataConnectorManager(){return this.dataConnectorManager}setSyncManager(e){this.syncManager=e,h.info("SyncManager attached:",e.isLead?"LEAD":"FOLLOWER")}isInSyncGroup(){return this.syncConfig!==null}isSyncLead(){var e;return((e=this.syncConfig)==null?void 0:e.isLead)===!0}getSyncConfig(){return this.syncConfig}cleanup(){this.collectionInterval&&(clearInterval(this.collectionInterval),this.collectionInterval=null),this.xmr&&(this.xmr.stop(),this.xmr=null),this.syncManager&&(this.syncManager.stop(),this.syncManager=null),this.dataConnectorManager.cleanup(),this.emit("cleanup-complete"),this.removeAllListeners()}getCurrentLayoutId(){return this.currentLayoutId}isCollecting(){return this.collecting}getPendingLayouts(){return Array.from(this.pendingLayouts.keys())}prioritizeFilesByLayout(e,t){const i=new Set;t.forEach(r=>{i.add(parseInt(String(r).replace(".xlf",""),10))});const o=e.map(r=>{let n;if(r.type==="layout"){const s=parseInt(r.id);n=i.has(s)?0:1}else r.type==="resource"||r.code==="fonts.css"||r.path&&(r.path.includes("bundle.min")||r.path.includes("fonts"))?n=2:n=3;return{file:r,tier:n}});return o.sort((r,n)=>r.tier!==n.tier?r.tier-n.tier:(r.file.size||0)-(n.file.size||0)),o.map(r=>r.file)}}class J{constructor(e,t){v(this,"overlay",null);v(this,"config");v(this,"updateTimer",null);this.config={position:"bottom-right",updateInterval:1e3,autoHide:!0,...e},this.config.enabled&&this.createOverlay()}createOverlay(){this.overlay=document.createElement("div"),this.overlay.id="download-overlay",this.overlay.style.cssText=`
3
+ position: fixed;
4
+ top: 50px;
5
+ left: 10px;
6
+ background: rgba(0, 0, 0, 0.85);
7
+ color: #fff;
8
+ font-family: system-ui, -apple-system, sans-serif;
9
+ font-size: 12px;
10
+ padding: 8px 12px;
11
+ border-radius: 4px;
12
+ border: 1px solid rgba(255, 255, 255, 0.2);
13
+ z-index: 999999;
14
+ max-width: 350px;
15
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
16
+ `,document.body.appendChild(this.overlay)}async updateOverlay(){var e;if(this.overlay)try{if(!((e=navigator.serviceWorker)!=null&&e.controller))throw new Error("No SW controller");const t=new MessageChannel,i=new Promise(r=>{t.port1.onmessage=n=>r(n.data),setTimeout(()=>r({success:!1}),500)});navigator.serviceWorker.controller.postMessage({type:"GET_DOWNLOAD_PROGRESS"},[t.port2]);const o=await i;if(o.success){const r=this.renderStatus(o.progress);r?(this.overlay.innerHTML=r,this.overlay.style.display="block"):(this.stopUpdating(),this.config.autoHide&&(this.overlay.style.display="none"))}else throw new Error("Progress request failed")}catch{this.stopUpdating(),this.config.autoHide&&this.overlay&&(this.overlay.style.display="none")}}renderStatus(e){const t=e||{};if(!t||Object.keys(t).length===0)return this.config.autoHide?"":'<div style="color: #6c6;">✓ No downloads</div>';let o=`<div style="font-weight: 600; margin-bottom: 6px;">Downloads: ${Object.keys(t).length} active</div>`;for(const[r,n]of Object.entries(t)){const s=this.extractFilename(r),a=Math.round(n.percent||0),c=this.formatBytes(n.downloaded||0),d=this.formatBytes(n.total||0);o+=`
17
+ <div style="margin-bottom: 6px; padding-bottom: 6px; border-bottom: 1px solid rgba(255,255,255,0.1);">
18
+ <div style="font-size: 11px; margin-bottom: 2px;">${s}</div>
19
+ <div style="background: rgba(255,255,255,0.1); height: 4px; border-radius: 2px; overflow: hidden;">
20
+ <div style="width: ${a}%; height: 100%; background: #4a9eff; transition: width 0.3s;"></div>
21
+ </div>
22
+ <div style="color: #999; font-size: 10px; margin-top: 2px;">
23
+ ${a}% · ${c} / ${d}
24
+ </div>
25
+ </div>
26
+ `}return o}extractFilename(e){try{const i=new URL(e).searchParams.get("file")||e.split("/").pop()||"unknown";return i.length>30?i.substring(0,27)+"...":i}catch{return"unknown"}}formatBytes(e){if(e<1024)return`${e} B`;const t=e/1024;if(t<1024)return`${t.toFixed(1)} KB`;const i=t/1024;return i<1024?`${i.toFixed(1)} MB`:`${(i/1024).toFixed(1)} GB`}startUpdating(){this.updateTimer||(this.updateTimer=window.setInterval(()=>{this.updateOverlay()},this.config.updateInterval))}stopUpdating(){this.updateTimer&&(clearInterval(this.updateTimer),this.updateTimer=null)}destroy(){this.stopUpdating(),this.overlay&&(this.overlay.remove(),this.overlay=null)}setEnabled(e){this.config.enabled=e,e&&!this.overlay?this.createOverlay():!e&&this.overlay&&this.destroy()}}function Y(){const e=new URLSearchParams(window.location.search).get("showDownloads");if(e!==null)return{enabled:e!=="0"&&e!=="false"};const t=localStorage.getItem("xibo_show_download_overlay");return t!==null?{enabled:t==="true"}:{enabled:!0,autoHide:!0}}const l=U("PWA"),$=new URL("./",window.location.href).pathname.replace(/\/$/,"");let R,M,x,Q,X,Z,C,ee,te,ie,oe,re;class ne{constructor(){v(this,"renderer");v(this,"core");v(this,"xmds");v(this,"downloadOverlay",null);v(this,"statsCollector",null);v(this,"logReporter",null);v(this,"displaySettings",null);v(this,"currentScheduleId",-1);v(this,"preparingLayoutId",null);v(this,"_screenshotInterval",null);v(this,"_screenshotMethod",null);v(this,"_screenshotInFlight",!1);v(this,"_html2canvasMod",null);v(this,"_wakeLock",null)}async init(){if(l.info("Initializing player with RendererLite + PlayerCore..."),await this.loadCoreModules(),"serviceWorker"in navigator)try{const i=await navigator.serviceWorker.register(`${$}/sw-pwa.js?v=${Date.now()}`,{scope:`${$}/`,type:"module",updateViaCache:"none"});l.info("Service Worker registered for offline mode:",i.scope),navigator.storage&&navigator.storage.persist&&(await navigator.storage.persist()?l.info("Persistent storage granted - cache won't be evicted"):l.warn("Persistent storage denied - cache may be evicted"))}catch(i){l.warn("Service Worker registration failed:",i)}l.info("Initializing cache..."),await R.init(),l.info("Initializing CacheProxy..."),C=new ye,await C.init(),l.info("CacheProxy ready - using Service Worker backend");const e=document.getElementById("player-container");if(!e)throw new Error("No #player-container found");this.renderer=new be({cmsUrl:x.cmsAddress,hardwareKey:x.hardwareKey},e,{getMediaUrl:async i=>{if(l.debug(`getMediaUrl called for media ${i}`),!await C.hasFile("media",String(i)))return l.warn(`Media ${i} not in cache`),"";const r=`${$}/cache/media/${i}`;return l.debug(`Using streaming URL for media ${i}: ${r}`),r},getWidgetHtml:async i=>{const o=`${$}/cache/widget/${i.layoutId}/${i.regionId}/${i.id}`;l.debug(`Looking for widget HTML at: ${o}`,i);try{if(await(await caches.open("xibo-media-v1")).match(o))return l.debug(`Widget HTML cached at ${o}, using cache URL for iframe`),{url:o,fallback:i.raw||""};l.warn(`No cached HTML found at ${o}`)}catch(r){l.error(`Failed to get cached widget HTML for ${i.id}:`,r)}return l.warn(`Using widget.raw fallback for ${i.id}`),i.raw||""}}),this.core=new Ce({config:x,xmds:this.xmds,cache:C,schedule:M,renderer:this.renderer,xmrWrapper:Z,statsCollector:this.statsCollector,displaySettings:this.displaySettings}),this.setupCoreEventHandlers(),this.setupRendererEventHandlers(),this.setupServiceWorkerEventHandlers(),this.setupInteractiveControl(),this.core.on("register-complete",i=>{var n,s;const o=parseFloat((n=i==null?void 0:i.settings)==null?void 0:n.latitude),r=parseFloat((s=i==null?void 0:i.settings)==null?void 0:s.longitude);o&&r&&!isNaN(o)&&!isNaN(r)&&(l.info(`Display location from CMS: ${o.toFixed(4)}, ${r.toFixed(4)}`),M!=null&&M.setLocation&&M.setLocation(o,r))}),this.setupUI(),window.addEventListener("online",()=>{console.log("[PWA] Browser reports online — triggering immediate collection"),this.updateStatus("Back online, syncing..."),this.removeOfflineIndicator(),this.core.collectNow().catch(i=>{l.error("Failed to collect after coming online:",i)})}),window.addEventListener("offline",()=>{console.warn("[PWA] Browser reports offline — continuing playback with cached data"),this.updateStatus("Offline mode — using cached content"),this.showOfflineIndicator()});const t=Y();t.enabled&&(this.downloadOverlay=new J(t,C),l.info("Download overlay enabled (hover bottom-right corner)")),await this.requestWakeLock(),document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&this.requestWakeLock()}),await this.core.collect(),l.info("Player initialized successfully")}async requestWakeLock(){if(!("wakeLock"in navigator)){l.debug("Wake Lock API not supported");return}try{this._wakeLock=await navigator.wakeLock.request("screen"),l.info("Screen Wake Lock acquired — display will stay on"),this._wakeLock.addEventListener("release",()=>{l.debug("Screen Wake Lock released"),this._wakeLock=null})}catch(e){l.warn("Wake Lock request failed:",e==null?void 0:e.message)}}async loadCoreModules(){var e;try{const t=await I(()=>import("./index-BY2j60YZ.js"),__vite__mapDeps([0,1,2]),import.meta.url),i=await I(()=>import("./index-_uzldOpz.js"),__vite__mapDeps([3,2]),import.meta.url),o=await I(()=>import("./index-BPNsrSEv.js"),__vite__mapDeps([4,2]),import.meta.url),r=await I(()=>import("./cache-proxy-Cx4Z8XMC.js").then(d=>d.i),__vite__mapDeps([1,2]),import.meta.url),n=await I(()=>import("./index-_q2HbdAU.js"),__vite__mapDeps([5,2]),import.meta.url),s=await I(()=>import("./index-CTmjUTVM.js"),__vite__mapDeps([6,2]),import.meta.url),a=await I(()=>import("./index-BEhNaWZ4.js"),__vite__mapDeps([7,2,1]),import.meta.url);if(R=t.cacheManager,M=o.scheduleManager,x=r.config,Q=i.RestClient,X=i.XmdsClient,Z=n.XmrWrapper,ee=s.StatsCollector,te=s.formatStats,ie=s.LogReporter,oe=s.formatLogs,re=a.DisplaySettings,(e=window.electronAPI)!=null&&e.getSystemInfo)try{const d=await window.electronAPI.getSystemInfo();d.macAddress&&(x.macAddress=d.macAddress)}catch{}if($.includes("pwa-xmds")||new URLSearchParams(window.location.search).get("transport")==="xmds")l.info("Using XMDS/SOAP transport (forced)"),this.xmds=new X(x);else{this.xmds=new Q(x);try{await this.xmds.registerDisplay(),l.info("Using REST transport")}catch(d){l.warn("REST unavailable, falling back to XMDS/SOAP:",d.message),this.xmds=new X(x)}}this.statsCollector=new ee,await this.statsCollector.init(),l.info("Stats collector initialized"),this.logReporter=new ie,await this.logReporter.init(),l.info("Log reporter initialized"),fe(({level:d,name:g,args:f})=>{if(!this.logReporter)return;const u=f.map(y=>typeof y=="string"?y:JSON.stringify(y)).join(" ");this.logReporter.log(d,`[${g}] ${u}`,"PLAYER").catch(()=>{})}),this.displaySettings=new re,l.info("Display settings manager initialized"),l.info("Core modules loaded")}catch(t){throw l.error("Failed to load core modules:",t),t}}setupCoreEventHandlers(){this.core.on("collection-start",()=>{this.updateStatus("Collecting data from CMS...")}),this.core.on("register-complete",e=>{var i;const t=((i=this.displaySettings)==null?void 0:i.getDisplayName())||e.displayName||x.hardwareKey;this.updateStatus(`Registered: ${t}`),this.displaySettings&&(document.title=`Xibo Player - ${this.displaySettings.getDisplayName()}`)}),this.core.on("files-received",e=>{this.updateStatus(`Downloading ${e.length} files...`)}),this.core.on("offline-mode",e=>{e?(this.updateStatus("Offline mode — using cached content"),this.showOfflineIndicator()):(this.updateStatus("Back online"),this.removeOfflineIndicator())}),this.core.on("purge-request",async e=>{try{const t=await C.deleteFiles(e);l.info(`Purge complete: ${t.deleted}/${t.total} files deleted`)}catch(t){l.warn("Purge failed:",t)}}),this.core.on("download-request",async e=>{var t;(t=this.downloadOverlay)==null||t.startUpdating();try{await C.requestDownload(e),l.info("Download request complete")}catch(i){l.error("Download request failed:",i),this.updateStatus("Download failed: "+i,"error")}}),this.core.on("schedule-received",e=>{var t;if(this.updateStatus("Processing schedule..."),e.layouts&&e.layouts.length>0?this.currentScheduleId=parseInt(e.layouts[0].scheduleid)||-1:e.campaigns&&e.campaigns.length>0&&(this.currentScheduleId=parseInt(e.campaigns[0].scheduleid)||-1),(t=this.renderer)!=null&&t.layoutPool){const i=new Set;if(e.layouts)for(const r of e.layouts){const n=parseInt(String(r.file||r.id||r).replace(".xlf",""),10);n&&i.add(n)}if(e.campaigns){for(const r of e.campaigns)if(r.layouts)for(const n of r.layouts){const s=parseInt(String(n.file||n.id||n).replace(".xlf",""),10);s&&i.add(s)}}const o=this.renderer.layoutPool.clearWarmNotIn(i);o>0&&l.info(`Cleared ${o} preloaded layout(s) no longer in schedule`)}l.debug("Current scheduleId for stats:",this.currentScheduleId)}),this.core.on("layout-prepare-request",async e=>{await this.prepareAndRenderLayout(e)}),this.core.on("layout-already-playing",()=>{}),this.core.on("no-layouts-scheduled",()=>{this.updateStatus("No layouts scheduled")}),this.core.on("collection-complete",()=>{this.updateStatus("Collection complete")}),this.core.on("collection-error",e=>{var t;this.updateStatus(`Collection error: ${e}`,"error"),(t=this.logReporter)==null||t.reportFault("COLLECTION_FAILED",`Collection cycle failed: ${(e==null?void 0:e.message)||e}`)}),this.core.on("xmr-connected",e=>{l.info("XMR connected:",e)}),this.core.on("xmr-misconfigured",e=>{l.warn(`XMR misconfigured (${e.reason}): ${e.message}`),console.warn(`%c[XMR] ${e.message}`,"background: #ff9800; color: #000; padding: 4px 8px; font-weight: bold;")}),this.core.on("log-level-changed",()=>{const e=se();l.info(`Log level changed, debug=${e}`),e&&!this.downloadOverlay?(this.downloadOverlay=new J(Y(),C),l.info("Download overlay enabled (log level → DEBUG)")):!e&&this.downloadOverlay&&(this.downloadOverlay.destroy(),this.downloadOverlay=null,l.info("Download overlay disabled (log level above DEBUG)"))}),this.core.on("overlay-layout-request",async e=>{l.info("Overlay layout requested:",e),await this.prepareAndRenderLayout(e)}),this.core.on("revert-to-schedule",()=>{l.info("Reverting to scheduled content"),this.updateStatus("Reverting to schedule...")}),this.core.on("purge-all-request",async()=>{l.info("Purging all cached content..."),this.updateStatus("Purging cache...");try{const e=await caches.keys();await Promise.all(e.map(t=>caches.delete(t))),l.info(`Purged ${e.length} caches`),await R.init()}catch(e){l.error("Cache purge failed:",e)}}),this.core.on("command-result",e=>{var t;l.info("Command result:",e),e.success||(t=this.logReporter)==null||t.reportFault("COMMAND_FAILED",`Command ${e.code} failed: ${e.reason||"unknown"}`)}),this.displaySettings&&(this.displaySettings.on("interval-changed",e=>{l.info(`Collection interval changed to ${e}s`)}),this.displaySettings.on("settings-applied",(e,t)=>{t.length>0&&l.info("Settings updated from CMS:",t.join(", ")),this._screenshotInterval||this.startScreenshotInterval()})),this.core.on("submit-stats-request",async()=>{await this.submitStats()}),this.core.on("submit-logs-request",async()=>{await this.submitLogs()}),this.core.on("screenshot-request",async()=>{await this.captureAndSubmitScreenshot()}),window.addEventListener("media-cached",async e=>{var i;const t=(i=e.detail)==null?void 0:i.id;l.debug(`Media ${t} download completed`),this.core.notifyMediaReady(t)}),this.core.on("check-pending-layout",async e=>{await this.prepareAndRenderLayout(e)})}setupInteractiveControl(){var e;(e=navigator.serviceWorker)==null||e.addEventListener("message",t=>{var c,d;if(((c=t.data)==null?void 0:c.type)!=="INTERACTIVE_CONTROL")return;const{method:i,path:o,search:r,body:n}=t.data,s=(d=t.ports)==null?void 0:d[0];if(!s)return;const a=this.handleInteractiveControl(i,o,r,n);s.postMessage(a)})}handleInteractiveControl(e,t,i,o){var r;switch(l.debug("IC request:",e,t,i),t){case"/info":return{status:200,body:JSON.stringify({hardwareKey:x.hardwareKey,displayName:x.displayName,playerType:"pwa",currentLayoutId:this.core.getCurrentLayoutId()})};case"/trigger":{let n={};try{n=o?JSON.parse(o):{}}catch{}return this.renderer.emit("interactiveTrigger",{targetId:n.id,triggerCode:n.trigger}),n.trigger&&this.core.handleTrigger(n.trigger),{status:200,body:"OK"}}case"/duration/expire":{let n={};try{n=o?JSON.parse(o):{}}catch{}return l.info("IC: Widget duration expire requested for",n.id),this.renderer.emit("widgetExpire",{widgetId:n.id}),{status:200,body:"OK"}}case"/duration/extend":{let n={};try{n=o?JSON.parse(o):{}}catch{}return l.info("IC: Widget duration extend by",n.duration,"for",n.id),this.renderer.emit("widgetExtendDuration",{widgetId:n.id,duration:parseInt(n.duration)}),{status:200,body:"OK"}}case"/duration/set":{let n={};try{n=o?JSON.parse(o):{}}catch{}return l.info("IC: Widget duration set to",n.duration,"for",n.id),this.renderer.emit("widgetSetDuration",{widgetId:n.id,duration:parseInt(n.duration)}),{status:200,body:"OK"}}case"/fault":{let n={};try{n=o?JSON.parse(o):{}}catch{}return(r=this.logReporter)==null||r.reportFault(n.code||"WIDGET_FAULT",n.reason||"Widget reported fault"),{status:200,body:"OK"}}case"/realtime":{const s=new URLSearchParams(i).get("dataKey");if(l.debug("IC: Realtime data request for key:",s),!s)return{status:400,body:JSON.stringify({error:"Missing dataKey parameter"})};const c=this.core.getDataConnectorManager().getData(s);return c===null?{status:404,body:JSON.stringify({error:`No data available for key: ${s}`})}:{status:200,body:typeof c=="string"?c:JSON.stringify(c)}}default:return{status:404,body:JSON.stringify({error:"Unknown IC route"})}}}setupServiceWorkerEventHandlers(){navigator.serviceWorker&&navigator.serviceWorker.addEventListener("message",e=>{const{type:t,fileId:i,fileType:o}=e.data;t==="FILE_CACHED"&&(l.debug(`Service Worker cached ${o}/${i}`),(o==="media"||o==="layout")&&this.core.notifyMediaReady(parseInt(i),o))})}setupRendererEventHandlers(){this.renderer.on("layoutStart",(e,t)=>{l.info("Layout started:",e),this.updateStatus(`Playing layout ${e}`),this.core.setCurrentLayout(e),this.statsCollector&&this.statsCollector.startLayout(e,this.currentScheduleId).catch(i=>{l.error("Failed to start layout stat:",i)})}),this.renderer.on("layoutEnd",e=>{l.info("Layout ended:",e),M==null||M.recordPlay(e.toString()),this.statsCollector&&this.statsCollector.endLayout(e,this.currentScheduleId).catch(i=>{l.error("Failed to end layout stat:",i)}),this.core.notifyLayoutStatus(e),this.core.clearCurrentLayout();const t=this.core.getPendingLayouts();if(t.length>0){l.info(`Layout ${t[0]} pending download, skipping advance`);return}l.info("Layout cycle completed, advancing to next layout..."),this.core.advanceToNextLayout()}),this.renderer.on("widgetStart",e=>{const{widgetId:t,layoutId:i,mediaId:o}=e;l.debug("Widget started:",e.type,t,"media:",o),this.statsCollector&&o&&this.statsCollector.startWidget(o,i,this.currentScheduleId).catch(r=>{l.error("Failed to start widget stat:",r)})}),this.renderer.on("widgetEnd",e=>{const{widgetId:t,layoutId:i,mediaId:o}=e;l.debug("Widget ended:",e.type,t,"media:",o),this.statsCollector&&o&&this.statsCollector.endWidget(o,i,this.currentScheduleId).catch(r=>{l.error("Failed to end widget stat:",r)})}),this.renderer.on("error",e=>{var t;l.error("Renderer error:",e),this.updateStatus(`Error: ${e.type}`,"error"),(t=this.logReporter)==null||t.reportFault(e.type||"RENDERER_ERROR",`Renderer error: ${e.message||e.type} (layout ${e.layoutId||"unknown"})`)}),this.renderer.on("action-trigger",e=>{var s,a;const{actionType:t,triggerCode:i,layoutCode:o,targetId:r,commandCode:n}=e;switch(l.info("Action trigger:",t,e),t){case"navLayout":case"navigateToLayout":i?this.core.handleTrigger(i):o&&this.core.changeLayout(o);break;case"navWidget":case"navigateToWidget":i?this.core.handleTrigger(i):r&&this.renderer.navigateToWidget(r);break;case"previousWidget":this.renderer.previousWidget((s=e.source)==null?void 0:s.regionId);break;case"nextWidget":this.renderer.nextWidget((a=e.source)==null?void 0:a.regionId);break;case"command":n&&this.core.executeCommand(n);break;default:l.warn("Unknown action type:",t)}}),this.renderer.on("request-next-layout-preload",async()=>{try{const e=this.core.peekNextLayout();if(!e){l.debug("No next layout to preload (single layout schedule or same layout)");return}const t=e.layoutId;if(this.renderer.layoutPool.has(t)){l.debug(`Layout ${t} already in preload pool`);return}l.info(`Preloading next layout ${t}...`);const i=await R.getCachedFile("layout",t);if(!i){l.debug(`Layout ${t} XLF not cached, skipping preload`);return}const o=await i.text(),r=await this.getRequiredMediaIds(o),n=this.getVideoMediaIds(o);if(!await this.checkAllMediaCached(r,n)){l.debug(`Media not fully cached for layout ${t}, skipping preload`);return}await this.fetchWidgetHtml(o,t),n.length>0&&await C.prewarmVideoChunks(n),await this.renderer.preloadLayout(o,t)?l.info(`Layout ${t} preloaded successfully`):l.warn(`Layout ${t} preload failed (will fall back to normal render)`)}catch(e){l.warn("Layout preload failed (non-blocking):",e)}})}async prepareAndRenderLayout(e){var t;if(this.core.getCurrentLayoutId()===e){l.debug(`Layout ${e} already playing, skipping duplicate prepare`);return}if(this.preparingLayoutId===e){l.debug(`Layout ${e} preparation already in progress, skipping`);return}this.preparingLayoutId=e;try{const i=await R.getCachedFile("layout",e);if(!i){l.info("Layout not in cache yet, marking as pending:",e),this.core.setPendingLayout(e,[e]),this.updateStatus(`Downloading layout ${e}...`);return}const o=await i.text(),r=await this.getRequiredMediaIds(o),n=this.getVideoMediaIds(o);if(!await this.checkAllMediaCached(r,n)){for(const a of r)C.prioritizeDownload("media",String(a));l.info(`Waiting for media to finish downloading for layout ${e}`),this.updateStatus(`Preparing layout ${e}...`),this.core.setPendingLayout(e,r);return}await this.prefetchWidgetDependencies(),await this.fetchWidgetHtml(o,e),n.length>0&&(l.info(`Pre-warming ${n.length} video file(s) for layout ${e}`),await C.prewarmVideoChunks(n)),await this.renderer.renderLayout(o,e),this.updateStatus(`Playing layout ${e}`)}catch(i){l.error("Failed to prepare layout:",e,i),this.updateStatus(`Failed to load layout ${e}`,"error"),(t=this.logReporter)==null||t.reportFault("LAYOUT_LOAD_FAILED",`Failed to prepare layout ${e}: ${(i==null?void 0:i.message)||i}`)}finally{this.preparingLayoutId=null}}async getRequiredMediaIds(e){const i=new DOMParser().parseFromString(e,"text/xml"),o=[];i.querySelectorAll("media[fileId]").forEach(a=>{const c=a.getAttribute("fileId");c&&o.push(parseInt(c,10))});const n=i.querySelector("layout"),s=n==null?void 0:n.getAttribute("background");if(s){const a=parseInt(s,10);!isNaN(a)&&!o.includes(a)&&o.push(a)}return o}getVideoMediaIds(e){const i=new DOMParser().parseFromString(e,"text/xml"),o=[];return i.querySelectorAll('media[type="video"]').forEach(r=>{const n=r.getAttribute("fileId");n&&o.push(parseInt(n,10))}),o}async checkAllMediaCached(e,t=[]){for(const i of e)try{if(!await C.hasFile("media",String(i)))return l.debug(`Media ${i} not yet cached`),!1;const r=await R.getCachedResponse("media",i);if(!r){const g=await caches.open("xibo-media-v1"),f=await g.match(`${$}/cache/media/${i}/metadata`);if(f){const u=await f.text(),y=JSON.parse(u),m=(y.totalSize/1024/1024).toFixed(1);if(t.includes(i)){if(!await g.match(`${$}/cache/media/${i}/chunk-0`))return l.debug(`Media ${i} video: chunk 0 not yet available`),!1;const _=y.numChunks-1;if(_>0&&!await g.match(`${$}/cache/media/${i}/chunk-${_}`))return l.debug(`Media ${i} video: last chunk (${_}) not yet available`),!1;l.info(`Media ${i} video ready for early playback (chunk 0 + ${_} of ${y.numChunks}, ${m} MB total)`)}else{const w=`${$}/cache/media/${i}/chunk-${y.numChunks-1}`;if(!await g.match(w))return l.debug(`Media ${i} chunked but still downloading (chunk ${y.numChunks-1} missing)`),!1;l.debug(`Media ${i} cached as chunks (${y.numChunks} x ${(y.chunkSize/1024/1024).toFixed(0)} MB = ${m} MB total)`)}continue}}const n=r.headers.get("Content-Type")||"",s=await r.blob();if(n==="text/plain"||s.size<100){l.warn(`Media ${i} corrupted (${n}, ${s.size} bytes) - will re-download`);const g=await caches.open("xibo-media-v1"),f=`${$}/cache/media/${i}`;return await g.delete(f),!1}const a=s.size/1024,c=a/1024,d=c>=1?`${c.toFixed(1)} MB`:`${a.toFixed(1)} KB`;l.debug(`Media ${i} cached and valid (${d})`)}catch{l.warn(`Unable to verify media ${i}, assuming cached (offline mode)`)}return!0}async prefetchWidgetDependencies(){const e=["bundle.min.js","fonts.css"],t=await caches.open("xibo-static-v1");for(const i of e)await t.match(`${$}/cache/static/${i}`)?l.debug(`Widget dependency ${i} already cached by SW`):l.debug(`Widget dependency ${i} not yet cached (will be fetched by SW on first use)`)}async fetchWidgetHtml(e,t){const o=new DOMParser().parseFromString(e,"text/xml"),r=["clock","calendar","weather","currencies","stocks","twitter","global","embedded","text","ticker"],n=[];for(const s of o.querySelectorAll("region")){const a=s.getAttribute("id");for(const c of s.querySelectorAll("media")){const d=c.getAttribute("type"),g=c.getAttribute("id");if(r.some(f=>d==null?void 0:d.includes(f))){const f=`/cache/widget/${t}/${a}/${g}`;n.push((async()=>{try{const y=await(await caches.open("xibo-media-v1")).match(f);let m;y?(m=await y.text(),l.debug(`Using cached widget HTML for ${d} ${g}`)):(m=await this.xmds.getResource(t,a,g),await R.cacheWidgetHtml(t,a,g,m),l.debug(`Retrieved widget HTML for ${d} ${g}`));const L=c.querySelector("raw");if(L)L.textContent=m;else{const w=o.createElement("raw");w.textContent=m,c.appendChild(w)}}catch(u){l.warn(`Failed to get widget HTML for ${d} ${g}:`,u)}})())}}}n.length>0&&(l.info(`Fetching ${n.length} widget HTML resources in parallel...`),await Promise.all(n),l.debug("All widget HTML fetched"))}setupUI(){document.getElementById("player-container")||l.warn("No #player-container found"),this.updateConfigDisplay()}updateConfigDisplay(){const e=document.getElementById("config-info");e&&(e.textContent=`CMS: ${x.cmsAddress} | Display: ${x.displayName||"Unknown"} | HW: ${x.hardwareKey}`)}async submitStats(){var e;if(!this.statsCollector){l.warn("Stats collector not initialized");return}try{const i=(((e=this.displaySettings)==null?void 0:e.getSetting("aggregationLevel"))||"Individual")==="Aggregate"?await this.statsCollector.getAggregatedStatsForSubmission(50):await this.statsCollector.getStatsForSubmission(50);if(i.length===0){l.debug("No stats to submit");return}l.info(`Submitting ${i.length} proof of play stats...`);const o=te(i);await this.xmds.submitStats(o)?(l.info("Stats submitted successfully"),await this.statsCollector.clearSubmittedStats(i),l.debug(`Cleared ${i.length} submitted stats from database`)):l.warn("Stats submission failed (CMS returned false)")}catch(t){l.error("Failed to submit stats:",t)}}async submitLogs(){if(this.logReporter)try{const e=await this.logReporter.getLogsForSubmission(100);if(e.length===0){l.debug("No logs to submit");return}l.info(`Submitting ${e.length} logs to CMS...`);const t=oe(e);await this.xmds.submitLog(t)?(l.info("Logs submitted successfully"),await this.logReporter.clearSubmittedLogs(e)):l.warn("Log submission failed (CMS returned false)")}catch(e){l.error("Failed to submit logs:",e)}}async captureAndSubmitScreenshot(){var e;if(this._screenshotInFlight){l.debug("Screenshot capture already in progress, skipping");return}this._screenshotInFlight=!0;try{let t;if(this._screenshotMethod==="electron"||this._screenshotMethod===null&&((e=window.electronAPI)!=null&&e.captureScreenshot)){const o=await window.electronAPI.captureScreenshot();if(o)this._screenshotMethod="electron",t=o;else{l.debug("Electron screenshot not ready yet, will retry next interval");return}}else t=await this.captureWithBrowserMethods();await this.xmds.submitScreenShot(t)?l.info(`Screenshot submitted (${this._screenshotMethod})`):l.warn("Screenshot submission failed")}catch(t){l.error("Failed to capture screenshot:",t)}finally{this._screenshotInFlight=!1}}async captureWithBrowserMethods(){if(this._screenshotMethod!=="html2canvas"){const e=await this.captureNative();if(e)return this._screenshotMethod="native",e;this._screenshotMethod="html2canvas",l.info("Native screen capture unavailable, using html2canvas")}return this.captureHtml2Canvas()}async captureNative(){var t;if(!((t=navigator.mediaDevices)!=null&&t.getDisplayMedia))return null;let e=null;try{e=await navigator.mediaDevices.getDisplayMedia({video:{displaySurface:"browser"},audio:!1,preferCurrentTab:!0});const i=e.getVideoTracks()[0];if(typeof ImageCapture<"u"){const s=await new ImageCapture(i).grabFrame(),a=document.createElement("canvas");return a.width=s.width,a.height=s.height,a.getContext("2d").drawImage(s,0,0),s.close(),a.toDataURL("image/jpeg",.8).split(",")[1]}const o=document.createElement("video");o.srcObject=e,o.muted=!0,await o.play(),await new Promise(n=>requestAnimationFrame(n));const r=document.createElement("canvas");return r.width=o.videoWidth,r.height=o.videoHeight,r.getContext("2d").drawImage(o,0,0),o.pause(),r.toDataURL("image/jpeg",.8).split(",")[1]}catch{return null}finally{e==null||e.getTracks().forEach(i=>i.stop())}}async captureHtml2Canvas(){const e=document.createElement("canvas");e.width=window.innerWidth,e.height=window.innerHeight;const t=e.getContext("2d");t.fillStyle="#000",t.fillRect(0,0,e.width,e.height);const i=document.getElementById("player-container");if(!i)return e.toDataURL("image/jpeg",.8).split(",")[1];const o=i.getBoundingClientRect(),r=getComputedStyle(i),n=r.backgroundColor;n&&n!=="transparent"&&n!=="rgba(0, 0, 0, 0)"&&(t.fillStyle=n,t.fillRect(o.left,o.top,o.width,o.height));const s=r.backgroundImage;if(s&&s!=="none"){const d=s.match(/url\(["']?(.*?)["']?\)/);if(d)try{const g=new Image;g.crossOrigin="anonymous",await new Promise(f=>{g.onload=()=>f(),g.onerror=()=>f(),setTimeout(()=>f(),2e3),g.src=d[1]}),g.naturalWidth&&t.drawImage(g,o.left,o.top,o.width,o.height)}catch{}}this._html2canvasMod||(this._html2canvasMod=(await I(async()=>{const{default:d}=await import("./html2canvas.esm-CBrSDip1.js");return{default:d}},[],import.meta.url)).default);const a=i.querySelectorAll("img, video, iframe, canvas");let c=0;for(const d of a){const g=d;if(g.style.visibility==="hidden"||g.style.display==="none")continue;const f=d.getBoundingClientRect();if(!(f.width===0||f.height===0))try{if(d instanceof HTMLImageElement){if(!d.complete||!d.naturalWidth)continue;if(getComputedStyle(d).objectFit==="contain"&&d.naturalWidth&&d.naturalHeight){const y=this.containedRect(d.naturalWidth,d.naturalHeight,f);t.drawImage(d,y.x,y.y,y.w,y.h)}else t.drawImage(d,f.left,f.top,f.width,f.height);c++}else if(d instanceof HTMLVideoElement){if(d.readyState<2)continue;if(getComputedStyle(d).objectFit==="contain"&&d.videoWidth&&d.videoHeight){const y=this.containedRect(d.videoWidth,d.videoHeight,f);t.drawImage(d,y.x,y.y,y.w,y.h)}else t.drawImage(d,f.left,f.top,f.width,f.height);c++}else if(d instanceof HTMLCanvasElement)t.drawImage(d,f.left,f.top,f.width,f.height),c++;else if(d instanceof HTMLIFrameElement){const u=d.contentDocument;if(!(u!=null&&u.body))continue;const y=document.createElement("div");y.style.cssText=`position:fixed;left:-9999px;top:0;width:${f.width}px;height:${f.height}px;overflow:hidden;`;const m=[];for(const b of u.querySelectorAll("style"))y.appendChild(b.cloneNode(!0));for(const b of u.querySelectorAll('link[rel="stylesheet"]')){const k=document.createElement("link");k.rel="stylesheet",k.href=new URL(b.getAttribute("href")||"",u.baseURI).href,y.appendChild(k),m.push(new Promise(D=>{k.onload=()=>D(),k.onerror=()=>D()}))}y.appendChild(u.body.cloneNode(!0)),document.body.appendChild(y);const L=u.querySelectorAll("img"),w=new Map;L.forEach((b,k)=>{b.naturalWidth&&b.naturalHeight&&w.set(String(k),{nw:b.naturalWidth,nh:b.naturalHeight})}),m.length>0&&await Promise.race([Promise.all(m),new Promise(b=>setTimeout(b,500))]);const _=await this._html2canvasMod(y,{useCORS:!0,allowTaint:!0,logging:!1,backgroundColor:null,width:f.width,height:f.height,onclone:b=>{const k=b.createElement("style");k.textContent="*, *::before, *::after { animation: none !important; transition: none !important; opacity: 1 !important; }",b.head.appendChild(k),b.querySelectorAll("img").forEach((E,le)=>{var j,K;const W=(j=b.defaultView)==null?void 0:j.getComputedStyle(E);if(!W||W.objectFit!=="contain")return;const N=w.get(String(le));if(!N)return;const O=E.clientWidth||parseFloat(W.width)||0,A=E.clientHeight||parseFloat(W.height)||0;if(!O||!A)return;const q=N.nw/N.nh,ce=O/A;let H,B;q>ce?(H=O,B=O/q):(B=A,H=A*q);const z=b.createElement("div");z.style.cssText=`width:${O}px;height:${A}px;display:flex;align-items:center;justify-content:center;overflow:hidden;`,E.style.objectFit="fill",E.style.width=`${H}px`,E.style.height=`${B}px`,(K=E.parentNode)==null||K.insertBefore(z,E),z.appendChild(E)})}});document.body.removeChild(y),t.drawImage(_,f.left,f.top,f.width,f.height),c++}}catch(u){l.warn("Screenshot: failed to draw element",d.tagName,u)}}return l.debug(`Screenshot: composed ${c}/${a.length} elements`),e.toDataURL("image/jpeg",.8).split(",")[1]}containedRect(e,t,i){const o=e/t,r=i.width/i.height;let n,s;return o>r?(n=i.width,s=i.width/o):(s=i.height,n=i.height*o),{x:i.left+(i.width-n)/2,y:i.top+(i.height-s)/2,w:n,h:s}}startScreenshotInterval(){var i;const e=((i=this.displaySettings)==null?void 0:i.getSetting("screenshotInterval"))||0;if(!e||e<=0)return;this._html2canvasMod||I(()=>import("./html2canvas.esm-CBrSDip1.js"),[],import.meta.url).then(o=>{this._html2canvasMod=o.default});const t=e*1e3;l.info(`Starting periodic screenshots every ${e}s`),this._screenshotInterval=setInterval(()=>{this.captureAndSubmitScreenshot()},t)}updateStatus(e,t="info"){const i=document.getElementById("status");i&&(i.textContent=e,i.className=`status status-${t}`),t==="error"?l.error("Status:",e):l.info("Status:",e)}showOfflineIndicator(){if(document.getElementById("offline-indicator"))return;const e=document.createElement("div");e.id="offline-indicator",e.textContent="OFFLINE",e.style.cssText="position:fixed;top:8px;right:8px;background:rgba(255,60,60,0.85);color:#fff;padding:4px 12px;border-radius:4px;font-size:12px;font-weight:bold;z-index:99999;pointer-events:none;",document.body.appendChild(e)}removeOfflineIndicator(){var e;(e=document.getElementById("offline-indicator"))==null||e.remove()}cleanup(){this.core.cleanup(),this.renderer.cleanup(),this._screenshotInterval&&(clearInterval(this._screenshotInterval),this._screenshotInterval=null),this._wakeLock&&(this._wakeLock.release(),this._wakeLock=null),this.downloadOverlay&&this.downloadOverlay.destroy()}}if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",()=>{const p=new ne;p.init().catch(e=>{l.error("Failed to initialize:",e)}),window.addEventListener("beforeunload",()=>{p.cleanup()})});else{const p=new ne;p.init().catch(e=>{l.error("Failed to initialize:",e)}),window.addEventListener("beforeunload",()=>{p.cleanup()})}
27
+ //# sourceMappingURL=main-C4ABDfkq.js.map