Merge branch 'ZoneMinder:master' into patch-6

pull/3843/head
IgorA100 2024-04-05 22:58:55 +03:00 committed by GitHub
commit 49704656b8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 724 additions and 245 deletions

View File

@ -47,5 +47,5 @@ module.exports = {
"php/remove-whitespace": false,
"php/remove-empty-line": false,
"php/remove-php-lint": false
},
}
};

View File

@ -21,4 +21,4 @@ jobs:
- name: Install ESLint
run: npm install eslint@8.7.0 eslint-config-google@0.14.0 eslint-plugin-html@6.2.0 eslint-plugin-php-markup@6.0.0
- name: Run ESLint
run: npx eslint --ext .php,.js .
run: npx eslint --ext .js.php,.js .

6
db/Object_Types.sql Normal file
View File

@ -0,0 +1,6 @@
CREATE TABLE Object_Types (
Id int(10) NOT NULL AUTO_INCREMENT,
Name varchar(32) UNIQUE,
Human TEXT,
PRIMARY KEY (Id)
);

View File

@ -1256,6 +1256,7 @@ CREATE TABLE `Events_Tags` (
CONSTRAINT `Events_Tags_ibfk_2` FOREIGN KEY (`EventId`) REFERENCES `Events` (`Id`) ON DELETE CASCADE
) ENGINE=@ZM_MYSQL_ENGINE@;
source @PKGDATADIR@/db/Object_Types.sql
-- We generally don't alter triggers, we drop and re-create them, so let's keep them in a separate file that we can just source in update scripts.
source @PKGDATADIR@/db/triggers.sql

View File

@ -18,7 +18,7 @@
%global zmtargetdistro %{?rhel:el%{rhel}}%{!?rhel:fc%{fedora}}
Name: zoneminder
Version: 1.37.56
Version: 1.37.57
Release: 2%{?dist}
Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons

View File

@ -222,8 +222,8 @@ $fields{model} = undef;
Options => undef,
User => undef,
Pass => undef,
Width => undef,
Height => undef,
Width => 0,
Height => 0,
Colours => 4,
Palette => 0,
Orientation => q`'ROTATE_0'`,

View File

