
const PERM_LIVEVIDEO =(1<<0);
const PERM_FILES =(1<<2);
const PERM_FILEDEL =(1<<3);
const PERM_CAMCONTROL=(1<<6);
const PERM_SCHED =(1<<7);
const PERM_PTZSET =(1<<8);
const PERM_AUDIORCV =(1<<9);
const PERM_TRIGGER =(1<<10);
const PERM_AUDIOSND =(1<<11);
function getURLParameter(name){
return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search)||[,""])[1].replace(/\+/g,'%20'))||null
}
var allCookies ={
getItem: function(sKey){
if(!sKey || !this.hasItem(sKey)){return null;}
return decodeURIComponent(document.cookie.replace(new RegExp("(?:^|.*;\\s*)" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g,"\\$&")+ "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*"),"$1"));
},
setItem: function(sKey,sValue,vEnd,sPath,sDomain,bSecure){
if(!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)){return;}
var sExpires = "";
if(vEnd){
switch(vEnd.constructor){
case Number:
sExpires = vEnd === Infinity ? "; expires=Tue, 19 Jan 2038 03:14:07 GMT" : "; max-age=" + vEnd;
break;
case String:
sExpires = "; expires=" + vEnd;
break;
case Date:
sExpires = "; expires=" + vEnd.toGMTString();
break;
}
}
document.cookie = encodeURIComponent(sKey)+ "=" + encodeURIComponent(sValue)+ sExpires + "; SameSite=Strict" +(sDomain ? "; domain=" + sDomain : "")+(sPath ? "; path=" + sPath : "")+(bSecure ? "; secure" : "");
},
removeItem: function(sKey,sPath){
if(!sKey || !this.hasItem(sKey)){return;}
document.cookie = encodeURIComponent(sKey)+ "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" +(sPath ? "; path=" + sPath : "");
},
hasItem: function(sKey){
return(new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g,"\\$&")+ "\\s*\\=")).test(document.cookie);
}
};
function ToInt16(i0,i1){
return(i0<<8)|i1;
}
function ToInt32(i0,i1,i2,i3){
return(i0<<24)|(i1<<16)|(i2<<8)|i3;
}
function ToInt64(i0,i1,i2,i3,i4,i5,i6,i7){
return(i0<<56)||(i1<<48)|(i2<<40)|(i3<<32)|(i4<<24)|(i5<<16)|(i6<<8)|i7;
}
function IsValidEmail(str){
let emailList = str.split(',').map(email => email.trim());
for(let email of emailList){
if(!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email)){
return false;
}
}
return emailList.some(email => email !== '');
}
function AppendByteArray(arr,arrToAppend){
var tmp = new Uint8Array((arr.byteLength|0)+(arrToAppend.byteLength|0));
tmp.set(arr,0);
tmp.set(arrToAppend,arr.byteLength|0);
return tmp;
}
function AbsDiff(a,b){
return Math.abs(a-b);
}
function SelectedName(sel){
return sel.options[sel.selectedIndex].textContent;
}
function SelectedCount(sel){
let count=0;
for(opt of sel.options)if(opt.selected)count++;
return count;
}
function SelectValueIsValid(sel,value){
for(let i=0;i<sel.options.length;i++){
if(sel.options[i].value==value){
return true;
}
}
return false;
}
function OptionWithValue(sel,value){
for(let i=0;i<sel.options.length;i++){
if(sel.options[i].value==value){
return sel.options[i];
}
}
return false;
}
function RemoveSelectOptionByValue(selId,optVal){
let sel=document.getElementById(selId);
for(let opt of sel.options)if(opt.value==optVal){opt.remove();break;}
}
function GetFormKeyValueString(form){
const formData=new FormData(form);
let str='formData';
for(let pair of formData.entries())str+='&'+pair[0]+'='+encodeURIComponent(pair[1]);
for(let c of form.querySelectorAll('input[type="checkbox"]'))str+='&'+c.id+'='+(c.checked?"1":"0");
return str;
}
function elementNamed(n){
return document.getElementsByName(n)[0];
}
function PositionElement(e,r){
e.style.left=r.x+'px';
e.style.top=r.y+'px';
e.style.width=r.w+'px';
e.style.height=r.h+'px';
}
function IsPointInRect(p,r){
if(p.x>=r.x && p.x<r.x+r.w && p.y>=r.y && p.y<r.y+r.h)return true;
return false;
}
function AddElement(parent,type,cssText){
e=document.createElement(type);
if(cssText)e.style.cssText=cssText;
parent.appendChild(e);
return e;
}
function LoginLoad(){
DetectDarkMode();
window.matchMedia('(prefers-color-scheme: dark)').addListener(event=>DetectDarkMode());
allCookies.setItem('detectedLanguage',BrowserLanguageCode(),Infinity);
allCookies.setItem('cookieTest','1',600);
let param=getURLParameter('feedback');
let feedbackDiv=document.getElementById('feedbackDiv');
if(param && param.length){
feedbackDiv.innerHTML=param;
feedbackDiv.style.display='block';
}else if(!document.cookie.includes("cookieTest=")){
feedbackDiv.innerHTML=Loc("Please enable cookies");
feedbackDiv.style.display='block';
}
}
function IndexLoad(){
DetectDarkMode();
window.matchMedia('(prefers-color-scheme: dark)').addListener(event=>DetectDarkMode());
if(_isSettingsWindow){
_which='set';
document.getElementById('brandTableWrapper').style.display='none';
document.getElementById('sidebarBoxWrapper').style.display='none';
}else{
allCookies.setItem('detectedLanguage',BrowserLanguageCode(),Infinity);
if(!_isAdmin){RemoveSelectOptionByValue('mainMenu','set');RemoveSelectOptionByValue('mainMenu','dash');}
if(!_isScreen)RemoveSelectOptionByValue('mainMenu','screen');
if(!_isAuth){RemoveSelectOptionByValue('mainMenu','separator');RemoveSelectOptionByValue('mainMenu','logout');}
if(document.getElementById('ssCamSelect').options.length<2)RemoveSelectOptionByValue('mainMenu','sched');
let dateParts=(new Intl.DateTimeFormat()).formatToParts(new Date());
_dateFormat=dateParts.map(part=>{
switch(part.type){
case 'day':return 'DD';
case 'month':return 'MM';
case 'year':return 'YYYY';
default:return '/';
}
}).join('');
if(getURLParameter('section'))_which=getURLParameter('section');
else _which=allCookies.getItem('selectedSection');
if(!SelectValueIsValid(document.getElementById('mainMenu'),_which))_which=null;
if(allCookies.getItem('sidebar')=='0')ShowHideSidebar(false);
if(_which==null){
_which='live';
allCookies.setItem('selectedSection',_which,Infinity);
}
}
if(getURLParameter('setTab'))allCookies.setItem('sgWhich',getURLParameter('setTab'),600);
document.getElementById('mainMenu').value=_which;
ShowHideContent();
}
function SectionChange(){
let sel=document.getElementById('mainMenu');
if(sel.value=='sched'){
ScheduleSetterPanelShow();
sel.value=_which;
return;
}else if(sel.value=='opt'){
WebOptionsPanelShow();
sel.value=_which;
return;
}
let prev=_which;
_which=sel.value;
if(_which=='logout'){
window.location='logout';
}else{
allCookies.setItem('selectedSection',_which,Infinity);
ShowHideContent();
}
}
function ShowHideContent(){
if(_whichPrev=='live')LiveUnload();
else if(_whichPrev=='cap')CapUnload();
else if(_whichPrev=='set')SgUnload();
else if(_whichPrev=='dash')DashUnload();
else if(_whichPrev=='screen')ScreenUnload();
if(_whichPrev=='set')window.location='/';
else{
if(_which=='live')LiveLoad();
else if(_which=='cap')CapLoad();
else if(_which=='set')SetLoad();
else if(_which=='dash')DashLoad();
else if(_which=='screen')ScreenLoad();
_whichPrev=_which;
}
}
function ShowHideSidebar(adjustContent){
let cc=document.getElementById('contentContainer');
let sc=document.getElementById('sidebarContainer');
let div=document.getElementById('showSidebarDiv');
if(_sidebarIsShown){
if(!sc.dataset.w)sc.dataset.w=sc.offsetWidth;
sc.style.display='none';
div.style.display='block';
cc.style.left='0';
cc.style.width='100vw';
_sidebarIsShown=0;
}else{
sc.style.display='flex';
div.style.display='none';
cc.style.left=sc.dataset.w+'px';
cc.style.width='calc(100vw - '+sc.dataset.w+'px)';
_sidebarIsShown=1;
}
if(adjustContent){
if(_which=='live')LiveConfigurePlayers();
else if(_which='cap')CapResizeFileListItems();
else if(_which=='dash')DashGenerate();
}
allCookies.setItem('sidebar',_sidebarIsShown,Infinity);
}
function WebOptionsPanelShow(){
if(!('MediaSource' in window)){
allCookies.setItem('liveFormat','0');
document.querySelector('#liveFormat option[value="1"]').disabled=true;
}
if(allCookies.hasItem('userLanguage'))document.getElementById('userLanguage').value=allCookies.getItem('userLanguage');
if(allCookies.hasItem('liveFormat'))document.getElementById('liveFormat').value=allCookies.getItem('liveFormat');
if(allCookies.hasItem('liveCropMode'))document.getElementById('liveCropMode').value=allCookies.getItem('liveCropMode');
if(allCookies.hasItem('liveFps'))document.getElementById('liveFps').value=parseInt(allCookies.getItem('liveFps'))?parseInt(allCookies.getItem('liveFps')):'';
if(allCookies.hasItem('liveExpand'))document.getElementById('liveExpand').value=allCookies.getItem('liveExpand');
document.getElementById('liveCamInfo').checked=allCookies.hasItem('liveCamInfo')?parseInt(allCookies.getItem('liveCamInfo'))?true:false:true;
if(allCookies.hasItem('liveHiRes'))document.getElementById('liveHiRes').checked=parseInt(allCookies.getItem('liveHiRes'))?true:false;
if(allCookies.hasItem('capLayout'))document.getElementById('capLayout').value=allCookies.getItem('capLayout');
if(allCookies.hasItem('capSize'))document.getElementById('capSize').value=allCookies.getItem('capSize');
if(allCookies.hasItem('timeFormat'))document.getElementById('timeFormat').value=allCookies.getItem('timeFormat');
else document.getElementById('timeFormat').value=DefaultTimeFormat();
document.getElementById('webOptions').style.display='flex';
}
function WebOptionsPanelHide(){
let oldLanguage=allCookies.getItem('userLanguage');
allCookies.setItem('userLanguage',document.getElementById('userLanguage').value,Infinity);
allCookies.setItem('liveFormat',document.getElementById('liveFormat').value,Infinity);
allCookies.setItem('liveCropMode',document.getElementById('liveCropMode').value,Infinity);
allCookies.setItem('liveFps',document.getElementById('liveFps').value,Infinity);
allCookies.setItem('liveExpand',document.getElementById('liveExpand').value,Infinity);
allCookies.setItem('liveCamInfo',document.getElementById('liveCamInfo').checked?1:0,Infinity);
allCookies.setItem('liveHiRes',document.getElementById('liveHiRes').checked?1:0,Infinity);
allCookies.setItem('capLayout',document.getElementById('capLayout').value,Infinity);
allCookies.setItem('capSize',document.getElementById('capSize').value,Infinity);
allCookies.setItem('timeFormat',document.getElementById('timeFormat').value,Infinity);
document.getElementById('webOptions').style.display='none';
if(allCookies.getItem('userLanguage')!=oldLanguage)window.location='/';
else if(_which=='live')LiveConfigurePlayers();
else if(_which=='cap')CapProcessFileList();
}
function ScheduleSetterPanelShow(){
document.getElementById('scheduleSetter').style.display='flex';
}
function ScheduleSetterPanelCancel(){
document.getElementById('scheduleSetter').style.display='none';
}
function ScheduleSetterPanelApply(){
let req='ssSetSchedule?cameraNum='+document.getElementById('ssCamSelect').value+'&schedule='+document.getElementById('ssSchedSelect').value+'&override='+document.getElementById('ssOverrideSelect').value+'&mode='+(document.getElementById('ssCC').checked?'C':'')+(document.getElementById('ssMC').checked?'M':'')+(document.getElementById('ssA').checked?'A':'');
fetch(req,{credentials:'same-origin'});
document.getElementById('scheduleSetter').style.display='none';
}
function SchedulePresetApply(presetId){
fetch('ssSetPreset?id='+presetId,{credentials:'same-origin'});
document.getElementById('scheduleSetter').style.display='none';
}
let loadedScripts=[];
function LoadScript(name,callback){
if(loadedScripts.includes(name)){
if(callback)callback();
return;
}
var script=document.createElement("script");
script.type="text/javascript";
script.onload=function(){
if(callback)callback();
};
script.src='js/'+name+'.js?'+_ssVersion;
document.head.appendChild(script);
loadedScripts.push(name);
}
function IsLocalhost(){
return(window.location.hostname=='127.0.0.1' || window.location.hostname=='localhost' || window.location.hostname=='localhost.')?true:false;
}
function DetectDarkMode(){
let arr=GetRootXStyles();
let which=(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)?1:0;
for(let item of arr)document.documentElement.style.setProperty(item.name,item.value.split('|')[which]);
}
function GetRootXStyles(){
let arr=[];
for(let sheet of document.styleSheets){
try{
for(let rule of sheet.cssRules){
if(rule.selectorText === ':root'){
for(let p of rule.style){
if(p.startsWith('--')&& p.endsWith('X')){
arr.push({name:p.slice(0,-1),value:rule.style.getPropertyValue(p).trim()});
}
}
}
}
}catch(e){
continue;
}
}
return arr;
}
function OptimumGridLayout(viewWidth,viewHeight,infoHeight,cams){
if(!cams.length)return [];
let colCount=OptimumColCount(viewWidth,viewHeight,infoHeight,cams);
let rowCount=Math.ceil(cams.length/colCount);
let divThickness=1;
var array=[];
for(let y=0,i=0;y<rowCount && i<cams.length;y++)
{
for(let x=0;x<colCount && i<cams.length;x++,i++)
{
array.push({x:Math.floor(viewWidth*x/colCount),
y:Math.floor(viewHeight*y/rowCount),
w:Math.floor(viewWidth*(x+1)/colCount)- Math.floor(viewWidth*x/colCount)-((x<colCount-1)?1:0),
h:Math.floor(viewHeight*(y+1)/rowCount)- Math.floor(viewHeight*y/rowCount)-((y<rowCount-1)?1:0)});
}
}
return array;
}
function OptimumColCount(viewWidth,viewHeight,infoHeight,cams){
let colCount=1,bestArea=0;
for(let i=1;i<=cams.length;i++){
let area=PixelArea(viewWidth,viewHeight,i,infoHeight,cams);
if(area>bestArea){
colCount=i;
bestArea=area;
}
}
return colCount;
}
function PixelArea(viewWidth,viewHeight,colCount,infoHeight,cams){
let rowCount=Math.ceil(cams.length/colCount);
let eWidth=viewWidth/colCount;
let eHeight=viewHeight/rowCount-infoHeight;
let area=0;
if(eWidth>0 && eHeight>0){
for(cam of cams){
if(cam.w/cam.h>=eWidth/eHeight){
area+=eWidth*eWidth*cam.h/cam.w
}else{
area+=eHeight*eHeight*cam.w/cam.h;
}
}
area+=eWidth*eHeight*0.05;
}
return area;
}
function RandomCharStr(n){
const chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result='';
for(let i=0;i<n;i++)result+=chars[Math.floor(Math.random()*chars.length)];
return result;
}
function CheckCodecSupport(codec){
let video=document.createElement('video');
return video.canPlayType(codec)?true:false;
}
function CamFromNum(n){
for(let i=0;i<_cams.length;i++)if(_cams[i].num==n)return _cams[i];
}
function SelectedRadioByName(name){
const radioInputs=document.querySelectorAll('input[name="'+name+'"]');
for(const input of radioInputs)if(input.checked)return input;
return null;
}
function SetRadioGroupValue(name,value){
const radioInputs=document.querySelectorAll('input[name="'+name+'"]');
for(const input of radioInputs)input.checked=(input.value===value);
}
function OpenUrlInNewTab(url){
if(_isSettingsWindow)fetch('openUrl?url='+encodeURIComponent(url),{credentials:'same-origin'});
else window.open(url,'_blank');
}
function SizeStr(mb){
if(mb>=1000000)return(mb/1000000).toFixed(1)+' TB';
if(mb>=1000)return(mb/1000).toFixed(1)+' GB';
if(mb>=0.1)return mb.toFixed(1)+' MB';
return parseInt(mb*1000)+' KB';
}
function AlignElementsLeft(e1,e2){
if(e1.offsetLeft <= e2.offsetLeft){
e1.style.marginLeft = '';
let existingMargin = parseInt(window.getComputedStyle(e1).marginLeft);
let diff = e2.offsetLeft - e1.offsetLeft;
e1.style.marginLeft = existingMargin + diff + 'px';
}else{
e2.style.marginLeft = '';
let existingMargin = parseInt(window.getComputedStyle(e2).marginLeft);
let diff = e1.offsetLeft - e2.offsetLeft;
e2.style.marginLeft = existingMargin + diff + 'px';
}
}
function UTF8StrToB64(str){
var utf8Bytes = new TextEncoder().encode(str);
var binaryStr = Array.from(utf8Bytes).map(byte => String.fromCharCode(byte)).join('');
return btoa(binaryStr);
}
function escapeStringForJSON(str){
return str
.replace(/\\/g,'\\\\')
.replace(/"/g, '\\"') // Escape double quotes
	.replace(/\n/g, '\\n') // Escape newlines
	.replace(/\r/g, '\\r') // Escape carriage returns
	.replace(/\t/g, '\\t'); // Escape tabs
}
function SetSliderVal(element,val) {
	element.value=val;
	
	var event = new Event('input', {
		bubbles: true,
		cancelable: true
	});
	
	element.dispatchEvent(event);
}
function TimeStr(seconds,is12) { //Expects input: number of seconds since midnight
	let h=Math.floor(seconds/3600);
	let m=Math.floor((seconds/60)%60);
	let s=Math.floor(seconds%60);
	let period='';
	let hStr='';
	
	if (is12) {
		period=(h<12)?' AM':' PM';
		h=h%12;
		h=h?h:12;
		hStr=h.toString();
	} else {
		hStr=h.toString().padStart(2,'0');
	}
	
	let mStr=m.toString().padStart(2,'0');
	let sStr=s.toString().padStart(2,'0');
	
	return `${hStr}:${mStr}:${sStr}${period}`;
}
function DateStr(str) { //Expect input 'YYYY-MM-DD'
	let [year,month,day]=str.split("-").map(Number);
	let fileDate=new Date(year,month-1,day);
	
	let today=new Date();
	today.setHours(0,0,0,0);  //Set to midnight
	
	let yesterday=new Date(today);
	yesterday.setDate(yesterday.getDate()-1);
	
	if (fileDate.getTime()===today.getTime()) return Loc('Today');
	if (fileDate.getTime()===yesterday.getTime()) return Loc('Yesterday');
	
	return fileDate.toLocaleDateString();
}
function DefaultTimeFormat() {
	let formatter=new Intl.DateTimeFormat(undefined,{hour:'numeric'});
	return formatter.resolvedOptions().hour12?1:0;
}
function MakeEditable(field) { //Workaround to prevent browser autofill
	if (field.hasAttribute('onfocus')) field.removeAttribute('onfocus');
	if (field.hasAttribute('readonly')) field.removeAttribute('readonly');
	field.blur();
	field.focus();
}
function DisableTextInputHelpers() {
	let textElements=document.querySelectorAll('input[type="text"],textarea');
	
	textElements.forEach(element => {
		element.setAttribute('autocomplete','off');
		element.setAttribute('autocorrect','off');
		element.setAttribute('spellcheck','false');
		element.setAttribute('autocapitalize','off');
	});
}
function OSMeetsRequired(min) { //"min" can be "11.3.5" or "11.3" or just "11"
	let currentVersion=_osVersion.split('.').map(Number);
	let minVersion=min.split('.').map(Number);
	
	for (let i=0;i<Math.max(currentVersion.length,minVersion.length);i++) {
		let currentComponent=currentVersion[i]||0;
		let minComponent=minVersion[i]||0;
		
		if (currentComponent>minComponent) return true;
		if (currentComponent<minComponent) return false;
	}
	
	return true;
}
function FixTimeElements() {
	const input=document.createElement('input');
	input.setAttribute('type','time');
	if (input.type==='time') return; //Native support
	
	document.querySelectorAll('input[type="time"]').forEach(input=>{
		// Restrict to numbers and colon
		input.addEventListener('keypress',(e)=>{
			if (!/[0-9:]/.test(e.key)) e.preventDefault();
		});
		
		// Basic format validation on blur
		input.addEventListener('blur',(e)=>{
			const value=e.target.value;
			const timePattern=/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/;
			
			if (value && !timePattern.test(value)) e.target.value='';
		});
		
		// Add placeholder
		input.placeholder='HH:MM';
		input.pattern='[0-2][0-9]:[0-5][0-9]';
	});
}
function InitNumericInputs() {
	document.querySelectorAll('[data-numeric]').forEach(function(input) {
		var type=input.getAttribute('data-numeric');
		
		input.addEventListener('input',function(e) {
			var value=this.value;
			
			if (type==='+int') {
				this.value=value.replace(/[^0-9]/g,'');
			} else if (type==='int') {
				this.value=value.replace(/[^0-9-]/g,'').replace(/(?!^)-/g,'');
			} else if (type==='+float') {
				value=value.replace(/[^0-9.]/g,'');
				var parts=value.split('.');
				if (parts.length>2) value=parts[0]+'.'+parts.slice(1).join('');
				this.value=value;
			} else if (type==='float') {
				value=value.replace(/[^0-9.-]/g,'');
				value=value.replace(/(?!^)-/g,'');
				var parts=value.split('.');
				if (parts.length>2) value=parts[0]+'.'+parts.slice(1).join('');
this.value=value;
}
});
});
}
