From 6ee8c91fc17b5085f0c815c96ffe1c720df56404 Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Fri, 31 May 2024 20:05:05 +0300 Subject: [PATCH 01/22] for
  • assign ID (watch.php) --- web/skins/classic/views/watch.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/watch.php b/web/skins/classic/views/watch.php index 34dbba457..dfda55b63 100644 --- a/web/skins/classic/views/watch.php +++ b/web/skins/classic/views/watch.php @@ -320,7 +320,7 @@ echo htmlSelect('cyclePeriod', $cyclePeriodOptions, $period, array('id'=>'cycleP From 6f7728bf1eae68804483b8ed3b5da751bfb9d73c Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Fri, 31 May 2024 20:11:43 +0300 Subject: [PATCH 02/22] Update watch.js --- web/skins/classic/views/js/watch.js | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 16e2d5d8a..203837e60 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -1190,7 +1190,33 @@ function cycleNext() { } clearInterval(intervalId); monitorStream.kill(); - window.location.replace('?view=watch&cycle='+cycle+'&mid='+monitorData[monIdx].id+'&mode='+mode); + + // +++ Старт следующего монитора + monitorStream = new MonitorStream(monitorData[monIdx]); + const img = document.getElementById('liveStream'+monitorData[monIdx-1].id); + const src = img.src; + if (src) { + const url = new URL(src); + url.searchParams.set('monitor', monitorData[monIdx].id); + url.searchParams.delete('connkey'); + url.searchParams.set('mode', 'single'); + img.src = ''; + img.src = url; + img.id = 'liveStream'+monitorData[monIdx].id; + } else { + // Пока х.з. что делать.... + } + + if (!monitorStream.started) { + monitorStream.start(); + } + + cycleStart(); + //Изменим активный элемент + document.getElementById('nav-item-cycle'+monitorData[monIdx-1].id).querySelector('a').classList.remove("active"); + document.getElementById('nav-item-cycle'+monitorData[monIdx].id).querySelector('a').classList.add("active"); + // --- Старт следующего монитора + //window.location.replace('?view=watch&cycle='+cycle+'&mid='+monitorData[monIdx].id+'&mode='+mode); } function cyclePrev() { From 408a7ac5caefef0186d9e8ced22f128146c5c6bf Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Tue, 4 Jun 2024 14:51:01 +0300 Subject: [PATCH 03/22] Update watch.js --- web/skins/classic/views/js/watch.js | 317 ++++++++++++++++------------ 1 file changed, 187 insertions(+), 130 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 675ae42f0..b81a3e99b 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -620,9 +620,16 @@ function fetchImage(streamImage) { } function handleClick(event) { + const targetId = event.target.id; + if (targetId.indexOf("nav-link") >= 0) { //Navigation through monitors + cycleStop(event.target); + const oldId = stringToNumber(document.querySelector('[id ^= "liveStream"]').id); + const newId = stringToNumber(targetId); + streamReStart(oldId, newId); + } if (panZoomEnabled) { //event.preventDefault(); - if (event.target.id) { + if (targetId) { //We are looking for an object with an ID, because there may be another element in the button. var obj = event.target; } else { @@ -909,92 +916,7 @@ function controlSetClicked() { } } -function streamStart() { - monitorStream = new MonitorStream(monitorData[monIdx]); - monitorStream.setBottomElement(document.getElementById('dvrControls')); - - // Start the fps and status updates. give a random delay so that we don't assault the server - //monitorStream.setScale($j('#scale').val(), $j('#width').val(), $j('#height').val()); - monitorsSetScale(monitorId); - monitorStream.start(); - if (streamMode == 'single') { - monitorStream.setup_onclick(fetchImage); - } else { - monitorStream.setup_onclick(handleClick); - monitorStream.setup_onmove(handleMove); - } - monitorStream.setup_onpause(onPause); - monitorStream.setup_onplay(onPlay); - monitorStream.setup_onalarm(refresh_events_table); - - monitorStream.setButton('enableAlarmButton', enableAlmBtn); - monitorStream.setButton('forceAlarmButton', forceAlmBtn); - monitorStream.setButton('zoomOutButton', $j('zoomOutBtn')); - if (canEdit.Monitors) { - // Will be enabled by streamStatus ajax - enableAlmBtn.on('click', cmdAlarm); - forceAlmBtn.on('click', cmdForce); - } else { - forceAlmBtn.prop('title', forceAlmBtn.prop('title') + ': disabled because cannot edit Monitors'); - enableAlmBtn.prop('title', enableAlmBtn.prop('title') + ': disabled because cannot edit Monitors'); - } - - /* - if (streamMode == 'single') { - statusCmdTimer = setTimeout(statusCmdQuery, 200); - setInterval(watchdogCheck, statusRefreshTimeout*2, 'status'); - } else { - streamCmdTimer = setTimeout(streamCmdQuery, 200); - setInterval(watchdogCheck, statusRefreshTimeout*2, 'stream'); - } - if (canStream || (streamMode == 'single')) { - var streamImg = $j('#imageFeed img'); - if (!streamImg) streamImg = $j('#imageFeed object'); - if (!streamImg) { - console.error('No streamImg found for imageFeed'); - } else { - if (streamMode == 'single') { - streamImg.click(streamImg, fetchImage); - setInterval(fetchImage, imageRefreshTimeout, $j('#imageFeed img')); - } else { - streamImg.click(function(event) { - handleClick(event); - }); - streamImg.on("error", function(thing) { - console.log("Error loading image"); - console.log(thing); - setInterval(fetchImage, 100, $j('#imageFeed img')); - }); - } - } // end if have streamImg - } // streamMode native or single - */ -} - -function initPage() { -// +++ Support of old ZoomPan algorithm - var useOldZoomPan = getCookie('zmUseOldZoomPan'); - const btnZoomOutBtn = document.getElementById('zoomOutBtn'); //Zoom out button below Frame. She may not - if (useOldZoomPan) { - panZoomEnabled = false; - if (btnZoomOutBtn) { - btnZoomOutBtn.classList.remove("hidden"); - } - } else { - if (btnZoomOutBtn) { - btnZoomOutBtn.classList.add("hidden"); - } - } - $j("#use-old-zoom-pan").click(function() { - useOldZoomPan = this.checked; - setCookie('zmUseOldZoomPan', this.checked); - location.reload(); - }); - document.getElementById('use-old-zoom-pan').checked = useOldZoomPan; - // --- Support of old ZoomPan algorithm - - zmPanZoom.init(); - +function streamPrepareStart(monitor=null) { if (canView.Control) { // Load the settings modal into the DOM if (monitorType == 'Local') getSettingsModal(); @@ -1011,7 +933,7 @@ function initPage() { } if ((monitorType != 'WebSite') && monitorData.length) { - streamStart(); + streamStart(monitor); if (window.history.length == 1) { $j('#closeControl').html(''); } @@ -1052,6 +974,149 @@ function initPage() { setInterval(reloadWebSite, monitorRefresh*1000); } + // Manage the generate Edit button + bindButton('#editBtn', 'click', null, function onEditClick(evt) { + evt.preventDefault(); + window.location.assign("?view=monitor&mid="+monitorId); + }); + + const el = document.querySelector('.imageFeed'); + el.addEventListener('mouseenter', handleMouseEnter); + el.addEventListener('mouseleave', handleMouseLeave); +} + +function handleMouseEnter(event) { + //Displaying "Scale" and other buttons at the top of the monitor image + const id = stringToNumber(this.id); + $j('#button_zoom' + id).stop(true, true).slideDown('fast'); +} + +function handleMouseLeave(event) { + const id = stringToNumber(this.id); + $j('#button_zoom' + id).stop(true, true).slideUp('fast'); +} + +function streamStart(monitor = null) { + if (monitor) { + monitorStream = new MonitorStream(monitor); + } else { + monitorStream = new MonitorStream(monitorData[monIdx]); + } + monitorStream.setBottomElement(document.getElementById('dvrControls')); + // Start the fps and status updates. give a random delay so that we don't assault the server + //monitorStream.setScale($j('#scale').val(), $j('#width').val(), $j('#height').val()); + monitorsSetScale(monitorId); + monitorStream.start(); + if (streamMode == 'single') { + monitorStream.setup_onclick(fetchImage); + } else { + monitorStream.setup_onclick(handleClick); + monitorStream.setup_onmove(handleMove); + } + monitorStream.setup_onpause(onPause); + monitorStream.setup_onplay(onPlay); + monitorStream.setup_onalarm(refresh_events_table); + + monitorStream.setButton('enableAlarmButton', enableAlmBtn); + monitorStream.setButton('forceAlarmButton', forceAlmBtn); + monitorStream.setButton('zoomOutButton', $j('zoomOutBtn')); + if (canEdit.Monitors) { + // Will be enabled by streamStatus ajax + enableAlmBtn.on('click', cmdAlarm); + forceAlmBtn.on('click', cmdForce); + } else { + forceAlmBtn.prop('title', forceAlmBtn.prop('title') + ': disabled because cannot edit Monitors'); + enableAlmBtn.prop('title', enableAlmBtn.prop('title') + ': disabled because cannot edit Monitors'); + } +} + +function streamReStart(oldId, newId) { + const el = document.querySelector('.imageFeed'); + const newMonitorName = document.getElementById('nav-item-cycle'+newId).querySelector('a').textContent; + const currentMonitor = monitorData.find((o) => { + return parseInt(o["id"]) === newId; + }); + monitorId = newId; + document.querySelector('title').textContent = newMonitorName; + + zmPanZoom.action('disable', {id: oldId}); + monitorStream.kill(); + el.removeEventListener('mouseenter', handleMouseEnter); + el.removeEventListener('mouseleave', handleMouseLeave); + + //Change main monitor block + document.getElementById('monitor').innerHTML = currentMonitor.streamHTML; + + //Change active element of the navigation menu + document.getElementById('nav-item-cycle'+oldId).querySelector('a').classList.remove("active"); + document.getElementById('nav-item-cycle'+newId).querySelector('a').classList.add("active"); + + //Set global variables from the current monitor + monitorWidth = currentMonitor.monitorWidth; + monitorHeight = currentMonitor.monitorHeight; + monitorType = currentMonitor.monitorType; + monitorRefresh = currentMonitor.monitorRefresh; + monitorStreamReplayBuffer = currentMonitor.monitorStreamReplayBuffer; + monitorControllable = currentMonitor.monitorControllable; + streamMode = currentMonitor.streamMode; + + table.bootstrapTable('destroy'); + streamPrepareStart(currentMonitor); + applyMonitorControllable(currentMonitor); + zmPanZoom.init(); + loadFontFaceObserver(); + document.getElementById('monitor').classList.remove('hidden-shift'); +} + +function applyMonitorControllable(currentMonitor) { + if (currentMonitor.monitorControllable) { + const ptzShow = getCookie('ptzShow'); + document.getElementById('ptzToggle').classList.remove("hidden"); + sidebarControls.html(currentMonitor.ptzControls); + if (ptzShow) { + sidebarControls.show(); + document.getElementById('ptzToggle').classList.remove("btn-secondary"); + document.getElementById('ptzToggle').classList.add("btn-primary"); + } else { + sidebarControls.hide(); + document.getElementById('ptzToggle').classList.remove("btn-primary"); + document.getElementById('ptzToggle').classList.add("btn-secondary"); + } + } else { + document.getElementById('ptzToggle').classList.add("hidden"); + sidebarControls.html(''); + sidebarControls.hide(); + } + changeObjectClass(); +} + +function initPage() { +// +++ Support of old ZoomPan algorithm + var useOldZoomPan = getCookie('zmUseOldZoomPan'); + const btnZoomOutBtn = document.getElementById('zoomOutBtn'); //Zoom out button below Frame. She may not + if (useOldZoomPan) { + panZoomEnabled = false; + if (btnZoomOutBtn) { + btnZoomOutBtn.classList.remove("hidden"); + } + } else { + if (btnZoomOutBtn) { + btnZoomOutBtn.classList.add("hidden"); + } + } + $j("#use-old-zoom-pan").click(function() { + useOldZoomPan = this.checked; + setCookie('zmUseOldZoomPan', this.checked); + location.reload(); + }); + document.getElementById('use-old-zoom-pan').checked = useOldZoomPan; + // --- Support of old ZoomPan algorithm + + zmPanZoom.init(); + + streamPrepareStart(); + applyMonitorControllable(monitorStream); + // Manage the BACK button bindButton('#backBtn', 'click', null, function onBackClick(evt) { evt.preventDefault(); @@ -1073,12 +1138,6 @@ function initPage() { $j('#settingsModal').modal('show'); }); - // Manage the generate Edit button - bindButton('#editBtn', 'click', null, function onEditClick(evt) { - evt.preventDefault(); - window.location.assign("?view=monitor&mid="+monitorId); - }); - bindButton('#cyclePlayBtn', 'click', null, cycleStart); bindButton('#cyclePauseBtn', 'click', null, cyclePause); bindButton('#cycleNextBtn', 'click', null, cycleNext); @@ -1119,17 +1178,6 @@ function initPage() { } }, 10*1000); } - $j(".imageFeed").hover( - //Displaying "Scale" and other buttons at the top of the monitor image - function() { - const id = stringToNumber(this.id); - $j('#button_zoom' + id).stop(true, true).slideDown('fast'); - }, - function() { - const id = stringToNumber(this.id); - $j('#button_zoom' + id).stop(true, true).slideUp('fast'); - } - ); setInterval(() => { //Updating Scale. When quickly scrolling the mouse wheel or quickly pressing Zoom In/Out, you should not set Scale very often. @@ -1139,6 +1187,10 @@ function initPage() { } }, 500); + document.addEventListener('click', function(event) { + handleClick(event); + }); + document.getElementById('monitor').classList.remove('hidden-shift'); changeObjectClass(); changeSize(); @@ -1188,6 +1240,12 @@ function cycleStart() { $j('#cyclePlayBtn').hide(); } +function cycleStop(target) { + monIdx = target.getAttribute('data-monIdx'); + $j('#secondsToCycle').text(''); + cyclePause(); +} + function cycleNext() { monIdx ++; if (monIdx >= monitorData.length) { @@ -1199,31 +1257,17 @@ function cycleNext() { clearInterval(intervalId); monitorStream.kill(); - // +++ Старт следующего монитора - monitorStream = new MonitorStream(monitorData[monIdx]); - const img = document.getElementById('liveStream'+monitorData[monIdx-1].id); - const src = img.src; - if (src) { - const url = new URL(src); - url.searchParams.set('monitor', monitorData[monIdx].id); - url.searchParams.delete('connkey'); - url.searchParams.set('mode', 'single'); - img.src = ''; - img.src = url; - img.id = 'liveStream'+monitorData[monIdx].id; + // +++ Start next monitor + let oldId, newId; + if (monIdx == 0) { + oldId = monitorData[monitorData.length-1].id; } else { - // Пока х.з. что делать.... + oldId = monitorData[monIdx-1].id; } - - if (!monitorStream.started) { - monitorStream.start(); - } - + newId = monitorData[monIdx].id; + streamReStart(oldId, newId); cycleStart(); - //Изменим активный элемент - document.getElementById('nav-item-cycle'+monitorData[monIdx-1].id).querySelector('a').classList.remove("active"); - document.getElementById('nav-item-cycle'+monitorData[monIdx].id).querySelector('a').classList.add("active"); - // --- Старт следующего монитора + // --- Start next monitor //window.location.replace('?view=watch&cycle='+cycle+'&mid='+monitorData[monIdx].id+'&mode='+mode); } @@ -1236,8 +1280,21 @@ function cyclePrev() { console.log('No monitorData for ' + monIdx); } clearInterval(intervalId); - monitorStream.stop(); - window.location.replace('?view=watch&cycle='+cycle+'&mid='+monitorData[monIdx].id+'&mode='+mode); + //monitorStream.stop(); + monitorStream.kill(); + + // +++ Start previous monitor + let oldId, newId; + if (monIdx == monitorData.length - 1) { + oldId = monitorData[0].id; + } else { + oldId = monitorData[monIdx+1].id; + } + newId = monitorData[monIdx].id; + streamReStart(oldId, newId); + cycleStart(); + // --- Start previous monitors + //window.location.replace('?view=watch&cycle='+cycle+'&mid='+monitorData[monIdx].id+'&mode='+mode); } function cyclePeriodChange() { @@ -1345,7 +1402,7 @@ function monitorsSetScale(id=null) { }); } //const el = document.getElementById('liveStream'+id); - if (panZoomEnabled) { + if (panZoomEnabled && zmPanZoom.panZoom[id]) { var panZoomScale = zmPanZoom.panZoom[id].getScale(); } else { var panZoomScale = 1; From da8ca3f2972e36499f517be83075c9a20206f402 Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Tue, 4 Jun 2024 15:08:36 +0300 Subject: [PATCH 04/22] Update watch.php --- web/skins/classic/views/watch.php | 66 ++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/web/skins/classic/views/watch.php b/web/skins/classic/views/watch.php index 0ee68dc3b..b8b0bbfec 100644 --- a/web/skins/classic/views/watch.php +++ b/web/skins/classic/views/watch.php @@ -103,7 +103,15 @@ if (!$cycle and isset($_COOKIE['zmCycleShow'])) { $showCycle = $_COOKIE['zmCycleShow'] == 'true'; } #Whether to show the controls button -$hasPtzControls = ( ZM_OPT_CONTROL && $monitor->Controllable() && canView('Control') && $monitor->Type() != 'WebSite' ); +$hasPtzControls = false; +foreach ($monitors as $m) { + if (( ZM_OPT_CONTROL && $m->Controllable() && canView('Control') && $m->Type() != 'WebSite' )) { + //If there is control for at least one camera, then we display the block. + $hasPtzControls = true; + } +} +//$hasPtzControls = ( ZM_OPT_CONTROL && $monitor->Controllable() && canView('Control') && $monitor->Type() != 'WebSite' ); + $showPtzControls = false; if ($hasPtzControls) { $showPtzControls = true; @@ -198,14 +206,19 @@ if ( ) { $options['scale'] = 0; } -if ($monitor->JanusEnabled()) { - $streamMode = 'janus'; -} else if ($monitor->RTSP2WebEnabled()) { - $streamMode = $monitor->RTSP2WebType(); -} else { - $streamMode = getStreamMode(); + +function getStreamModeMonitor($monitor) { + if ($monitor->JanusEnabled()) { + $streamMode = 'janus'; + } else if ($monitor->RTSP2WebEnabled()) { + $streamMode = $monitor->RTSP2WebType(); + } else { + $streamMode = getStreamMode(); + } + return $streamMode; } +$streamMode = getStreamModeMonitor($monitor); noCacheHeaders(); xhtmlHeaders(__FILE__, $monitor->Name().' - '.translate('Feed')); getBodyTopHTML(); @@ -332,8 +345,31 @@ echo htmlSelect('cyclePeriod', $cyclePeriodOptions, $period, array('id'=>'cycleP @@ -342,9 +378,6 @@ echo htmlSelect('cyclePeriod', $cyclePeriodOptions, $period, array('id'=>'cycleP
    Type() != 'WebSite') { - $options['state'] = true; -} echo $monitor->getStreamHTML($options); ?>
    @@ -409,11 +442,8 @@ if ($streamMode == 'jpeg') { Type() != 'WebSite') ) { JanusEnabled() ) { -?> - - - -RTSP2WebEnabled() and $monitor->RTSP2WebType == "HLS") { ?> From d6b7daf5dbc3647bb73f81bbc7a72c796dc824a7 Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Tue, 4 Jun 2024 15:15:28 +0300 Subject: [PATCH 05/22] Added function "loadFontFaceObserver()" skin.js Because the function code must also be called from other JS, for example watch.js --- web/skins/classic/js/skin.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index 149e3ed67..3f92441b6 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -1160,9 +1160,13 @@ function stringToNumber(str) { return parseInt(str.replace(/\D/g, '')); } -const font = new FontFaceObserver('Material Icons', {weight: 400}); -font.load().then(function() { - $j('.material-icons').css('display', 'inline-block'); -}, function() { - $j('.material-icons').css('display', 'inline-block'); -}); +function loadFontFaceObserver() { + const font = new FontFaceObserver('Material Icons', {weight: 400}); + font.load().then(function() { + $j('.material-icons').css('display', 'inline-block'); + }, function() { + $j('.material-icons').css('display', 'inline-block'); + }); +} + +loadFontFaceObserver(); From ba5cb4adcd5781c4d223e84fc8ff6120a20f4086 Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Tue, 4 Jun 2024 15:22:59 +0300 Subject: [PATCH 06/22] Added $this->streamSrc = $streamSrc (Monitor.php) Required to switch without reloading the page between monitors on the Watch page --- web/includes/Monitor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index 8a6a708b3..32119782d 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -495,7 +495,7 @@ public static function getStatuses() { unset($args['zones']); $streamSrc .= '?'.http_build_query($args, '', $querySep); - + $this->streamSrc = $streamSrc; return $streamSrc; } // end function getStreamSrc From 92903c6a1009e2970a79e3bf55817ce738ca2564 Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Tue, 4 Jun 2024 15:26:14 +0300 Subject: [PATCH 07/22] =?UTF-8?q?Added=20more=20values=20=E2=80=8B?= =?UTF-8?q?=E2=80=8Bto=20"monitorData"=20(watch.js.php)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/skins/classic/views/js/watch.js.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/watch.js.php b/web/skins/classic/views/js/watch.js.php index 8142cf066..f2154ac8b 100644 --- a/web/skins/classic/views/js/watch.js.php +++ b/web/skins/classic/views/js/watch.js.php @@ -3,6 +3,7 @@ global $nextMid; global $options; global $monitors; + global $monitorsExtraData; global $streamMode; global $showPtzControls; global $monitor; @@ -53,7 +54,16 @@ monitorData[monitorData.length] = { 'onclick': function(){window.location.assign( '?view=watch&mid=Id() ?>' );}, 'type': 'Type() ?>', 'refresh': 'Refresh() ?>', - 'janus_pin': 'Janus_Pin() ?>' + 'janus_pin': 'Janus_Pin() ?>', + 'streamHTML': 'Id()]['StreamHTML']) ?>', + 'ptzControls': 'Id()]['ptzControls']) ?>', + 'monitorWidth': parseInt('ViewWidth() ?>'), + 'monitorHeight': parseInt('ViewHeight() ?>'), + 'monitorType': 'Type() ?>', + 'monitorRefresh': 'Refresh() ?>', + 'monitorStreamReplayBuffer': parseInt('StreamReplayBuffer() ?>'), + 'monitorControllable': Controllable()?'true':'false' ?>, + 'streamMode': '' }; Date: Tue, 4 Jun 2024 15:28:54 +0300 Subject: [PATCH 08/22] .hidden, .hidden-shift, .invisible should be at the end of the file (skin.css) --- web/skins/classic/css/base/skin.css | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/web/skins/classic/css/base/skin.css b/web/skins/classic/css/base/skin.css index 3e2a389c6..122073b54 100644 --- a/web/skins/classic/css/base/skin.css +++ b/web/skins/classic/css/base/skin.css @@ -469,18 +469,6 @@ body.sticky #content { * Generic useful classes, especially with mootools */ -.hidden { - display: none; -} - -.hidden-shift { - position: absolute !important; left: -999em !important; -} - -.invisible { - visibility: hidden; -} - .nowrap { white-space: nowrap; } @@ -1124,3 +1112,18 @@ html::-webkit-scrollbar-thumb, div::-webkit-scrollbar-thumb, nav::-webkit-scroll width: 100%; } } + +/* +++ This block should always be located at the end! */ +.hidden { + display: none; +} + +.hidden-shift { + position: absolute !important; left: -999em !important; +} + +.invisible { + visibility: hidden; +} +/* --- This block should always be located at the end! */ + From 28414275522747aa733f5663703256240a4b3606 Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Tue, 4 Jun 2024 15:39:57 +0300 Subject: [PATCH 09/22] Chore: eslint (watch.js) --- web/skins/classic/views/js/watch.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index b81a3e99b..29d3c086a 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -1002,7 +1002,7 @@ function streamStart(monitor = null) { } else { monitorStream = new MonitorStream(monitorData[monIdx]); } - monitorStream.setBottomElement(document.getElementById('dvrControls')); + monitorStream.setBottomElement(document.getElementById('dvrControls')); // Start the fps and status updates. give a random delay so that we don't assault the server //monitorStream.setScale($j('#scale').val(), $j('#width').val(), $j('#height').val()); monitorsSetScale(monitorId); @@ -1046,7 +1046,7 @@ function streamReStart(oldId, newId) { //Change main monitor block document.getElementById('monitor').innerHTML = currentMonitor.streamHTML; - + //Change active element of the navigation menu document.getElementById('nav-item-cycle'+oldId).querySelector('a').classList.remove("active"); document.getElementById('nav-item-cycle'+newId).querySelector('a').classList.add("active"); @@ -1258,13 +1258,13 @@ function cycleNext() { monitorStream.kill(); // +++ Start next monitor - let oldId, newId; + let oldId; if (monIdx == 0) { oldId = monitorData[monitorData.length-1].id; } else { oldId = monitorData[monIdx-1].id; } - newId = monitorData[monIdx].id; + const newId = monitorData[monIdx].id; streamReStart(oldId, newId); cycleStart(); // --- Start next monitor @@ -1282,15 +1282,15 @@ function cyclePrev() { clearInterval(intervalId); //monitorStream.stop(); monitorStream.kill(); - + // +++ Start previous monitor - let oldId, newId; + let oldId; if (monIdx == monitorData.length - 1) { oldId = monitorData[0].id; } else { oldId = monitorData[monIdx+1].id; } - newId = monitorData[monIdx].id; + const newId = monitorData[monIdx].id; streamReStart(oldId, newId); cycleStart(); // --- Start previous monitors From ec824469fb7da65939c0394b11ca27fdc3b2a8f8 Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Wed, 5 Jun 2024 14:00:44 +0300 Subject: [PATCH 10/22] Fix: Image size with Scale=Auto and others (watch.js) - Code cleaning with removal of unused code. - Added a temporary function "setButtonStateWatch" - Added a Stop button - Partially redesigned the logic for displaying DVR control buttons (Play, Stop, Pause) --- web/skins/classic/views/js/watch.js | 141 +++++++++++++++------------- 1 file changed, 75 insertions(+), 66 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 29d3c086a..38d4add20 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -8,6 +8,7 @@ var sidebarControls = $j('#ptzControls'); var wrapperMonitor = $j('#wrapperMonitor'); var filterQuery = '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId; var idle = 0; +var monitorStream = false; /* Stream is not started */ var classSidebarL = 'col-sm-3'; /* id="sidebar" */ var classSidebarR = 'col-sm-2'; /* id="ptzControls" */ @@ -132,36 +133,11 @@ function showPtzControls() { showMode = 'control'; } -function changeSize() { - var width = $j('#width').val(); - var height = $j('#height').val(); - - //monitorStream.setScale('0', width, height); - monitorsSetScale(monitorId); - //$j('#scale').val('0'); - $j('#sidebar ul').height($j('#wrapperMonitor').height()-$j('#cycleButtons').height()); - - //setCookie('zmWatchScale', '0'); - setCookie('zmWatchWidth', width); - setCookie('zmWatchHeight', height); -} // end function changeSize() - function changeScale() { const scale = $j('#scale').val(); setCookie('zmWatchScaleNew'+monitorId, scale); setCookie('zmCycleScale', scale); monitorsSetScale(monitorId); -/* - const scale = $j('#scale').val(); - setCookie('zmWatchScale'+monitorId, scale); - $j('#width').val('auto'); - $j('#height').val('auto'); - setCookie('zmCycleScale', scale); - setCookie('zmWatchWidth', 'auto'); - setCookie('zmWatchHeight', 'auto'); - - setScale(); -*/ } function changeStreamQuality() { @@ -170,22 +146,6 @@ function changeStreamQuality() { monitorsSetScale(monitorId); } -// Implement current scale, as opposed to changing it -function setScale() { -/* - const scale = $j('#scale').val(); - //monitorStream.setScale(scale, $j('#width').val(), $j('#height').val()); - monitorsSetScale(monitorId); - // Always turn it off, we will re-add it below. I don't know if you can add a callback multiple - // times and what the consequences would be - $j(window).off('resize', endOfResize); //remove resize handler when Scale to Fit is not active - if (scale == '0') { - $j(window).on('resize', endOfResize); //remove resize handler when Scale to Fit is not active - changeSize(); - } -*/ -} // end function changeScale - function getStreamCmdResponse(respObj, respText) { watchdogOk('stream'); streamCmdTimer = clearTimeout(streamCmdTimer); @@ -335,8 +295,12 @@ function streamCmdPause(action) { } function onPlay() { - setButtonState('pauseBtn', 'inactive'); - setButtonState('playBtn', 'active'); + //monitorStream.setup_onplay(onPlay); //IgorA100 Added for testing, but probably not required + //setButtonState('pauseBtn', 'inactive'); + //setButtonState('playBtn', 'active'); + setButtonStateWatch('pauseBtn', 'inactive'); + setButtonStateWatch('stopBtn', 'inactive'); + setButtonStateWatch('playBtn', 'unavail'); if (monitorStream.status.delayed == true) { setButtonState('stopBtn', 'inactive'); if (monitorStreamReplayBuffer) { @@ -359,14 +323,21 @@ function onPlay() { function streamCmdPlay(action) { onPlay(); if (action) { - monitorStream.streamCommand(CMD_PLAY); + if (monitorStream.started) { + //Stream was on pause + monitorStream.streamCommand(CMD_PLAY); + } else { + //Stream has been stopped + monitorStream.start(); + } } } function streamCmdStop(action) { - setButtonState('pauseBtn', 'inactive'); - setButtonState('playBtn', 'unavail'); - setButtonState('stopBtn', 'active'); + monitorStream.onplay = false; //Without this line, "onPlay" is triggered immediately due to "if (this.onplay) this.onplay();" in MonitorStream.js + //setButtonState('pauseBtn', 'inactive'); + //setButtonState('playBtn', 'unavail'); + //setButtonState('stopBtn', 'active'); if (monitorStreamReplayBuffer) { setButtonState('fastFwdBtn', 'unavail'); setButtonState('slowFwdBtn', 'unavail'); @@ -374,10 +345,14 @@ function streamCmdStop(action) { setButtonState('fastRevBtn', 'unavail'); } if (action) { - monitorStream.streamCommand(CMD_STOP); + //monitorStream.streamCommand(CMD_STOP); + monitorStream.kill(); } - setButtonState('stopBtn', 'unavail'); - setButtonState('playBtn', 'active'); + //setButtonState('stopBtn', 'unavail'); + //setButtonState('playBtn', 'active'); + setButtonStateWatch('playBtn', 'inactive'); + setButtonStateWatch('stopBtn', 'unavail'); + setButtonStateWatch('pauseBtn', 'unavail'); } function streamCmdFastFwd(action) { @@ -983,6 +958,16 @@ function streamPrepareStart(monitor=null) { const el = document.querySelector('.imageFeed'); el.addEventListener('mouseenter', handleMouseEnter); el.addEventListener('mouseleave', handleMouseLeave); + + let i = setInterval(function() { + if (document.querySelector('[id ^= "liveStream"]').offsetHeight > 20) { + //You need to wait until the image appears. + clearInterval(i); + document.getElementById('monitor').classList.remove('hidden-shift'); + monitorsSetScale(monitorId); + } + }, 100); + setButtonState('stopBtn', 'active'); } function handleMouseEnter(event) { @@ -1005,7 +990,7 @@ function streamStart(monitor = null) { monitorStream.setBottomElement(document.getElementById('dvrControls')); // Start the fps and status updates. give a random delay so that we don't assault the server //monitorStream.setScale($j('#scale').val(), $j('#width').val(), $j('#height').val()); - monitorsSetScale(monitorId); + //monitorsSetScale(monitorId); monitorStream.start(); if (streamMode == 'single') { monitorStream.setup_onclick(fetchImage); @@ -1031,6 +1016,7 @@ function streamStart(monitor = null) { } function streamReStart(oldId, newId) { + document.getElementById('monitor').classList.add('hidden-shift'); const el = document.querySelector('.imageFeed'); const newMonitorName = document.getElementById('nav-item-cycle'+newId).querySelector('a').textContent; const currentMonitor = monitorData.find((o) => { @@ -1040,7 +1026,9 @@ function streamReStart(oldId, newId) { document.querySelector('title').textContent = newMonitorName; zmPanZoom.action('disable', {id: oldId}); - monitorStream.kill(); + if (monitorStream) { + monitorStream.kill(); + } el.removeEventListener('mouseenter', handleMouseEnter); el.removeEventListener('mouseleave', handleMouseLeave); @@ -1065,7 +1053,7 @@ function streamReStart(oldId, newId) { applyMonitorControllable(currentMonitor); zmPanZoom.init(); loadFontFaceObserver(); - document.getElementById('monitor').classList.remove('hidden-shift'); + //document.getElementById('monitor').classList.remove('hidden-shift'); } function applyMonitorControllable(currentMonitor) { @@ -1114,9 +1102,6 @@ function initPage() { zmPanZoom.init(); - streamPrepareStart(); - applyMonitorControllable(monitorStream); - // Manage the BACK button bindButton('#backBtn', 'click', null, function onBackClick(evt) { evt.preventDefault(); @@ -1185,15 +1170,18 @@ function initPage() { monitorsSetScale(monitorId); updateScale = false; } - }, 500); + }, 300); document.addEventListener('click', function(event) { handleClick(event); }); - document.getElementById('monitor').classList.remove('hidden-shift'); + //document.getElementById('monitor').classList.remove('hidden-shift'); changeObjectClass(); - changeSize(); + streamPrepareStart(); + if (monitorStream) { + applyMonitorControllable(monitorStream); + } } // initPage function watchFullscreen() { @@ -1313,7 +1301,7 @@ function cycleToggle(e) { button.toggleClass('btn-secondary'); button.toggleClass('btn-primary'); changeObjectClass(); - changeSize(); + monitorsSetScale(monitorId); } function ptzToggle(e) { @@ -1328,7 +1316,7 @@ function ptzToggle(e) { button.toggleClass('btn-secondary'); button.toggleClass('btn-primary'); changeObjectClass(); - changeSize(); + monitorsSetScale(monitorId); } function changeRate(e) { @@ -1393,13 +1381,17 @@ function panZoomOut(el) { function monitorsSetScale(id=null) { //This function will probably need to be moved to the main JS file, because now used on Watch & Montage pages if (id || typeof monitorStream !== 'undefined') { - //monitorStream used on Watch page. - if (monitorStream) { + if (monitorStream !== false) { + //monitorStream used on Watch page. var curentMonitor = monitorStream; - } else { + } else if (typeof monitors !== 'undefined') { + //used on Montage, Watch & Event page. var curentMonitor = monitors.find((o) => { return parseInt(o["id"]) === id; }); + } else { + //Stream is missing + return; } //const el = document.getElementById('liveStream'+id); if (panZoomEnabled && zmPanZoom.panZoom[id]) { @@ -1533,12 +1525,29 @@ document.onvisibilitychange = () => { TimerHideShow = clearTimeout(TimerHideShow); TimerHideShow = setTimeout(function() { //Stop monitor when closing or hiding page - monitorStream.kill(); + if (monitorStream) { + monitorStream.kill(); + } }, 15*1000); } else { //Start monitor when show page - if (!monitorStream.started) { + if (monitorStream && !monitorStream.started) { monitorStream.start(); } } }; + +function setButtonStateWatch(element_id, btnClass) { + //Temporary function so as not to break anything else, because analysis of the setButtonState function in skin.js is required, + //and also review the logic of the buttons and more (if (this.onplay) this.onplay() in MonitorStream.js) var element = document.getElementById(element_id); + if ( element ) { + element.className = btnClass; + if (btnClass == 'unavail') { + element.disabled = true; + } else { + element.disabled = false; + } + } else { + console.log('Element was null or not found in setButtonState. id:'+element_id); + } +} From 2dcb3a3c2f7ac007617b935796f1b50660f39a88 Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Wed, 5 Jun 2024 14:04:57 +0300 Subject: [PATCH 11/22] Display a single style of DVR control buttons (watch.php) Button visibility/activity will be controlled by watch.js --- web/skins/classic/views/watch.php | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/web/skins/classic/views/watch.php b/web/skins/classic/views/watch.php index b8b0bbfec..336e7a287 100644 --- a/web/skins/classic/views/watch.php +++ b/web/skins/classic/views/watch.php @@ -381,55 +381,35 @@ echo htmlSelect('cyclePeriod', $cyclePeriodOptions, $period, array('id'=>'cycleP echo $monitor->getStreamHTML($options); ?> -Type() != 'WebSite') { -?>
    -StreamReplayBuffer() != 0) { -?> - - -StreamReplayBuffer() != 0) { -?> - -
    -Type() != 'WebSite' ?>
    -
    From 39e9697873d3e6305a449070bae50857a918d8c2 Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Wed, 5 Jun 2024 20:55:35 +0300 Subject: [PATCH 19/22] Do not change the "stopBtn" button when analyzing "monitorStream.status.delayed" (watch.js) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise, the "stopBtn" button will not be active when viewing. I still don’t understand why this was necessary? --- web/skins/classic/views/js/watch.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 82cfbc01e..9e4295542 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -302,7 +302,7 @@ function onPlay() { setButtonStateWatch('stopBtn', 'inactive'); setButtonStateWatch('playBtn', 'unavail'); if (monitorStream.status.delayed == true) { - setButtonState('stopBtn', 'inactive'); + //setButtonState('stopBtn', 'inactive'); if (monitorStreamReplayBuffer) { setButtonState('fastFwdBtn', 'inactive'); setButtonState('slowFwdBtn', 'inactive'); @@ -310,7 +310,7 @@ function onPlay() { setButtonState('fastRevBtn', 'inactive'); } } else { - setButtonState('stopBtn', 'unavail'); + //setButtonState('stopBtn', 'unavail'); if (monitorStreamReplayBuffer) { setButtonState('fastFwdBtn', 'unavail'); setButtonState('slowFwdBtn', 'unavail'); @@ -967,7 +967,7 @@ function streamPrepareStart(monitor=null) { monitorsSetScale(monitorId); } }, 100); - setButtonState('stopBtn', 'active'); + setButtonStateWatch('stopBtn', 'active'); } function handleMouseEnter(event) { From 05e936325125b6e6e87a47777e52ac052d2109e1 Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Fri, 7 Jun 2024 21:46:39 +0300 Subject: [PATCH 20/22] Pressing any DVR control button will stop cycle. (watch.js) --- web/skins/classic/views/js/watch.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 9e4295542..3b176c3dc 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -601,6 +601,8 @@ function handleClick(event) { const oldId = stringToNumber(document.querySelector('[id ^= "liveStream"]').id); const newId = stringToNumber(targetId); streamReStart(oldId, newId); + } else if (event.target.closest('#dvrControls')) { //Controls DVR + cyclePause(); } if (panZoomEnabled) { //event.preventDefault(); From cbf3812a30fd55f7c899dcf5ab4d441c10275983 Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Fri, 7 Jun 2024 21:56:02 +0300 Subject: [PATCH 21/22] After pressing Play, do not reset the time countdown if pause was enabled before (watch.js) --- web/skins/classic/views/js/watch.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 3b176c3dc..aff30f1ce 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -1230,7 +1230,7 @@ function cyclePause() { } function cycleStart() { - secondsToCycle = $j('#cyclePeriod').val(); + secondsToCycle = (secondsToCycle == 0) ? $j('#cyclePeriod').val() : secondsToCycle; intervalId = setInterval(nextCycleView, 1000); cycle = true; $j('#cyclePauseBtn').show(); @@ -1238,6 +1238,7 @@ function cycleStart() { } function cycleStop(target) { + secondsToCycle = 0; monIdx = target.getAttribute('data-monIdx'); $j('#secondsToCycle').text(''); cyclePause(); From da8882b67fab2984ffd90188f2e9c95c3cc286fd Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Fri, 7 Jun 2024 22:08:25 +0300 Subject: [PATCH 22/22] Chore: Code optimization (watch.js) --- web/skins/classic/views/js/watch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index aff30f1ce..557d9a656 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -1230,7 +1230,7 @@ function cyclePause() { } function cycleStart() { - secondsToCycle = (secondsToCycle == 0) ? $j('#cyclePeriod').val() : secondsToCycle; + if (secondsToCycle == 0) secondsToCycle = $j('#cyclePeriod').val(); intervalId = setInterval(nextCycleView, 1000); cycle = true; $j('#cyclePauseBtn').show();