@ -22,7 +22,6 @@
# This module contains the common definitions and functions used by the rest
# of the ZoneMinder scripts
#
package ZoneMinder::Object;
use 5.006;
@ -32,6 +31,7 @@ use Time::HiRes qw{ gettimeofday tv_interval };
use Carp qw( cluck );
require ZoneMinder::Base;
require ZoneMinder::Object_Type;
our @ISA = qw(ZoneMinder::Base);
@ -361,7 +361,7 @@ sub set {
if ( $params ) {
foreach my $field ( keys %{$params} ) {
$log->debug("field: $field, ".def_or_undef($$self{$field}).' =? param: '.def_or_undef($$params{$field})) if $debug;
$log->debug("field: $field, ".def_or_undef($$self{$field}).' =? param: '.def_or_undef($$params{$field})) if $debug or DEBUG_ALL;
if ( ( ! defined $$self{$field} ) or ($$self{$field} ne $params->{$field}) ) {
# Only make changes to fields that have changed
if ( defined $fields{$field} ) {
@ -886,6 +886,62 @@ sub clone {
return $new;
} # end sub clone
sub Object_Type {
if ( $_[0]{object_type_id} ) {
$_[0]{Object_Type} = new ZoneMinder::Object_Type( $_[0]{object_type_id} );
} else {
$_[0]{Object_Type} = ZoneMinder::Object_Type->find_one( name=>ref $_[0] );
$_[0]{Object_Type} = new ZoneMinder::Object_Type() if ! $_[0]{Object_Type};
} # end if
return $_[0]{Object_Type};
} # end sub Object_Type
sub object_type {
if ( @_ > 1 ) {
my $Type = ZoneMinder::Object_Type->find_one( name => $_[1] );
if ( ! $Type ) {
$Type = new ZoneMinder::Object_Type();
$Type->save({ Name=>$_[1], Human=>$_[1] });
} # end if
$_[0]{object_type} = $Type->Name();
$_[0]{object_type_id} = $Type->Id();
} # end if
if ( ! $_[0]{object_type} ) {
$_[0]{object_type} = new ZoneMinder::Object_Type( $_[0]{object_type_id} )->Name();
} # end if
return $_[0]{object_type};
} # end sub object_type
sub Object {
my $self = shift;
if ( @_ ) {
$self->object_type( ref $_[0] );
$$self{object_id} = $_[0]{id};
$$self{Object} = $_[0];
} # end if
my $type = $self->object_type();
if ( !$type ) {
Error('No type in Object::Object'. $self->to_string()) if ref $self ne 'ZoneMinder::Log';
return undef;
} # end if
my ( $module ) = $type =~ /ZoneMinder::(.*)/;
if ( $module ) {
eval {
require "ZoneMinder/$module.pm";
};
if ( ! $$self{Object} ) {
$_ = $type->new($$self{object_id});
Debug( 'Returning object of type ' . ref $_ ) if $debug;
$$self{Object} = $_;
}
return $$self{Object};
} else {
Error("Unvalid object $type");
return new ZoneMinder::Object();
}
} # end sub Object
sub AUTOLOAD {
my $type = ref($_[0]);
Carp::cluck("No type in autoload") if ! $type;

View File

@ -0,0 +1,49 @@
use strict;
package ZoneMinder::Object_Type;
our @ISA = qw( ZoneMinder::Object );
use vars qw( $debug $table %fields %transforms %defaults $primary_key );
$debug = 0;
$table = 'Object_Types';
$primary_key = 'id';
%fields = (
Id => 'Id',
Name => 'Name',
Human => 'Human',
);
%defaults = (
);
%transforms = (
Name => [ 's/^\s+//', 's/\s+$//', 's/\s\s+/ /g' ],
Human => [ 's/^\s+//', 's/\s+$//', 's/\s\s+/ /g', 's/^ZoneMinder:://' ],
);
sub Object {
if ( $_[0]{Name} ) {
my $name = $_[0]{Name};
$name =~ s/::/\//g;
eval {
require $name.'.pm';
};
$ZoneMinder::log->error("failed requiring $name $@") if $@;
return $_[0]{Name}->new($_[1]);
}
my ($caller, undef, $line) = caller;
$ZoneMinder::log->error("Unknown object from $caller:$line");
return new ZoneMinder::Object();
} # end sub Object
sub Human {
if ( @_ > 1 ) {
$_[0]{Human} = $_[1];
}
if ( ! $_[0]{Human} ) {
$_[0]{Human} = $_[0]{Name};
$_[0]{Human} =~ s/^ZoneMinder:://;
}
return $_[0]{Human};
}
1;
__END__

View File

@ -528,10 +528,20 @@ void EventStream::processCommand(const CmdMsg *msg) {
x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2];
y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4];
Debug(1, "Got PAN command, to %d,%d", x, y);
send_frame = true;
if (paused) {
step = 1;
send_twice = true;
}
break;
case CMD_SCALE :
scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2];
Debug(1, "Got SCALE command, to %d", scale);
send_frame = true;
if (paused) {
step = 1;
send_twice = true;
}
break;
case CMD_PREV :
Debug(1, "Got PREV command");

View File

@ -1 +1 @@
1.37.56
1.37.57

View File

@ -121,6 +121,7 @@ class Event extends ZM_Object {
public function StartDateTimeSecs() {
return strtotime($this->{'StartDateTime'});
}
public function EndDateTimeSecs() {
return strtotime($this->{'EndDateTime'});
}
@ -256,16 +257,19 @@ class Event extends ZM_Object {
}
public function getStreamSrc( $args=array(), $querySep='&' ) {
$streamSrc = '';
$Server = $this->Server();
# If we are in a multi-port setup, then use the multiport, else by
# passing null Server->Url will use the Port set in the Server setting
$streamSrc .= $Server->Url(
ZM_MIN_STREAMING_PORT ?
if ($args['mode'] == 'mp4') { #Downloading a video file. It is possible to reconsider the condition later.
#If the port is different from 80, the browser will start watching the video instead of downloading.
$port = null;
} else {
$port = ZM_MIN_STREAMING_PORT ?
ZM_MIN_STREAMING_PORT+$this->{'MonitorId'} :
null);
null;
}
$streamSrc = $Server->Url($port);
if ( $this->{'DefaultVideo'} and $args['mode'] != 'jpeg' ) {
$streamSrc .= $Server->PathToIndex();
@ -317,8 +321,8 @@ class Event extends ZM_Object {
}
function createListThumbnail( $overwrite=false ) {
# The idea here is that we don't really want to use the analysis jpeg as the thumbnail.
# The snapshot image will be generated during capturing
# The idea here is that we don't really want to use the analysis jpeg as the thumbnail.
# The snapshot image will be generated during capturing
if ( file_exists($this->Path().'/snapshot.jpg') ) {
Debug("snapshot exists");
$frame = null;
@ -380,7 +384,7 @@ class Event extends ZM_Object {
function getThumbnailSrc( $args=array(), $querySep='&' ) {
# The thumbnail is theoretically the image with the most motion.
# We always store at least 1 image when capturing
# We always store at least 1 image when capturing
$streamSrc = '';
$Server = $this->Server();
@ -525,7 +529,7 @@ class Event extends ZM_Object {
'imageClass' => $alarmFrame?'alarm':'normal',
'isAnalysisImage' => $isAnalysisImage,
'hasAnalysisImage' => $hasAnalysisImage,
'FrameId' => $frame['FrameId'],
'FrameId' => $frame['FrameId'],
);
return $imageData;
@ -677,6 +681,7 @@ class Event extends ZM_Object {
}
return false;
}
function canEdit($u=null) {
global $user;
if (!$u) $u=$user;
@ -840,5 +845,4 @@ class Event extends ZM_Object {
} # end sub GenerateVideo
} # end class
?>

View File

@ -1085,6 +1085,10 @@ class Filter extends ZM_Object {
foreach ( $Servers as $server ) {
$servers[$server->Id()] = validHtmlStr($server->Name());
}
$weekdays = array();
for ( $i = 0; $i < 7; $i++ ) {
$weekdays[$i] = date('D', mktime(12, 0, 0, 1, $i+1, 2001));
}
$availableTags = array();
foreach ( dbFetchAll('SELECT Id, Name FROM Tags ORDER BY LastAssignedDate DESC') AS $tag ) {
$availableTags[$tag['Id']] = validHtmlStr($tag['Name']);
@ -1115,14 +1119,16 @@ class Filter extends ZM_Object {
#$html .= '<span>'.htmlSelect("filter[Query][terms][$i][op]", $opTypes, $term['op']).'</span>'.PHP_EOL;
if ( $term['attr'] == 'Archived' ) {
$html .= htmlSelect("filter[Query][terms][$i][val]", $archiveTypes, $term['val'],['class'=>'chosen chosen-full-width']).PHP_EOL;
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", $archiveTypes, $term['val'],['class'=>'chosen chosen-auto-width']).PHP_EOL;
$html .= '</span>';
} else if ( $term['attr'] == 'Tags' ) {
$selected = explode(',', $term['val']);
// echo '<pre>selected: '; print_r($selected); echo '</pre>';
if (count($selected) == 1 and !$selected[0]) {
$selected = null;
}
$options = ['class'=>'term-value-wrapper chosen chosen-full-width', 'multiple'=>'multiple', 'data-placeholder'=>translate('All Tags')];
$options = ['class'=>'chosen chosen-auto-width', 'multiple'=>'multiple', 'data-placeholder'=>translate('All Tags')];
if (isset($term['cookie'])) {
$options['data-cookie'] = $term['cookie'];
@ -1134,7 +1140,9 @@ class Filter extends ZM_Object {
// echo '<pre>selected: '; print_r($selected); echo '</pre>';
// echo '<pre>options: '; print_r($options); echo '</pre>';
$html .= '<span class="term-value-wrapper">'.htmlSelect("filter[Query][terms][$i][val]", $availableTags, $selected, $options).'</span>'.PHP_EOL;
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", $availableTags, $selected, $options).PHP_EOL;
$html .= '</span>';
// $html .= '<span>'.htmlSelect("filter[Query][terms][$i][val]", array_combine($availableTags,$availableTags), $term['val'],
// $options).'</span>'.PHP_EOL;
// $html .= '<span>'.htmlSelect("filter[Query][terms][$i][val]", $availableTags, $term['val'], $options).'</span>'.PHP_EOL;
@ -1147,7 +1155,8 @@ class Filter extends ZM_Object {
} else if ( $term['attr'] == 'DateTime' || $term['attr'] == 'StartDateTime' || $term['attr'] == 'EndDateTime') {
$html .= '<span class="term-value-wrapper"><input type="text" class="term-value datetimepicker" name="filter[Query][terms]['.$i.'][val]"';
$html .= '<span class="term-value-wrapper">';
$html .= '<input type="text" class="term-value datetimepicker" name="filter[Query][terms]['.$i.'][val]"';
if (isset($term['id'])) {
$html .= ' id="'.$term['id'].'"';
} else {
@ -1160,9 +1169,11 @@ class Filter extends ZM_Object {
$html .= ' value="'.(isset($term['val'])?validHtmlStr(str_replace('T', ' ', $term['val'])):'').'"';
if (!isset($term['placeholder'])) $term['placeholder'] = translate('Attr'.$term['attr']);
$html .= ' placeholder="'.$term['placeholder'].'"/></span>'.PHP_EOL;
$html .= ' placeholder="'.$term['placeholder'].'"/>'.PHP_EOL;
$html .= '</span>';
} else if ( $term['attr'] == 'Date' || $term['attr'] == 'StartDate' || $term['attr'] == 'EndDate' ) {
$html .= '<span class="term-value-wrapper"><input type="text" class="term-value datepicker" name="filter[Query][terms]['.$i.'][val]" id="filter[Query][terms]['.$i.'][val]"';
$html .= '<span class="term-value-wrapper">';
$html .= '<input type="text" class="term-value datepicker" name="filter[Query][terms]['.$i.'][val]" id="filter[Query][terms]['.$i.'][val]"';
if (isset($term['cookie'])) {
if (!$term['val'] and isset($_COOKIE[$term['cookie']])) $term['val'] = $_COOKIE[$term['cookie']];
$html .= ' data-cookie="'.$term['cookie'].'"';
@ -1170,19 +1181,29 @@ class Filter extends ZM_Object {
$html .= ' value="'.(isset($term['val'])?validHtmlStr($term['val']):'').'" placeholder="'.translate('Attr'.$term['attr']).'"';
$html .= '/></span>'.PHP_EOL;
} else if ( $term['attr'] == 'StartTime' || $term['attr'] == 'EndTime' ) {
$html .= '<span class="term-value-wrapper"><input type="text" name="filter[Query][terms]['.$i.'][val]" id="filter[Query][terms]['.$i.'][val]" value="'.(isset($term['val'])?validHtmlStr(str_replace('T', ' ', $term['val'])):'' ).'"/></span>'.PHP_EOL;
$html .= '<span class="term-value-wrapper">';
$html .= '<input type="text" name="filter[Query][terms]['.$i.'][val]" id="filter[Query][terms]['.$i.'][val]" value="'.(isset($term['val'])?validHtmlStr(str_replace('T', ' ', $term['val'])):'' ).'"/>'.PHP_EOL;
$html .= '</span>';
} else if ( $term['attr'] == 'ExistsInFileSystem' ) {
$html .= '<span class="term-value-wrapper">'.htmlSelect("filter[Query][terms][$i][val]", $booleanValues, $term['val'], ['class'=>'chosen chosen-full-width']).'</span>'.PHP_EOL;
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", $booleanValues, $term['val'], ['class'=>'chosen chosen-auto-width']).PHP_EOL;
$html .= '</span>';
} else if ( $term['attr'] == 'Group') {
$html .= '<span class="term-value-wrapper">'.htmlSelect("filter[Query][terms][$i][val]", Group::get_dropdown_options(), $term['val'],
['class'=>'term-value chosen chosen-full-width',
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", Group::get_dropdown_options(), $term['val'],
['class'=>'term-value chosen chosen-auto-width',
'multiple'=>'multiple',
'data-placeholder'=>translate('All Groups')]).'</span>'.PHP_EOL;
'data-placeholder'=>translate('All Groups')]).PHP_EOL;
$html .= '</span>';
} else if ( $term['attr'] == 'StateId' ) {
$html .= '<span class="term-value-wrapper">'.htmlSelect("filter[Query][terms][$i][val]", $states, $term['val'], ['class'=>'chosen chosen-full-width']).'</span>'.PHP_EOL;
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", $states, $term['val'], ['class'=>'chosen chosen-auto-width']).PHP_EOL;
$html .= '</span>';
} else if ( strpos($term['attr'], 'Weekday') !== false ) {
$html .= '<span class="term-value-wrapper">'.htmlSelect("filter[Query][terms][$i][val]", $weekdays, $term['val'], ['class'=>'chosen chosen-full-width']).'</span>'.PHP_EOL;
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", $weekdays, $term['val'], ['class'=>'chosen chosen-auto-width']).PHP_EOL;
$html .= '</span>';
} else if ( $term['attr'] == 'Monitor' ) {
$monitors = [];
foreach (Monitor::find(['Deleted'=>false], ['order'=>'lower(Name)']) as $m) {
@ -1195,14 +1216,16 @@ class Filter extends ZM_Object {
if (count($selected) == 1 and !$selected[0]) {
$selected = null;
}
$options = ['class'=>'term-value chosen chosen-full-width', 'multiple'=>'multiple', 'data-placeholder'=>translate('All Monitors')];
$options = ['class'=>'term-value chosen chosen-auto-width', 'multiple'=>'multiple', 'data-placeholder'=>translate('All Monitors')];
if (isset($term['cookie'])) {
$options['data-cookie'] = $term['cookie'];
if (!$selected and isset($_COOKIE[$term['cookie']]) and $_COOKIE[$term['cookie']])
$selected = explode(',', $_COOKIE[$term['cookie']]);
}
$html .= '<span class="term-value-wrapper">'.htmlSelect("filter[Query][terms][$i][val]", $monitors, $selected, $options).'</span>'.PHP_EOL;
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", $monitors, $selected, $options).PHP_EOL;
$html .= '</span>';
} else if ( $term['attr'] == 'MonitorName' ) {
$monitor_names = [];
foreach (Monitor::find(['Deleted'=>false], ['order'=>'lower(Name)']) as $m) {
@ -1210,22 +1233,30 @@ class Filter extends ZM_Object {
$monitor_names[$m->Name()] = validHtmlStr($m->Name());
}
}
$html .= '<span class="term-value-wrapper">'.htmlSelect("filter[Query][terms][$i][val]", array_combine($monitor_names,$monitor_names), $term['val'],
['class'=>'term-value chosen chosen-full-width', 'multiple'=>'multiple', 'data-placeholder'=>translate('All Monitors')]).'</span>'.PHP_EOL;
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", array_combine($monitor_names,$monitor_names), $term['val'],
['class'=>'term-value chosen chosen-auto-width', 'multiple'=>'multiple', 'data-placeholder'=>translate('All Monitors')]).PHP_EOL;
$html .= '</span>';
} else if ( $term['attr'] == 'ServerId' || $term['attr'] == 'MonitorServerId' || $term['attr'] == 'StorageServerId' || $term['attr'] == 'FilterServerId' ) {
$html .= '<span class="term-value-wrapper">'.htmlSelect("filter[Query][terms][$i][val]", $servers, $term['val'],
['class'=>'term-value chosen chosen-full-width', 'multiple'=>'multiple']).'</span>'.PHP_EOL;
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", $servers, $term['val'],
['class'=>'term-value chosen chosen-auto-width', 'multiple'=>'multiple']).PHP_EOL;
$html .= '</span>';
} else if ( ($term['attr'] == 'StorageId') || ($term['attr'] == 'SecondaryStorageId') ) {
if (!$storageareas) {
$storageareas = array('' => array('Name'=>'NULL Unspecified'), '0' => array('Name'=>'Zero')) + ZM_Object::Objects_Indexed_By_Id('ZM\Storage');
}
$html .= '<span class="term-value-wrapper">'.htmlSelect("filter[Query][terms][$i][val]", $storageareas, $term['val'],
['class'=>'term-value chosen chosen-full-width', 'multiple'=>'multiple']).'</span>'.PHP_EOL;
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", $storageareas, $term['val'],
['class'=>'term-value chosen chosen-auto-width', 'multiple'=>'multiple']).PHP_EOL;
$html .= '</span>';
} else if ( $term['attr'] == 'AlarmedZoneId' ) {
$html .= '<span class="term-value-wrapper">'.htmlSelect("filter[Query][terms][$i][val]", $zones, $term['val'],
['class'=>'term-value chosen chosen-full-width', 'multiple'=>'multiple']).'</span>'.PHP_EOL;
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", $zones, $term['val'],
['class'=>'term-value chosen chosen-auto-width', 'multiple'=>'multiple']).PHP_EOL;
$html .= '</span>';
} else if ( $term['attr'] == 'Notes' ) {
$attrs = ['class'=>'term-value chosen chosen-full-width', 'multiple'=>'multiple', 'data-placeholder'=>translate('Event Type')];
$attrs = ['class'=>'term-value chosen chosen-auto-width', 'multiple'=>'multiple', 'data-placeholder'=>translate('Event Type')];
$selected = explode(',', $term['val']);
if (count($selected) == 1 and !$selected[0]) {
$selected = null;
@ -1246,13 +1277,19 @@ class Filter extends ZM_Object {
'car' => 'Car',
'truck' => 'Truck',
'vehicle' => 'Vehicle'];
$html .= '<span class="term-value-wrapper">'.htmlSelect("filter[Query][terms][$i][val]", $options, $selected, $attrs).'</span>'.PHP_EOL;
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect("filter[Query][terms][$i][val]", $options, $selected, $attrs).PHP_EOL;
$html .= '</span>';
} else {
#$html .= $term['attr'];
$html .= '<span class="term-value-wrapper"><input class="term-value"type="text" name="filter[Query][terms]['.$i.'][val]" value="'.validHtmlStr($term['val']).'"/></span>'.PHP_EOL;
$html .= '<span class="term-value-wrapper">';
$html .= '<input class="term-value"type="text" name="filter[Query][terms]['.$i.'][val]" value="'.validHtmlStr($term['val']).'"/>'.PHP_EOL;
$html .= '</span>';
}
} else { # no attr ?
$html .= '<span class="term-value-wrapper"><input class="term-value" type="text" name="filter[Query][terms]['.$i.'][val]" value="'.(isset($term['val'])?validHtmlStr($term['val']):'' ).'"/></span>'.PHP_EOL;
$html .= '<span class="term-value-wrapper">';
$html .= '<input class="term-value" type="text" name="filter[Query][terms]['.$i.'][val]" value="'.(isset($term['val'])?validHtmlStr($term['val']):'' ).'"/></span>'.PHP_EOL;
$html .= '</span>';
}
$html .= '</span>';

View File

@ -140,6 +140,11 @@ if ($action == 'save') {
if (count($changes)) {
// monitor->Id() has a value when the db record exists
if ($monitor->Id()) {
if ($monitor->Deleted() and ! isset($_REQUEST['newMonitor[Deleted]'])) {
# We are saving a new monitor with a specified Id and the Id is used in a deleted record.
# Undelete it so that it is visible.
$monitor->Deleted(false);
}
# If we change anything that changes the shared mem size, zma can complain. So let's stop first.
if ($monitor->Type() != 'WebSite') {

View File

@ -339,6 +339,10 @@ function MonitorStream(monitorData) {
console.log('onclick');
};
this.onmove = function(evt) {
console.log('onmove');
};
this.setup_onclick = function(func) {
if (func) {
this.onclick = func;
@ -350,6 +354,17 @@ function MonitorStream(monitorData) {
}
};
this.setup_onmove = function(func) {
if (func) {
this.onmove = func;
}
if (this.onmove) {
const el = this.getFrame();
if (!el) return;
el.addEventListener('mousemove', this.onmove, false);
}
};
this.disable_onclick = function() {
const el = this.getElement();
if (!el) return;

View File

@ -88,9 +88,9 @@ $SLANG = array(
'AlarmBrFrames' => 'Кадры<br/>тревоги',
'AlarmFrame' => 'Кадр тревоги',
'AlarmFrameCount' => 'Число кадров тревоги',
'AlarmLimits' => 'Гран.&nbsp;зоны&nbsp;трев.',
'AlarmLimits' => 'Границы зоны тревоги',
'AlarmMaximumFPS' => 'Макс. к/с при тревоге',
'AlarmPx' => 'Пкс&nbsp;трев.',
'AlarmPx' => 'Тревожных пикселей',
'AlarmRGBUnset' => 'Вы должны установить цвет тревоги (RGB)',
'AlarmRefImageBlendPct'=> 'Смешение опорного кадра тревоги, %', // Added - 2015-04-18
'Alert' => 'Бдительность',
@ -175,7 +175,7 @@ $SLANG = array(
'BadWidth' => 'Неправильная ширина',
'Bandwidth' => 'канал',
'BandwidthHead' => 'канал', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing;
'BlobPx' => кс объекта',
'BlobPx' => икселей объекта',
'BlobSizes' => 'Размер объектов',
'Blobs' => 'Кол-во объектов',
'Brightness' => 'Яркость',

View File

@ -21,6 +21,11 @@
* Primary look and feel styles
*/
:root {
--scrollbarBG: #F1F1F1;
--sliderBG: #C1C1C1;
}
@font-face {
font-family: "Material Icons";
font-style: normal;
@ -100,14 +105,14 @@ p {
font-weight: normal;
}
th {
/*
th {
padding: 3px;
text-transform: uppercase;
font-size: 0.8em;
font-weight: 600;
*/
}
*/
.thead-highlight {
background-color:#dfe4ea;
@ -150,8 +155,16 @@ a.btn,
button.btn {
line-height: 1;
font-size: 18px;
margin-bottom: 3px;
}
input,textarea,select,button,.btn-primary {
#toolbar .btn-normal {
margin-right: 3px;
}
#rightButtons .btn-normal,
#leftButtons .btn-normal {
margin-right: 0px;
}
input, textarea, select, button, .btn-primary {
border: 1px #ccc solid;
padding: 5px;
border-radius: 1px;
@ -162,7 +175,6 @@ input,textarea,select,button,.btn-primary {
background-color: #f8f8f8;
text-align: left;
border-radius:4px;
margin: 1px 0;
}
input.noborder {
@ -444,6 +456,10 @@ body.sticky #content {
display: none;
}
.hidden-shift {
position: absolute !important; left: -999em !important;
}
.invisible {
visibility: hidden;
}
@ -505,7 +521,6 @@ body.sticky #page {
#header {
width: 100%;
text-align: left;
background-color: #34495e;
padding: 5px 0px;
margin: 0 auto 4px auto;
@ -736,9 +751,23 @@ ul.nav.nav-pills.flex-column {
background-color: #f5f5f5;
}
.chosen-container {
text-align: left;
/*min-width: 11em;*/ /* Why ???*/
#toolbar,
.filterTable {
display:flex;
flex-wrap: wrap;
}
.controlHeader .chosen-container,
#header .chosen-container,
#fbpanel .chosen-container,
#toolbar .chosen-container {
text-align: left;
min-width: 11em; /* Makes a nice uniform display */
}
#mfbpanel {
width: calc(100% - 25px);
float: left;
}
.chosen-container-active .chosen-choices {
@ -799,14 +828,19 @@ li.search-choice {
.zoom,
.zoom-console {
transform-origin: 0% 50%;
transform-origin: 0% 00%;
transform: scale(5); /* (arbitray zoom value - Note if the zoom is too large, it will go outside of the viewport) */
position: sticky;
z-index: 1001;
}
#framesTable .zoom {
transform-origin: 100% 0%;
}
a.flip {
float: right;
padding-right: 5px;
}
#content table.major .colDiskSpace {
@ -836,8 +870,6 @@ a.flip {
padding: 4px 10px 4px 10px;
color: white;
}
#shutdownButton i {
}
.imageFeed {
margin: 0 auto;
text-align: center;
@ -890,16 +922,17 @@ a.flip {
border-radius: 0;
border: none;
}
.controlHeader {
.controlHeader, #fieldsTable {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
justify-content: center;
}
/* to match chosen text input height */
.controlHeader input[type="text"] {
height: 29px;
padding: 2px 5px 4px 5px;
margin-top:0;
width: 100%;
}
@media screen and (max-width:767px) {
.controlHeader {
@ -1000,6 +1033,57 @@ button .material-icons {
display: block;
}
input.hasDatepicker {
max-width:140px;
/* Change scrollbar style */
div::-webkit-scrollbar, nav::-webkit-scrollbar, .chosen-results::-webkit-scrollbar {
width: 11px;
height: 11px;
}
div, nav, .chosen-results {
scrollbar-width: thin;
scrollbar-color: var(--sliderBG) var(--scrollbarBG);
}
div::-webkit-scrollbar-track, nav::-webkit-scrollbar-track, .chosen-results::-webkit-scrollbar-track {
background: var(--scrollbarBG);
}
div::-webkit-scrollbar-thumb, nav::-webkit-scrollbar-thumb, .chosen-results::-webkit-scrollbar-thumb {
background-color: var(--sliderBG);
border-radius: 6px;
border: 3px solid var(--scrollbarBG);
}
.term {
display: flex;
flex-direction: column;
margin-left: 15px;
}
@media screen and (max-width:500px) {
.term {
width: 100% !important;
flex-direction: row;
margin-right: 15px;
margin-bottom: 5px;
}
.term-label-wrapper {
text-wrap: nowrap;
margin-top: 7px;
margin-right: 5px;
}
.term-value-wrapper {
width: 100%;
flex-grow: 1;
}
.term-value-wrapper .chosen-container {
width: 100% !important;
}
label {
text-wrap: nowrap;
}
input.hasDatepicker {
max-width: 100%;
width: 100%;
}
}

View File

@ -103,13 +103,13 @@
margin-bottom: 2rem;
}
.MonitorNameFilter,
/* .MonitorNameFilter,
.FunctionFilter,
.StatusFilter,
.SourceFilter,
.MonitorFilter {
display: inline-block;
}
} */
form[name="monitorForm"] {
display: flex;
flex-flow: column nowrap;
@ -125,7 +125,8 @@ body.sticky #monitorList {
}
body.sticky #monitorList thead {
position: sticky;
top: -1px;
top: 0;
box-shadow: 0 0px 0, 0 -3px 0 #dfe4ea;
}
#toolbar,
#contentButtons {

View File

@ -31,6 +31,7 @@
z-index: 10;
border: none;
border-right: 1px solid black;
cursor: pointer;
}
#alarmCues span {
@ -192,6 +193,7 @@ height: 100%;
*/
margin: 0;
z-index: 5;
overflow-x: clip;
}
#progressBar .progressBox {
@ -337,7 +339,7 @@ svg.zones {
#indicator {
height: 2.75em;
position: absolute;
border-left: 1px solid blue;
border-left: 2px solid blue;
margin-top: -1.25em;
}
.video-js .vjs-text-track-display {

View File

@ -43,18 +43,6 @@ input[type="number"].form-control {
padding: 5px;
}
#toolbar,
.filterTable {
display:flex;
flex-wrap: wrap;
}
#toolbar,
.filterTable > span{
padding-bottom: 5px;
padding-right: 15px;
}
#events {
height: 100%;
overflow: visible;
@ -85,31 +73,6 @@ body.sticky #eventTable thead {
top: -1px;
}
.term {
display: flex;
flex-direction: column;
}
.term-value, .chosen-container{
width: 100% !important;
}
@media screen and (max-width:500px) {
.term {
width: 100%;
flex-direction: row;
}
.term-label-wrapper {
text-wrap: nowrap;
margin-top: 7px;
}
.term-value-wrapper {
width: 100%;
flex-grow: 1;
}
label {
text-wrap: nowrap;
}
input.hasDatepicker {
max-width: 100%;
}
}

View File

@ -35,7 +35,7 @@ body.sticky #options {
padding-top: 15px;
}
.wrapper-scroll-table {
body.sticky .wrapper-scroll-table {
position: absolute;
margin-right: calc(var(--indent-bootstrap-row) * -1);
margin-left: calc(var(--indent-bootstrap-row) * -1);

View File

@ -67,8 +67,3 @@
stroke: #0000FF;
fill: #0000FF;
}
#mfbpanel {
padding-right: 5px;
padding-left: 5px;
}

View File

@ -163,7 +163,7 @@ textarea.form-control-sm {
min-height: 3rem;
}
.wrapper-scroll-table {
body.sticky .wrapper-scroll-table {
position: absolute;
margin-right: calc(var(--indent-bootstrap-row) * -1);
margin-left: calc(var(--indent-bootstrap-row) * -1);

View File

@ -1,6 +1,10 @@
#header {
}
#header form{
width: 100%;
}
.container-fluid {
/* padding-left: 0;
padding-right: 0;*/

View File

@ -380,7 +380,6 @@ th.table-th-sort-rev span.table-th-sort-span {
}
#content {
width: 96%;
margin: 0 auto 8px auto;
line-height: 130%;
text-align: center;
@ -399,14 +398,6 @@ th.table-th-sort-rev span.table-th-sort-span {
margin-bottom: 8px;
}
/*
#contentDiv {
margin: 0 auto 8px;
line-height: 140%;
text-align: center;
}
*/
#content > input[type=submit],
#content > input[type=button],
#content > button,

View File

@ -1,14 +1,7 @@
#header {
width: 99%;
}
#layout {
margin-right: 10px;
}
#content {
width: 99%;
}
#monitors .monitor {
min-width: 180px;
}
@ -76,4 +69,3 @@
stroke: #0000FF;
fill: #0000FF;
}

View File

@ -24,6 +24,7 @@
:root {
--scrollbarBG: #CFD8DC;
--sliderBG: #95AFC0;
--alarmBG: #352424;
}
body {
@ -59,9 +60,6 @@ img.normal {
border: #cccccc solid 1px;
}
hr {
}
ul.tabList li {
color: #8f8fc2;
border: #8f8fc2 solid 1px;
@ -205,6 +203,18 @@ input[type=submit]:disabled,
background-color: #444444;
}
.bootstrap-table .fixed-table-container .table thead th .both {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC")
}
.bootstrap-table .fixed-table-container .table thead th .asc {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAACQSURBVHja7NKhDcJQFIXhVwNJE6bAMUNN3VkBiUazRxkAdZZAdYrfVeIRGFLMw7RJ02D6gIDoTX5zxadOiDGGTxW+jg0PK8eqsBbDfyp2wHpg7d/CsDZYF6yI1WCtkzCsDOvUQX1HrCwFK7GuI+yGVUzCsFZY9QjqO2Mtp2A7rBbr/qIWa/ubnc3YjP0z9hwA30ri1Ip5X+oAAAAASUVORK5CYII=")
}
.bootstrap-table .fixed-table-container .table thead th .desc {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAACUSURBVHja7NIhDsJAFIThqWkTEk5Rxxlq6uYKSHQ194ADoOYSKE4xDolH1JBiFrNNSFPRbSoIqfjNvORTDyEELBVWbMX+ArO4t9hZfI3UWTykYIXFq8Uw0s3idjIGABYri+0AelqsASAVyyyeB9jFYpaMRbC0eI/Qw+KuvyVjEWwsvi0ev/e5WG7xZHEzC/uJp/0MAFmI4tSJnjqtAAAAAElFTkSuQmCC")
}
.nav-pills>li.active>a, .nav-pills>li.active>a:focus, .nav-pills>li.active>a:hover {
color: #ffa801;
background-color: #333333;
@ -229,6 +239,7 @@ li.search-choice {
background-color: #444444 !important;
color: #eee !important;
-webkit-box-shadow: none !important;
box-shadow: none !important;
background-image: none !important;
}
@ -299,4 +310,3 @@ html, body, div, nav, .chosen-results {
border-radius: 6px;
border: 3px solid var(--scrollbarBG);
}

View File

@ -3,7 +3,7 @@
}
tr.alarm {
background-color: #fa8072;
background-color: var(--alarmBG);
}
tr.bulk {

View File

@ -24,7 +24,7 @@ span.alert {
}
#eventList tr.recent {
background-color: #B0E0E6;
background-color: var(--alarmBG);
}
#eventList tr.highlight {

View File

@ -261,6 +261,22 @@ if ( currentView != 'none' && currentView != 'login' ) {
$j.ajaxSetup({timeout: AJAX_TIMEOUT}); //sets timeout for all getJSON.
$j(document).ready(function() {
// List of functions that are allowed to be called via the value of an object's DOM attribute.
const safeFunc = {
drawGraph: function() {
if (typeof drawGraph !== 'undefined' && $j.isFunction(drawGraph)) drawGraph();
},
refreshWindow: function() {
if (typeof refreshWindow !== 'undefined' && $j.isFunction(refreshWindow)) refreshWindow();
},
changeScale: function() {
if (typeof changeScale !== 'undefined' && $j.isFunction(changeScale)) changeScale();
},
applyChosen: function() {
if (typeof applyChosen !== 'undefined' && $j.isFunction(applyChosen)) applyChosen();
}
};
// Load the Logout and State modals into the dom
$j('#logoutButton').click(clickLogout);
if ( canEdit.System ) $j('#stateModalBtn').click(getStateModal);
@ -295,24 +311,44 @@ if ( currentView != 'none' && currentView != 'login' ) {
const objIconButton = _this_.find("i");
const obj = $j(_this_.attr('data-flip-сontrol-object'));
obj.removeClass('hidden');
if ( obj.is(":visible") ) {
if (objIconButton.is('[class="material-icons"]')) { // use material-icons
if ( obj.is(":visible") && !obj.hasClass("hidden-shift")) {
if (objIconButton.is('[class~="material-icons"]')) { // use material-icons
objIconButton.html(objIconButton.attr('data-icon-hidden'));
} else if (objIconButton.is('[class^="fa-"]')) { //use Font Awesome
} else if (objIconButton.is('[class*="fa-"]')) { //use Font Awesome
objIconButton.removeClass(objIconButton.attr('data-icon-visible')).addClass(objIconButton.attr('data-icon-hidden'));
}
setCookie('zmFilterBarFlip'+_this_.attr('data-flip-сontrol-object'), 'hidden');
} else { //hidden
if (objIconButton.is('[class="material-icons"]')) { // use material-icons
obj.removeClass('hidden-shift').addClass('hidden'); //It is necessary to make the block invisible both for JS and for humans
if (objIconButton.is('[class~="material-icons"]')) { // use material-icons
objIconButton.html(objIconButton.attr('data-icon-visible'));
} else if (objIconButton.is('[class^="fa-"]')) { //use Font Awesome
} else if (objIconButton.is('[class*="fa-"]')) { //use Font Awesome
objIconButton.removeClass(objIconButton.attr('data-icon-hidden')).addClass(objIconButton.attr('data-icon-visible'));
}
setCookie('zmFilterBarFlip'+_this_.attr('data-flip-сontrol-object'), 'visible');
}
obj.slideToggle("fast");
const nameFuncBefore = _this_.attr('data-flip-сontrol-run-before-func') ? _this_.attr('data-flip-сontrol-run-before-func') : null;
const nameFuncAfter = _this_.attr('data-flip-сontrol-run-after-func') ? _this_.attr('data-flip-сontrol-run-after-func') : null;
const nameFuncAfterComplet = _this_.attr('data-flip-сontrol-run-after-complet-func') ? _this_.attr('data-flip-сontrol-run-after-complet-func') : null;
if (nameFuncBefore) {
$j.each(nameFuncBefore.split(' '), function(i, nameFunc) {
if (typeof safeFunc[nameFunc] === 'function') safeFunc[nameFunc]();
});
}
obj.slideToggle("fast", function() {
if (nameFuncAfterComplet) {
$j.each(nameFuncAfterComplet.split(' '), function(i, nameFunc) {
if (typeof safeFunc[nameFunc] === 'function') safeFunc[nameFunc]();
});
}
});
if (nameFuncAfter) {
$j.each(nameFuncAfter.split(' '), function(i, nameFunc) {
if (typeof safeFunc[nameFunc] === 'function') safeFunc[nameFunc]();
});
}
});
// Manage visible filter bar & control button (after document ready)
@ -327,26 +363,28 @@ if ( currentView != 'none' && currentView != 'login' ) {
}
if (сookie == 'hidden') {
if (objIconButton.is('[class="material-icons"]')) { // use material-icons
if (objIconButton.is('[class~="material-icons"]')) { // use material-icons
objIconButton.html(objIconButton.attr('data-icon-hidden'));
} else if (objIconButton.is('[class^="fa-"]')) { //use Font Awesome
} else if (objIconButton.is('[class*="fa-"]')) { //use Font Awesome
objIconButton.addClass(objIconButton.attr('data-icon-hidden'));
}
obj.css({'display': 'none'});
obj.addClass('hidden-shift'); //To prevent jerking when running the "Chosen" script, it is necessary to make the block visible to JS, but invisible to humans!
} else { //no cookies or opened.
if (objIconButton.is('[class="material-icons"]')) { // use material-icons
if (objIconButton.is('[class~="material-icons"]')) { // use material-icons
objIconButton.html(objIconButton.attr('data-icon-visible'));
} else if (objIconButton.is('[class^="fa-"]')) { //use Font Awesome
} else if (objIconButton.is('[class*="fa-"]')) { //use Font Awesome
objIconButton.addClass(objIconButton.attr('data-icon-visible'));
}
obj.css({'display': 'block'});
obj.removeClass('hidden-shift');
}
});
// Manage the web console filter bar minimize chevron
$j("#mfbflip").click(function() {
/*$j("#mfbflip").click(function() {
$j("#mfbpanel").slideToggle("slow", function() {
changeScale();
if ($j.isFunction('changeScale')) {
changeScale();
}
});
var mfbflip = $j("#mfbflip");
if ( mfbflip.html() == 'keyboard_arrow_up' ) {
@ -358,7 +396,7 @@ if ( currentView != 'none' && currentView != 'login' ) {
$j('.chosen').chosen("destroy");
$j('.chosen').chosen();
}
});
});*/
// Autoclose the hamburger button if the end user clicks outside the button
$j(document).click(function(event) {
var target = $j(event.target);
@ -1106,9 +1144,9 @@ function applyChosen() {
const limit_search_threshold = 10;
$j('.chosen').chosen('destroy');
$j('.chosen').not('.chosen-full-width, .chosen-auto-width').chosen({disable_search_threshold: limit_search_threshold});
$j('.chosen.chosen-full-width').chosen({disable_search_threshold: limit_search_threshold, width: "100%"});
$j('.chosen.chosen-auto-width').chosen({disable_search_threshold: limit_search_threshold, width: "auto"});
$j('.chosen').not('.chosen-full-width, .chosen-auto-width').chosen({disable_search_threshold: limit_search_threshold, search_contains: true});
$j('.chosen.chosen-full-width').chosen({disable_search_threshold: limit_search_threshold, search_contains: true, width: "100%"});
$j('.chosen.chosen-auto-width').chosen({disable_search_threshold: limit_search_threshold, search_contains: true, width: "auto"});
}
const font = new FontFaceObserver('Material Icons', {weight: 400});

View File

@ -61,13 +61,14 @@ if (canView('Groups')) {
}
if (count($GroupsById)) {
$html .= '<span id="groupControl"><label>'. translate('Group') .'</label>';
$html .= '<span class="term" id="groupControl"><label>'. translate('Group') .'</label>';
$html .= '<span class="term-value-wrapper">';
# This will end up with the group_id of the deepest selection
$group_id = isset($_SESSION['GroupId']) ? $_SESSION['GroupId'] : null;
$html .= ZM\Group::get_group_dropdown();
$groupSql = ZM\Group::get_group_sql($group_id);
$html .= '</span>
';
$html .= '</span>';
$html .= '</span>';
}
}
@ -99,12 +100,14 @@ if (count($user->unviewableMonitorIds()) ) {
$values = array_merge($values, $ids);
}
$html .= '<span class="MonitorNameFilter"><label>'.translate('Name').'</label>';
$html .= '<input type="text" name="MonitorName" value="'.(isset($_SESSION['MonitorName'])?validHtmlStr($_SESSION['MonitorName']):'').'" placeholder="text or regular expression"/>';
$html .= '<span class="term MonitorNameFilter"><label>'.translate('Name').'</label>';
$html .= '<span class="term-value-wrapper">';
$html .= '<input type="text" name="MonitorName" value="'.(isset($_SESSION['MonitorName'])?validHtmlStr($_SESSION['MonitorName']):'').'" placeholder="text or regular expression"/></span>';
$html .= '</span>'.PHP_EOL;
function addFilterSelect($name, $options) {
$html = '<span class="'.$name.'Filter"><label>'.translate($name).'</label>';
$html = '<span class="term '.$name.'Filter"><label>'.translate($name).'</label>';
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect($name.'[]', $options,
(isset($_SESSION[$name])?$_SESSION[$name]:''),
array(
@ -113,7 +116,8 @@ function addFilterSelect($name, $options) {
'multiple'=>'multiple',
'data-placeholder'=>'All',
)
);
);
$html .= '</span>';
$html .= '</span>'.PHP_EOL;
return $html;
}
@ -123,7 +127,8 @@ $html .= addFilterSelect('Analysing', array('None'=>translate('None'), 'Always'=
$html .= addFilterSelect('Recording', array('None'=>translate('None'), 'OnMotion'=>translate('On Motion'),'Always'=>translate('Always')));
if ( count($ServersById) > 1 ) {
$html .= '<span class="ServerFilter"><label>'. translate('Server').'</label>';
$html .= '<span class="term ServerFilter"><label>'. translate('Server').'</label>';
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect('ServerId[]', $ServersById,
(isset($_SESSION['ServerId'])?$_SESSION['ServerId']:''),
array(
@ -133,12 +138,13 @@ if ( count($ServersById) > 1 ) {
'data-placeholder'=>'All',
)
);
$html .= '</span>
';
$html .= '</span>';
$html .= '</span>';
} # end if have Servers
if ( count($StorageById) > 1 ) {
$html .= '<span class="StorageFilter"><label>'.translate('Storage').'</label>';
$html .= '<span class="term StorageFilter"><label>'.translate('Storage').'</label>';
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect('StorageId[]', $StorageById,
(isset($_SESSION['StorageId'])?$_SESSION['StorageId']:''),
array(
@ -147,18 +153,19 @@ if ( count($StorageById) > 1 ) {
'multiple'=>'multiple',
'data-placeholder'=>'All',
) );
$html .= '</span>
';
$html .= '</span>';
$html .= '</span>';
} # end if have Storage Areas
$html .= '<span class="StatusFilter"><label>'.translate('Status').'</label>';
$html .= '<span class="term StatusFilter"><label>'.translate('Status').'</label>';
$status_options = array(
'Unknown' => translate('StatusUnknown'),
'NotRunning' => translate('StatusNotRunning'),
'Running' => translate('StatusRunning'),
'Connected' => translate('StatusConnected'),
);
$html .= htmlSelect( 'Status[]', $status_options,
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect( 'Status[]', $status_options,
( isset($_SESSION['Status']) ? $_SESSION['Status'] : '' ),
array(
'data-on-change'=>'submitThisForm',
@ -166,13 +173,14 @@ $html .= htmlSelect( 'Status[]', $status_options,
'multiple'=>'multiple',
'data-placeholder'=>'All'
) );
$html .= '</span>
';
$html .= '</span>';
$html .= '</span>';
$html .= '<span class="SourceFilter"><label>'.translate('Source').'</label>';
$html .= '<span class="term SourceFilter"><label>'.translate('Source').'</label>';
$html .= '<span class="term-value-wrapper">';
$html .= '<input type="text" name="Source" value="'.(isset($_SESSION['Source'])?validHtmlStr($_SESSION['Source']):'').'" placeholder="text or regular expression"/>';
$html .= '</span>
';
$html .= '</span>';
$html .= '</span>';
$sqlAll = 'SELECT M.*, S.*, E.*
FROM Monitors AS M
@ -262,7 +270,8 @@ $html .= '</span>
}
$displayMonitors[] = $monitors[$i];
} # end foreach monitor
$html .= '<span class="MonitorFilter"><label>'.translate('Monitor').'</label>';
$html .= '<span class="term MonitorFilter"><label>'.translate('Monitor').'</label>';
$html .= '<span class="term-value-wrapper">';
$html .= htmlSelect('MonitorId[]', $monitors_dropdown, $selected_monitor_ids,
array(
'data-on-change'=>'submitThisForm',
@ -272,8 +281,8 @@ $html .= '</span>
) );
# Repurpose this variable to be the list of MonitorIds as a result of all the filtering
$display_monitor_ids = array_map(function($monitor_row){return $monitor_row['Id'];}, $displayMonitors);
$html .= '</span>
';
$html .= '</span>';
$html .= '</span>';
echo $html;
?>
</div>

View File

@ -165,7 +165,7 @@ echo $navbar ?>
<form name="monitorForm" method="post" action="?view=<?php echo $view; ?>">
<input type="hidden" name="action" value=""/>
<div id="fbpanel" class="filterBar hidden">
<div id="fbpanel" class="filterBar hidden-shift">
<?php echo $filterbar ?>
</div>
@ -351,10 +351,10 @@ for ($monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1) {
if (ZM_WEB_LIST_THUMBS && ($monitor['Capturing'] != 'None') && canView('Stream')) {
$options = array();
$ratio_factor = $Monitor->ViewHeight() / $Monitor->ViewWidth();
$ratio_factor = $Monitor->ViewWidth() ? $Monitor->ViewHeight() / $Monitor->ViewWidth() : 1;
$options['width'] = ZM_WEB_LIST_THUMB_WIDTH;
$options['height'] = ZM_WEB_LIST_THUMB_HEIGHT ? ZM_WEB_LIST_THUMB_HEIGHT : ZM_WEB_LIST_THUMB_WIDTH*$ratio_factor;
$options['scale'] = intval(100*ZM_WEB_LIST_THUMB_WIDTH / $Monitor->ViewWidth());
$options['scale'] = $Monitor->ViewWidth() ? intval(100*ZM_WEB_LIST_THUMB_WIDTH / $Monitor->ViewWidth()) : 100;
$options['mode'] = 'jpeg';
$options['frames'] = 1;
@ -410,7 +410,7 @@ for ($monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1) {
echo translate('Analysing') . ': '.translate($monitor['Analysing']).'<br/>';
}
if ($monitor['Recording'] != 'None') {
echo translate('Recording'). ': '.translate($monitor['Recording']).'<br/>';
echo translate('Recording') . ': '.translate($monitor['Recording']) . ($monitor['ONVIF_Event_Listener'] ? ' Use ONVIF' : "") . '<br/>';
}
?><br/>
<div class="small text-nowrap text-muted">

View File

@ -33,6 +33,14 @@ var wasHidden = false;
var availableTags = [];
var selectedTags = [];
var PrevCoordinatFrame = {x: null, y: null};
var coordinateMouse = {
start_x: null, start_y: null,
shiftMouse_x: null, shiftMouse_y: null,
shiftMouseForTrigger_x: null, shiftMouseForTrigger_y: null
};
var leftBtnStatus = {Down: false, UpAfterDown: false};
$j(document).on("keydown", "", function(e) {
e = e || window.event;
if (!$j(".tag-input").is(":focus")) {
@ -71,6 +79,10 @@ $j(document).on("keydown", "", function(e) {
$j("#tagInput").focus();
showDropdown();
}
} else if (e.ctrlKey && (e.shift || e.shiftKey)) {
//Panning (moving the enlarged frame)
} else if (e.shift || e.shiftKey) {
//Panning
} else {
console.log('Modal is not visible: key not implemented: ', e.key, ' keyCode: ', e.keyCode);
}
@ -902,23 +914,101 @@ function handleClick(event) {
return; // ignore clicks on control bar
}
// target should be the img tag
if (!(event.ctrlKey && (event.shift || event.shiftKey))) {
const target = $j(event.target);
const width = target.width();
const height = target.height();
const scaleX = parseFloat(eventData.Width / width);
const scaleY = parseFloat(eventData.Height / height);
const pos = target.offset();
const x = parseInt((event.pageX - pos.left) * scaleX);
const y = parseInt((event.pageY - pos.top) * scaleY);
if (event.shift || event.shiftKey) { // handle both jquery and mootools
streamPan(x, y);
updatePrevCoordinatFrame(x, y); //Fixing current coordinates after scaling or shifting
} else if (event.ctrlKey) { // allow zoom out by control click. useful in fullscreen
streamZoomOut();
} else {
streamZoomIn(x, y);
updatePrevCoordinatFrame(x, y); //Fixing current coordinates after scaling or shifting
}
}
}
function shiftImgFrame() { //We calculate the coordinates of the image displacement and shift the image
let newPosX = parseInt(PrevCoordinatFrame.x - coordinateMouse.shiftMouse_x);
let newPosY = parseInt(PrevCoordinatFrame.y - coordinateMouse.shiftMouse_y);
if (newPosX < 0) newPosX = 0;
if (newPosX > eventData.Width) newPosX = eventData.Width;
if (newPosY < 0) newPosY = 0;
if (newPosY > eventData.Height) newPosY = eventData.Height;
streamPan(newPosX, newPosY);
updatePrevCoordinatFrame(newPosX, newPosY);
coordinateMouse.shiftMouseForTrigger_x = coordinateMouse.shiftMouseForTrigger_y = 0;
}
function updateCoordinateMouse(x, y) { //We fix the coordinates when pressing the left mouse button
coordinateMouse.start_x = x;
coordinateMouse.start_y = y;
}
function updatePrevCoordinatFrame(x, y) { //Update the Frame's current coordinates
PrevCoordinatFrame.x = x;
PrevCoordinatFrame.y = y;
}
function getCoordinateMouse(event) { //We get the current cursor coordinates taking into account the scale relative to the frame size.
const target = $j(event.target);
const width = target.width();
const height = target.height();
const scaleX = parseInt(eventData.Width / width);
const scaleY = parseInt(eventData.Height / height);
const scaleX = parseFloat(eventData.Width / target.width());
const scaleY = parseFloat(eventData.Height / target.height());
const pos = target.offset();
const x = parseInt((event.pageX - pos.left) * scaleX);
const y = parseInt((event.pageY - pos.top) * scaleY);
if (event.shift || event.shiftKey) { // handle both jquery and mootools
streamPan(x, y);
} else if (event.ctrlKey) { // allow zoom out by control click. useful in fullscreen
streamZoomOut();
return {x: parseInt((event.pageX - pos.left) * scaleX), y: parseInt((event.pageY - pos.top) * scaleY)}; //The point of the mouse click relative to the dimensions of the real frame.
}
function handleMove(event) {
if (event.ctrlKey && (event.shift || event.shiftKey)) {
document.ondragstart = function() {
return false;
}; //Allow drag and drop
} else {
streamZoomIn(x, y);
document.ondragstart = function() {}; //Prevent drag and drop
return false;
}
if (leftBtnStatus.Down) { //The left button was previously pressed and is now being held. Processing movement with a pressed button.
var {x, y} = getCoordinateMouse(event);
const k = Math.log(2.72) / Math.log(parseFloat($j('#zoomValue').html())) - 0.3; //Necessary for correctly shifting the image in accordance with the scaling proportions
coordinateMouse.shiftMouse_x = parseInt((x - coordinateMouse.start_x) * k);
coordinateMouse.shiftMouse_y = parseInt((y - coordinateMouse.start_y) * k);
coordinateMouse.shiftMouseForTrigger_x = Math.abs(parseInt(x - coordinateMouse.start_x));
coordinateMouse.shiftMouseForTrigger_y = Math.abs(parseInt(y - coordinateMouse.start_y));
}
if (event.buttons == 1 && leftBtnStatus.Down != true) { //Start of pressing left button
const {x, y} = getCoordinateMouse(event);
updateCoordinateMouse(x, y);
leftBtnStatus.Down = true;
} else if (event.buttons == 0 && leftBtnStatus.Down == true) { //Up left button after pressed
leftBtnStatus.Down = false;
leftBtnStatus.UpAfterDown = true;
}
if ((leftBtnStatus.UpAfterDown) || //The left button was raised or the cursor was moved more than 30 pixels relative to the actual size of the image
((coordinateMouse.shiftMouseForTrigger_x > 30) && leftBtnStatus.Down) ||
((coordinateMouse.shiftMouseForTrigger_y > 30) && leftBtnStatus.Down)) {
//We perform frame shift
shiftImgFrame();
updateCoordinateMouse(x, y);
leftBtnStatus.UpAfterDown = false;
}
}
@ -1084,6 +1174,9 @@ function initPage() {
vid.on('click', function(event) {
handleClick(event);
});
vid.on('mousemove', function(event) { // It is not clear whether it is necessary...
handleMove(event);
});
vid.on('volumechange', function() {
setCookie('volume', vid.volume());
});
@ -1118,6 +1211,9 @@ function initPage() {
$j(streamImg).click(function(event) {
handleClick(event);
});
$j(streamImg).mousemove(function(event) {
handleMove(event);
});
}
}
} // end if videojs or mjpeg stream

View File

@ -16,6 +16,14 @@ var classMonitorW_SB_L = 'col-sm-9'; /* id="wrapperMonitor" ONLY WITH LEFT */
var classMonitorW_SB_R = 'col-sm-10'; /* id="wrapperMonitor" ONLY WITH RIGHT */
var classMonitorWO_SB = 'col-sm-12'; /* id="wrapperMonitor" MAXIMUM width */
var PrevCoordinatFrame = {x: null, y: null};
var coordinateMouse = {
start_x: null, start_y: null,
shiftMouse_x: null, shiftMouse_y: null,
shiftMouseForTrigger_x: null, shiftMouseForTrigger_y: null
};
var leftBtnStatus = {Down: false, UpAfterDown: false};
/*
This is the format of the json object sent by bootstrap-table
@ -593,27 +601,105 @@ function fetchImage(streamImage) {
}
function handleClick(event) {
if (!(event.ctrlKey && (event.shift || event.shiftKey))) {
// target should be the img tag
const target = $j(event.target);
const width = target.width();
const height = target.height();
const target = $j(event.target);
const width = target.width();
const height = target.height();
const scaleX = parseInt(monitorWidth / width);
const scaleY = parseInt(monitorHeight / height);
const pos = target.offset();
const x = parseInt((event.pageX - pos.left) * scaleX);
const y = parseInt((event.pageY - pos.top) * scaleY);
const scaleX = parseFloat(monitorWidth / width);
const scaleY = parseFloat(monitorHeight / height);
const pos = target.offset();
const x = parseInt((event.pageX - pos.left) * scaleX);
const y = parseInt((event.pageY - pos.top) * scaleY);
if (showMode == 'events' || !imageControlMode) {
if ( event.shift || event.shiftKey ) {
streamCmdPan(x, y);
} else if (event.ctrlKey) {
streamCmdZoomOut();
if (showMode == 'events' || !imageControlMode) {
if (event.shift || event.shiftKey) {
streamCmdPan(x, y);
updatePrevCoordinatFrame(x, y); //Fixing current coordinates after scaling or shifting
} else if (event.ctrlKey) {
streamCmdZoomOut();
} else {
streamCmdZoomIn(x, y);
updatePrevCoordinatFrame(x, y); //Fixing current coordinates after scaling or shifting
}
} else {
streamCmdZoomIn(x, y);
controlCmdImage(x, y);
}
}
}
function shiftImgFrame() { //We calculate the coordinates of the image displacement and shift the image
let newPosX = parseInt(PrevCoordinatFrame.x - coordinateMouse.shiftMouse_x);
let newPosY = parseInt(PrevCoordinatFrame.y - coordinateMouse.shiftMouse_y);
if (newPosX < 0) newPosX = 0;
if (newPosX > monitorWidth) newPosX = monitorWidth;
if (newPosY < 0) newPosY = 0;
if (newPosY > monitorHeight) newPosY = monitorHeight;
streamCmdPan(newPosX, newPosY);
updatePrevCoordinatFrame(newPosX, newPosY);
coordinateMouse.shiftMouseForTrigger_x = coordinateMouse.shiftMouseForTrigger_y = 0;
}
function updateCoordinateMouse(x, y) { //We fix the coordinates when pressing the left mouse button
coordinateMouse.start_x = x;
coordinateMouse.start_y = y;
}
function updatePrevCoordinatFrame(x, y) { //Update the Frame's current coordinates
PrevCoordinatFrame.x = x;
PrevCoordinatFrame.y = y;
}
function getCoordinateMouse(event) { //We get the current cursor coordinates taking into account the scale relative to the frame size.
const target = $j(event.target);
const scaleX = parseFloat(monitorWidth / target.width());
const scaleY = parseFloat(monitorHeight / target.height());
const pos = target.offset();
return {x: parseInt((event.pageX - pos.left) * scaleX), y: parseInt((event.pageY - pos.top) * scaleY)}; //The point of the mouse click relative to the dimensions of the real frame.
}
function handleMove(event) {
if (event.ctrlKey && event.shiftKey) {
document.ondragstart = function() {
return false;
}; //Allow drag and drop
} else {
controlCmdImage(x, y);
document.ondragstart = function() {}; //Prevent drag and drop
return false;
}
if (leftBtnStatus.Down) { //The left button was previously pressed and is now being held. Processing movement with a pressed button.
var {x, y} = getCoordinateMouse(event);
const k = Math.log(2.72) / Math.log(parseFloat($j('#zoomValue'+monitorId).html())) - 0.3; //Necessary for correctly shifting the image in accordance with the scaling proportions
coordinateMouse.shiftMouse_x = parseInt((x - coordinateMouse.start_x) * k);
coordinateMouse.shiftMouse_y = parseInt((y - coordinateMouse.start_y) * k);
coordinateMouse.shiftMouseForTrigger_x = Math.abs(parseInt(x - coordinateMouse.start_x));
coordinateMouse.shiftMouseForTrigger_y = Math.abs(parseInt(y - coordinateMouse.start_y));
}
if (event.buttons == 1 && leftBtnStatus.Down != true) { //Start of pressing left button
const {x, y} = getCoordinateMouse(event);
updateCoordinateMouse(x, y);
leftBtnStatus.Down = true;
} else if (event.buttons == 0 && leftBtnStatus.Down == true) { //Up left button after pressed
leftBtnStatus.Down = false;
leftBtnStatus.UpAfterDown = true;
}
if ((leftBtnStatus.UpAfterDown) || //The left button was raised or the cursor was moved more than 30 pixels relative to the actual size of the image
((coordinateMouse.shiftMouseForTrigger_x > 30) && leftBtnStatus.Down) ||
((coordinateMouse.shiftMouseForTrigger_y > 30) && leftBtnStatus.Down)) {
//We perform frame shift
shiftImgFrame();
updateCoordinateMouse(x, y);
leftBtnStatus.UpAfterDown = false;
}
}
@ -754,19 +840,19 @@ function controlSetClicked() {
console.log('loading');
// Load the PTZ Preset modal into the DOM
$j.getJSON(monitorUrl + '?request=modal&modal=controlpreset&mid=' + monitorId+'&'+auth_relay)
.done(function(data) {
insertModalHtml('ctrlPresetModal', data.html);
updatePresetLabels();
// Manage the Preset Select box
$j('#preset').change(updatePresetLabels);
// Manage the Save button
$j('#cPresetSubmitModal').click(function(evt) {
evt.preventDefault();
$j('#ctrlPresetForm').submit();
});
$j('#ctrlPresetModal').modal('show');
})
.fail(logAjaxFail);
.done(function(data) {
insertModalHtml('ctrlPresetModal', data.html);
updatePresetLabels();
// Manage the Preset Select box
$j('#preset').change(updatePresetLabels);
// Manage the Save button
$j('#cPresetSubmitModal').click(function(evt) {
evt.preventDefault();
$j('#ctrlPresetForm').submit();
});
$j('#ctrlPresetModal').modal('show');
})
.fail(logAjaxFail);
} else {
console.log('not loading');
modal.modal('show');
@ -800,6 +886,7 @@ function initPage() {
monitorStream.setup_onclick(fetchImage);
} else {
monitorStream.setup_onclick(handleClick);
monitorStream.setup_onmove(handleMove);
}
monitorStream.setup_onpause(onPause);
monitorStream.setup_onplay(onPlay);

View File

@ -39,46 +39,58 @@ getBodyTopHTML();
<div id="toolbar">
<button id="backBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Back') ?>" disabled><i class="fa fa-arrow-left"></i></button>
<button id="refreshBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Refresh') ?>" ><i class="fa fa-refresh"></i></button>
<span class="ComponentFilter">
<div class="controlHeader">
<span class="term ComponentFilter">
<label><?php echo translate('Component') ?></label>
<?php
$components = dbFetchAll('SELECT DISTINCT Component FROM Logs ORDER BY Component', 'Component');
ZM\Debug(print_r($components, true));
$options = [''=>translate('All')] + array_combine($components, $components);
ZM\Debug(print_r($options, true));
echo htmlSelect('filterComponent', $options, '', array('id'=>'filterComponent'));
echo '<span class="term-value-wrapper">';
echo htmlSelect('filterComponent', $options, '', array('id'=>'filterComponent', 'class'=>'chosen'));
echo '</span>';
?>
</span>
<?php if (count($Servers)>1) { ?>
<span class="ServerFilter">
<span class="term ServerFilter">
<label><?php echo translate('Server') ?></label>
<?php
$ServersById = array(''=>translate('All')) + array_to_hash_by_key('Id', $Servers);
echo htmlSelect('filterServerId', $ServersById, '', array('id'=>'filterServerId'));
echo '<span class="term-value-wrapper">';
echo htmlSelect('filterServerId', $ServersById, '', array('id'=>'filterServerId', 'class'=>'chosen'));
echo '</span>';
?>
</span>
<?php } ?>
<span class="LevelFilter">
<span class="term LevelFilter">
<label><?php echo translate('Level') ?></label>
<?php
$levels = array(''=>translate('All'));
foreach (array_values(ZM\Logger::$codes) as $level) {
$levels[$level] = $level;
}
echo '<span class="term-value-wrapper">';
echo htmlSelect('filterLevel', $levels,
(isset($_SESSION['ZM_LOG_FILTER_LEVEL']) ? $_SESSION['ZM_LOG_FILTER_LEVEL'] : ''),
array('data-on-change'=>'filterLog', 'id'=>'filterLevel'));
array('data-on-change'=>'filterLog', 'id'=>'filterLevel', 'class'=>'chosen'));
#array('class'=>'form-control chosen', 'data-on-change'=>'filterLog'));
echo '</span>';
?>
</span>
<span class="StartDateTimeFilter">
<span class="term StartDateTimeFilter">
<label><?php echo translate('Start Date/Time') ?></label>
<input type="text" name="filterStartDateTime" id="filterStartDateTime" value=""/>
<span class="term-value-wrapper">
<input type="text" name="filterStartDateTime" id="filterStartDateTime" value=""/>
</span>
</span>
<span class="EndDateTimeFilter">
<span class="term EndDateTimeFilter">
<label><?php echo translate('End Date/Time') ?></label>
<input type="text" name="filterEndDateTime" id="filterEndDateTime" value=""/>
<span class="term-value-wrapper">
<input type="text" name="filterEndDateTime" id="filterEndDateTime" value=""/>
</span>
</span>
</div>
</div><!--toolbar-->
<table

View File

@ -1577,7 +1577,7 @@ echo htmlSelect('newMonitor[ReturnLocation]', $return_options, $monitor->ReturnL
</div><!--page-->
<script src="<?php echo cache_bust('js/MonitorLinkExpression.js') ?>"></script>
<script type="module" nonce="<?php echo $cspNonce ?>">
import DmsCoordinates, { parseDms } from "./js/dms.js";
import DmsCoordinates, {parseDms} from "./js/dms.js";
window.DmsCoordinates = DmsCoordinates;
window.parseDms = parseDms;
</script>

View File

@ -190,10 +190,13 @@ echo getNavBarHTML();
<div id="page">
<div id="header">
<?php
$html = '';
$flip = ( (!isset($_COOKIE['zmMonitorFilterBarFlip'])) or ($_COOKIE['zmMonitorFilterBarFlip'] == 'down')) ? 'up' : 'down';
$html .= '<a class="flip" href="#"><i id="mfbflip" class="material-icons md-18">keyboard_arrow_' .$flip. '</i></a>'.PHP_EOL;
$html .= '<div class="container-fluid" id="mfbpanel"'.( ( $flip == 'down' ) ? ' style="display:none;"' : '' ) .'>'.PHP_EOL;
$html = '<a class="flip" href="#"
data-flip-сontrol-object="#mfbpanel"
data-flip-сontrol-run-after-func="applyChosen"
data-flip-сontrol-run-after-complet-func="changeScale">
<i id="mfbflip" class="material-icons md-18" data-icon-visible="filter_alt_off" data-icon-hidden="filter_alt"></i>
</a>'.PHP_EOL;
$html .= '<div id="mfbpanel" class="hidden-shift container-fluid">'.PHP_EOL;
echo $html;
?>
<div id="headerButtons">

View File

@ -271,10 +271,13 @@ getBodyTopHTML();
<input type="hidden" name="view" value="montagereview"/>
<div id="header">
<?php
$html = '';
$flip = ( (!isset($_COOKIE['zmMonitorFilterBarFlip'])) or ($_COOKIE['zmMonitorFilterBarFlip'] == 'down')) ? 'up' : 'down';
$html .= '<a class="flip" href="#"><i id="mfbflip" class="material-icons md-18">keyboard_arrow_' .$flip. '</i></a>'.PHP_EOL;
$html .= '<div class="container-fluid" id="mfbpanel"'.( ( $flip == 'down' ) ? ' style="display:none;"' : '' ) .'>'.PHP_EOL;
$html = '<a class="flip" href="#"
data-flip-сontrol-object="#mfbpanel"
data-flip-сontrol-run-after-func="applyChosen drawGraph"
data-flip-сontrol-run-after-complet-func="changeScale">
<i id="mfbflip" class="material-icons md-18" data-icon-visible="filter_alt_off" data-icon-hidden="filter_alt"></i>
</a>'.PHP_EOL;
$html .= '<div id="mfbpanel" class="hidden-shift container-fluid">'.PHP_EOL;
echo $html;
?>
<?php echo $filter_bar ?>
@ -324,8 +327,11 @@ if (count($filter->terms())) {
?>
<button type="button" id="downloadVideo" data-on-click="click_download"><?php echo translate('Download Video') ?></button>
<?php } // end if !live ?>
<button type="button" id="collapse" data-flip-сontrol-object="#timelinediv" data-flip-сontrol-run-after-func="drawGraph"> <!-- OR run redrawScreen? -->
<i class="material-icons" data-icon-visible="history_toggle_off" data-icon-hidden="schedule"></i>
</button>
</div>
<div id="timelinediv">
<div id="timelinediv" class="hidden-shift">
<canvas id="timeline"></canvas>
<span id="scrubleft"></span>
<span id="scrubright"></span>

View File

@ -194,10 +194,13 @@ echo getNavBarHTML() ?>
<div id="page">
<div id="header">
<?php
$html = '';
$flip = ( (!isset($_COOKIE['zmMonitorFilterBarFlip'])) or ($_COOKIE['zmMonitorFilterBarFlip'] == 'down')) ? 'up' : 'down';
$html .= '<a class="flip" href="#"><i id="mfbflip" class="material-icons md-18">keyboard_arrow_' .$flip. '</i></a>'.PHP_EOL;
$html .= '<div class="container-fluid" id="mfbpanel"'.( ( $flip == 'down' ) ? ' style="display:none;"' : '' ) .'>'.PHP_EOL;
$html = '<a class="flip" href="#"
data-flip-сontrol-object="#mfbpanel"
data-flip-сontrol-run-after-func="applyChosen"
data-flip-сontrol-run-after-complet-func="changeScale">
<i id="mfbflip" class="material-icons md-18" data-icon-visible="filter_alt_off" data-icon-hidden="filter_alt"></i>
</a>'.PHP_EOL;
$html .= '<div id="mfbpanel" class="hidden-shift container-fluid">'.PHP_EOL;
echo $html;
?>
<div class="controlHeader">
@ -420,7 +423,7 @@ if ( canView('Events') && ($monitor->Type() != 'WebSite') ) {
data-show-refresh="true"
class="table-sm table-borderless"
>
<thead>
<thead class="thead-highlight">
<!-- Row styling is handled by bootstrap-tables -->
<tr>
<th data-sortable="false" data-field="Delete"><?php echo translate('Delete') ?></th>