Merge branch 'master' of github.com:ZoneMinder/zoneminder

pull/3090/head
Isaac Connor 2020-12-03 10:26:27 -05:00
commit c8aac87f74
29 changed files with 639 additions and 9788 deletions

View File

@ -148,6 +148,9 @@ set(ZM_CACHEDIR "/var/cache/zoneminder" CACHE PATH
"Location of the web server cache busting files, default: /var/cache/zoneminder")
set(ZM_CONTENTDIR "/var/lib/zoneminder" CACHE PATH
"Location of dynamic content (events and images), default: /var/lib/zoneminder")
set(ZM_FONTDIR "${CMAKE_INSTALL_FULL_DATADIR}/zoneminder/fonts" CACHE PATH
"Location of the font files used for timestamping, default: <prefix>/${CMAKE_INSTALL_DATADIR}/zoneminder/fonts")
set(ZM_DB_HOST "localhost" CACHE STRING
"Hostname where ZoneMinder database located, default: localhost")
set(ZM_DB_NAME "zm" CACHE STRING
@ -911,6 +914,7 @@ set(BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS_SAVED}")
add_subdirectory(src)
add_subdirectory(scripts)
add_subdirectory(db)
add_subdirectory(fonts)
add_subdirectory(web)
add_subdirectory(misc)
add_subdirectory(onvif)

5
fonts/CMakeLists.txt Normal file
View File

@ -0,0 +1,5 @@
# Glob all database upgrade scripts
file(GLOB fontfileslist RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "*.zmfnt")
# Install the fonts
install(FILES ${fontfileslist} DESTINATION "${ZM_FONTDIR}")

BIN
fonts/default.zmfnt Normal file

Binary file not shown.

View File

@ -3780,6 +3780,14 @@ our @options = (
type => $types{boolean},
category => 'logging',
},
{
name => 'ZM_FONT_FILE_LOCATION',
default => '@ZM_FONTDIR@/default.zmfnt',
description => 'Font file location',
help => 'This font is used for timestamp labels.',
type => $types{string},
category => 'config',
},
);
our %options_hash = map { ( $_->{name}, $_ ) } @options;

View File

@ -4,7 +4,7 @@
configure_file(zm_config_data.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config_data.h" @ONLY)
# Group together all the source files that are used by all the binaries (zmc, zma, zmu, zms etc)
set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_frame.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_input.cpp zm_ffmpeg_camera.cpp zm_group.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_libvnc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_nvsocket.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_swscale.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp zm_fifo.cpp zm_crypt.cpp)
set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_frame.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_input.cpp zm_ffmpeg_camera.cpp zm_group.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_libvnc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_font.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_nvsocket.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_swscale.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp zm_fifo.cpp zm_crypt.cpp)
# A fix for cmake recompiling the source files for every target.

File diff suppressed because it is too large Load Diff

72
src/zm_font.cpp Normal file
View File

@ -0,0 +1,72 @@
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include "zm.h"
#include "zm_font.h"
#include "zm_utils.h"
int ZmFont::ReadFontFile(const std::string &loc) {
FILE *f = fopen(loc.c_str(), "rb");
if ( !f ) return -1; // FILE NOT FOUND
struct stat st;
stat(loc.c_str(), &st);
font = new ZMFONT;
// MAGIC + pad + BitmapHeaders
size_t readsize = fread(&font[0], 1, 8 + (sizeof(ZMFONT_BH) * 4), f);
if ( readsize < 8 + (sizeof(ZMFONT_BH) * 4) ) {
delete font;
font = nullptr;
return -2; // EOF reached, invalid file
}
if ( memcmp(font->MAGIC, "ZMFNT", 5) != 0 ) // Check whether magic is correct
return -3;
for ( int i = 0; i < 4; i++ ) {
/* Character Width cannot be greater than 64 as a row is represented as a uint64_t,
height cannot be greater than 200(arbitary number which i have chosen, shouldn't need more than this) and
idx should not be more than filesize
*/
if ( (font->header[i].charWidth > 64 && font->header[i].charWidth == 0) || \
(font->header[i].charHeight > 200 && font->header[i].charHeight == 0) || \
(font->header[i].idx > st.st_size) ) {
delete font;
font = nullptr;
return -4;
}
}
datasize = st.st_size - (8 + sizeof(ZMFONT_BH) * 4);
font->data = new uint64_t[datasize/sizeof(uint64_t)];
readsize = fread(&font->data[0], 1, datasize, f);
if( readsize < datasize) { // Shouldn't happen
delete[] font->data;
font->data = nullptr;
delete font;
font = nullptr;
return -2;
}
fclose(f);
return 0;
}
ZmFont::~ZmFont() {
if ( font && font->data ) {
delete[] font->data;
font->data = nullptr;
}
if ( font ) {
delete font;
font = nullptr;
}
}
uint64_t *ZmFont::GetBitmapData() {
return &font->data[font->header[size].idx];
}

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,6 @@
//
#include "zm.h"
#include "zm_font.h"
#include "zm_bigfont.h"
#include "zm_image.h"
#include "zm_utils.h"
#include "zm_rgb.h"
@ -78,6 +77,9 @@ static deinterlace_4field_fptr_t fptr_deinterlace_4field_gray8;
/* Pointer to image buffer memory copy function */
imgbufcpy_fptr_t fptr_imgbufcpy;
/* Font */
static ZmFont font;
void Image::update_function_pointers() {
/* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */
if ( pixels % 16 || pixels % 12 ) {
@ -489,6 +491,12 @@ void Image::Initialise() {
g_u_table = g_u_table_global;
b_u_table = b_u_table_global;
int res = font.ReadFontFile(config.font_file_location);
if( res == -1 ) {
Panic("Invalid font location.");
} else if( res == -2 || res == -3 || res == -4 ) {
Panic("Invalid font file.");
}
initialised = true;
}
@ -1873,8 +1881,12 @@ const Coord Image::centreCoord( const char *text, int size=1 ) const {
line = text+index;
line_no++;
}
int x = (width - (max_line_len * ZM_CHAR_WIDTH * size) ) / 2;
int y = (height - (line_no * LINE_HEIGHT * size) ) / 2;
font.SetFontSize(size);
uint16_t char_width = font.GetCharWidth();
uint16_t char_height = font.GetCharHeight();
int x = (width - (max_line_len * char_width )) / 2;
int y = (height - (line_no * char_height) ) / 2;
return Coord(x, y);
}
@ -1920,8 +1932,10 @@ void Image::MaskPrivacy( const unsigned char *p_bitmask, const Rgb pixel_colour
}
/* RGB32 compatible: complete */
void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int size, const Rgb fg_colour, const Rgb bg_colour )
{
/* Bitmap decoding trick has been adopted from here:
https://lemire.me/blog/2018/02/21/iterating-over-set-bits-quickly/
*/
void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int size, const Rgb fg_colour, const Rgb bg_colour ) {
strncpy(text, p_text, sizeof(text)-1);
unsigned int index = 0;
@ -1934,31 +1948,30 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int
const uint8_t fg_g_col = GREEN_VAL_RGBA(fg_colour);
const uint8_t fg_b_col = BLUE_VAL_RGBA(fg_colour);
const uint8_t fg_bw_col = fg_colour & 0xff;
const Rgb fg_rgb_col = rgb_convert(fg_colour,subpixelorder);
const bool fg_trans = (fg_colour == RGB_TRANSPARENT);
const Rgb fg_rgb_col = rgb_convert(fg_colour, subpixelorder);
const uint8_t bg_r_col = RED_VAL_RGBA(bg_colour);
const uint8_t bg_g_col = GREEN_VAL_RGBA(bg_colour);
const uint8_t bg_b_col = BLUE_VAL_RGBA(bg_colour);
const uint8_t bg_bw_col = bg_colour & 0xff;
const Rgb bg_rgb_col = rgb_convert(bg_colour,subpixelorder);
const Rgb bg_rgb_col = rgb_convert(bg_colour, subpixelorder);
const bool bg_trans = (bg_colour == RGB_TRANSPARENT);
int zm_text_bitmask = 0x80;
if ( size == 2 )
zm_text_bitmask = 0x8000;
font.SetFontSize(size);
const uint16_t char_width = font.GetCharWidth();
const uint16_t char_height = font.GetCharHeight();
const uint64_t *font_bitmap = font.GetBitmapData();
while ( (index < text_len) && (line_len = strcspn(line, "\n")) ) {
unsigned int line_width = line_len * ZM_CHAR_WIDTH * size;
unsigned int line_width = line_len * char_width;
unsigned int lo_line_x = coord.X();
unsigned int lo_line_y = coord.Y() + (line_no * LINE_HEIGHT * size);
unsigned int lo_line_y = coord.Y() + (line_no * char_height);
unsigned int min_line_x = 0;
unsigned int max_line_x = width - line_width;
unsigned int min_line_y = 0;
unsigned int max_line_y = height - (LINE_HEIGHT * size);
unsigned int max_line_y = height - char_height;
if ( lo_line_x > max_line_x )
lo_line_x = max_line_x;
@ -1970,7 +1983,7 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int
lo_line_y = min_line_y;
unsigned int hi_line_x = lo_line_x + line_width;
unsigned int hi_line_y = lo_line_y + (LINE_HEIGHT * size);
unsigned int hi_line_y = lo_line_y + char_height;
// Clip anything that runs off the right of the screen
if ( hi_line_x > width )
@ -1980,100 +1993,77 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int
if ( colours == ZM_COLOUR_GRAY8 ) {
unsigned char *ptr = &buffer[(lo_line_y*width)+lo_line_x];
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < (ZM_CHAR_HEIGHT * size); y++, r++, ptr += width ) {
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < char_height; y++, r++, ptr += width ) {
unsigned char *temp_ptr = ptr;
for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) {
int f;
if ( size == 2 ) {
if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) {
if ( line[c] > 0xFF ) {
Warning("Unsupported character %c in %s", line[c], line);
continue;
}
f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r];
} else {
if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) {
Warning("Unsupported character %c in %s", line[c], line);
continue;
}
f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r];
}
for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) {
if ( f & (zm_text_bitmask >> i) ) {
if ( !fg_trans )
*temp_ptr = fg_bw_col;
} else if ( !bg_trans ) {
*temp_ptr = bg_bw_col;
}
uint64_t f = font_bitmap[(line[c] * char_height) + r];
if ( !bg_trans ) memset(temp_ptr, bg_bw_col, char_width);
while ( f != 0 ) {
uint64_t t = f & -f;
int idx = char_width - __builtin_ctzll(f>>2);
*(temp_ptr + idx) = fg_bw_col;
f ^= t;
}
temp_ptr += char_width;
}
}
} else if ( colours == ZM_COLOUR_RGB24 ) {
unsigned int wc = width * colours;
unsigned char *ptr = &buffer[((lo_line_y*width)+lo_line_x)*colours];
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < (ZM_CHAR_HEIGHT * size); y++, r++, ptr += wc ) {
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < char_height; y++, r++, ptr += wc ) {
unsigned char *temp_ptr = ptr;
for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) {
int f;
if ( size == 2 ) {
if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) {
if ( line[c] > 0xFF ) {
Warning("Unsupported character %c in %s", line[c], line);
continue;
}
f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r];
} else {
if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) {
Warning("Unsupported character %c in %s", line[c], line);
continue;
}
f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r];
}
for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr += colours ) {
if ( f & (zm_text_bitmask >> i) ) {
if ( !fg_trans ) {
RED_PTR_RGBA(temp_ptr) = fg_r_col;
GREEN_PTR_RGBA(temp_ptr) = fg_g_col;
BLUE_PTR_RGBA(temp_ptr) = fg_b_col;
uint64_t f = font_bitmap[(line[c] * char_height) + r];
if ( !bg_trans ) {
for( int i = 0; i < char_width; i++ ) { // We need to set individual r,g,b components
RED_PTR_RGBA((temp_ptr + (i*3))) = bg_r_col;
GREEN_PTR_RGBA((temp_ptr + (i*3))) = bg_g_col;
BLUE_PTR_RGBA((temp_ptr + (i*3))) = bg_b_col;
}
} else if ( !bg_trans ) {
RED_PTR_RGBA(temp_ptr) = bg_r_col;
GREEN_PTR_RGBA(temp_ptr) = bg_g_col;
BLUE_PTR_RGBA(temp_ptr) = bg_b_col;
}
}
while ( f != 0 ) {
uint64_t t = f & -f;
int idx = char_width - __builtin_ctzll(f >> 2);
RED_PTR_RGBA((temp_ptr + (idx*3))) = fg_r_col;
GREEN_PTR_RGBA((temp_ptr + (idx*3))) = fg_g_col;
BLUE_PTR_RGBA((temp_ptr + (idx*3))) = fg_b_col;
f ^= t;
}
temp_ptr += char_width * colours;
}
}
} else if ( colours == ZM_COLOUR_RGB32 ) {
unsigned int wc = width * colours;
uint8_t *ptr = &buffer[((lo_line_y*width)+lo_line_x)<<2];
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < (ZM_CHAR_HEIGHT * size); y++, r++, ptr += wc ) {
uint8_t *ptr = &buffer[((lo_line_y*width)+lo_line_x) << 2];
for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < char_height; y++, r++, ptr += wc ) {
Rgb* temp_ptr = (Rgb*)ptr;
for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) {
int f;
if ( size == 2 ) {
if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) {
if ( line[c] > 0xFF ) {
Warning("Unsupported character %c in %s", line[c], line);
continue;
}
f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r];
} else {
if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) {
Warning("Unsupported character %c in %s", line[c], line);
continue;
uint64_t f = font_bitmap[(line[c] * char_height) + r];
if ( !bg_trans ) {
for( int i = 0; i < char_width; i++ )
*(temp_ptr + i) = bg_rgb_col;
}
f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r];
}
for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) {
if ( f & (zm_text_bitmask >> i) ) {
if ( !fg_trans ) {
*temp_ptr = fg_rgb_col;
}
} else if ( !bg_trans ) {
*temp_ptr = bg_rgb_col;
while ( f != 0 ) {
uint64_t t = f & -f;
int idx = char_width - __builtin_ctzll(f >> 2);
*(temp_ptr + idx) = fg_rgb_col;
f ^= t;
}
temp_ptr += char_width;
}
}
}
} else {

View File

@ -0,0 +1,27 @@
<?php
if ( !canEdit('Events') ) return;
$eid = isset($_REQUEST['eid']) ? $_REQUEST['eid'] : '';
$eid = validInt($eid);
$Event = new ZM\Event($eid);
?>
<div class="modal" id="eventRenameModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><?php echo translate('Rename') .' '. translate('Event') ?></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<input type="text" value="<?php echo validHtmlStr($Event->Name()) ?>"/>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" id="eventRenameBtn"><?php echo translate('Save') ?></button>
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?php echo translate('Cancel') ?></button>
</div>
</div>
</div>
</div>

View File

@ -330,7 +330,7 @@ function collectData() {
foreach ( $postFuncs as $element=>$func )
$sqlData[$element] = eval('return( '.$func.'( $sqlData ) );');
$data[] = $sqlData;
if ( isset($limi) && ++$count >= $limit )
if ( isset($limit) && ++$count >= $limit )
break;
} # end foreach
} # end if have limit == 1
@ -364,6 +364,7 @@ switch ( $_REQUEST['layout'] ) {
$response = array( strtolower(validJsStr($_REQUEST['entity'])) => $data );
if ( isset($_REQUEST['loopback']) )
$response['loopback'] = validJsStr($_REQUEST['loopback']);
#ZM\Warning(print_r($response, true));
ajaxResponse($response);
break;
}

View File

@ -571,3 +571,6 @@ li.search-choice {
}
/* end chosen override */
modal-content {
background-color: #222222;
}

View File

@ -83,7 +83,7 @@ function exportEventDetail($event, $exportFrames, $exportImages) {
<tr><th scope="row"><?php echo translate('Monitor') ?></th><td><?php echo validHtmlStr($event->Monitor()->Name()) ?> (<?php echo $event->MonitorId() ?>)</td></tr>
<tr><th scope="row"><?php echo translate('Cause') ?></th><td><?php echo validHtmlStr($event->Cause()) ?></td></tr>
<tr><th scope="row"><?php echo translate('Notes') ?></th><td><?php echo validHtmlStr($event->Notes()) ?></td></tr>
<tr><th scope="row"><?php echo translate('Time') ?></th><td><?php echo strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->StartTime())) ?></td></tr>
<tr><th scope="row"><?php echo translate('Time') ?></th><td><?php echo strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->StartDateTime())) ?></td></tr>
<tr><th scope="row"><?php echo translate('Duration') ?></th><td><?php echo $event->Length() ?></td></tr>
<tr><th scope="row"><?php echo translate('Frames') ?></th><td><?php echo $event->Frames() ?></td></tr>
<tr><th scope="row"><?php echo translate('AttrAlarmFrames') ?></th><td><?php echo $event->AlarmFrames() ?></td></tr>
@ -999,5 +999,5 @@ function exportEvents(
unlink($monitorPath.'/'.$html_eventMaster);
}
return '?view=archive%26type='.$exportFormat.'%26connkey='.$connkey;
return '?view=archive&type='.$exportFormat.'&connkey='.$connkey;
} // end function exportEvents

View File

@ -497,9 +497,9 @@ function getBandwidthHTML($bandwidth_options, $user) {
}
$result = '<li id="getBandwidthHTML" class="nav-item dropdown mx-2">'.PHP_EOL;
$result .= '<a class="dropdown-toggle mr-2" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="dropdown_bandwidth"><i class="material-icons md-18 mr-1">network_check</i>'.translate($bandwidth_options[$_COOKIE['zmBandwidth']]).'</a>'.PHP_EOL;
$result .= '<a class="dropdown-toggle mr-2" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="material-icons md-18 mr-1">network_check</i>'.translate($bandwidth_options[$_COOKIE['zmBandwidth']]).'</a>'.PHP_EOL;
$result .= '<div class="dropdown-menu" aria-labelledby="dropdown_bandwidth">'.PHP_EOL;
$result .= '<div class="dropdown-menu" id="dropdown_bandwidth" aria-labelledby="dropdown_bandwidth">'.PHP_EOL;
if ( count($bandwidth_options) > 1 ) {
if ( isset($bandwidth_options['high']) )
$result .= '<a data-pdsa-dropdown-val="high" class="dropdown-item" href="#">' .translate('High'). '</a>'.PHP_EOL;

View File

@ -830,10 +830,11 @@ function startDownload( exportFile ) {
}
function exportResponse(data, responseText) {
console.log(data);
console.log('exportResponse data: ' + JSON.stringify(data));
var generated = (data.result=='Ok') ? 1 : 0;
var exportFile = '?view=archive&type='+data.exportFormat+'&connkey='+data.connkey;
//var exportFile = '?view=archive&type='+data.exportFormat+'&connkey='+data.connkey;
var exportFile = data.exportFile;
$j('#exportProgress').removeClass( 'text-warning' );
if ( generated ) {

View File

@ -42,7 +42,7 @@ xhtmlHeaders(__FILE__, translate('ControlCaps'));
<button id="deleteBtn" class="btn btn-danger" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Delete') ?>" disabled><i class="fa fa-trash"></i></button>
</div>
<div id="content">
<div id="content" class="table-responsive-sm">
<table
id="controlTable"
data-locale="<?php echo i18n() ?>"
@ -54,7 +54,6 @@ xhtmlHeaders(__FILE__, translate('ControlCaps'));
data-remember-order="true"
data-click-to-select="true"
data-maintain-meta-data="true"
data-mobile-responsive="true"
data-buttons-class="btn btn-normal"
data-toolbar="#toolbar"
data-show-columns="true"

View File

@ -46,7 +46,7 @@ xhtmlHeaders(__FILE__, translate('Devices') );
<button id="deleteBtn" class="btn btn-danger" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Delete') ?>" disabled><i class="fa fa-trash"></i></button>
</div>
<div id="content" class="row justify-content-center">
<div id="content" class="row justify-content-center table-responsive-sm">
<table
id="devicesTable"
data-locale="<?php echo i18n() ?>"
@ -59,7 +59,6 @@ xhtmlHeaders(__FILE__, translate('Devices') );
data-remember-order="true"
data-click-to-select="true"
data-maintain-meta-data="true"
data-mobile-responsive="true"
data-buttons-class="btn btn-normal"
data-toolbar="#toolbar"
data-show-columns="true"

View File

@ -120,86 +120,113 @@ $filterQuery = $filter->querystring();
$connkey = generateConnKey();
$focusWindow = true;
$popup = (isset($_REQUEST['popup']) && ($_REQUEST['popup'] == 1));
xhtmlHeaders(__FILE__, translate('Event'));
xhtmlHeaders(__FILE__, translate('Event').' '.$Event->Id());
?>
<body>
<div id="page">
<?php if ( !$popup ) echo getNavBarHTML() ?>
<div id="header">
<?php echo getNavBarHTML() ?>
<?php
if ( !$Event->Id() ) {
echo 'Event was not found.';
} else {
if ( !file_exists($Event->Path()) ) {
if ( !file_exists($Event->Path()) )
echo '<div class="error">Event was not found at '.$Event->Path().'. It is unlikely that playback will be possible.</div>';
}
$storage = validHtmlStr($Event->Storage()->Name()).( $Event->SecondaryStorageId() ? ', '.validHtmlStr($Event->SecondaryStorage()->Name()) : '' );
?>
<div id="dataBar">
<span id="dataId" title="<?php echo translate('Id') ?>"><?php echo $Event->Id() ?></span>
<span id="dataMonitor" title="<?php echo translate('Monitor') ?>"><?php echo $Monitor->Id().' '.validHtmlStr($Monitor->Name()) ?></span>
<span id="dataCause" title="<?php echo $Event->Notes()?validHtmlStr($Event->Notes()):translate('AttrCause') ?>"><?php echo validHtmlStr($Event->Cause()) ?></span>
<span id="dataTime" title="<?php echo translate('Time') ?>"><?php echo strftime(STRF_FMT_DATETIME_SHORT, strtotime($Event->StartDateTime())) ?></span>
<span id="dataDuration" title="<?php echo translate('Duration') ?>"><?php echo $Event->Length().'s' ?></span>
<span id="dataFrames" title="<?php echo translate('AttrFrames').'/'.translate('AttrAlarmFrames') ?>"><?php echo $Event->Frames() ?>/<?php echo $Event->AlarmFrames() ?></span>
<span id="dataScore" title="<?php echo translate('AttrTotalScore').'/'.translate('AttrAvgScore').'/'.translate('AttrMaxScore') ?>"><?php echo $Event->TotScore() ?>/<?php echo $Event->AvgScore() ?>/<?php echo $Event->MaxScore() ?></span>
<span id="Storage">
<?php echo
human_filesize($Event->DiskSpace(null)) . ' on ' . validHtmlStr($Event->Storage()->Name()).
( $Event->SecondaryStorageId() ? ', '.validHtmlStr($Event->SecondaryStorage()->Name()) : '' )
?></span>
<div id="closeWindow"><a href="#" data-on-click="<?php echo $popup ? 'closeWindow' : 'backWindow' ?>"><?php echo $popup ? translate('Close') : translate('Back') ?></a></div>
<!-- BEGIN HEADER -->
<div class="d-flex flex-row justify-content-between px-3 py-1">
<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>
<button id="renameBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Rename') ?>" disabled><i class="fa fa-font"></i></button>
<button id="archiveBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Archive') ?>" disabled><i class="fa fa-archive"></i></button>
<button id="unarchiveBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Unarchive') ?>" disabled><i class="fa fa-file-archive-o"></i></button>
<button id="editBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Edit') ?>" disabled><i class="fa fa-pencil"></i></button>
<button id="exportBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Export') ?>"><i class="fa fa-external-link"></i></button>
<button id="downloadBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('DownloadVideo') ?>"><i class="fa fa-download"></i></button>
<button id="statsBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Stats') ?>" ><i class="fa fa-info"></i></button>
<button id="deleteBtn" class="btn btn-danger" data-toggle="tooltip" data-placement="top" title="<?php echo translate('Delete') ?>"><i class="fa fa-trash"></i></button>
</div>
<div id="menuBar1">
<div id="nameControl">
<input type="text" id="eventName" name="eventName" value="<?php echo validHtmlStr($Event->Name()) ?>" />
<button value="Rename" type="button" data-on-click="renameEvent"<?php if ( !canEdit('Events') ) { ?> disabled="disabled"<?php } ?>>
<?php echo translate('Rename') ?></button>
</div>
<?php
if ( canEdit('Events') ) {
?>
<div id="deleteEvent"><button type="button" data-on-click="deleteEvent" <?php echo $Event->can_delete() ? '' : ' disabled="disabled" title="'.$Event->cant_delete_reason().'"' ?>><?php echo translate('Delete') ?></button></div>
<div id="editEvent"><button type="button" data-on-click="editEvent"><?php echo translate('Edit') ?></button></div>
<div id="archiveEvent"<?php echo $Event->Archived() == 1 ? ' class="hidden"' : '' ?>><button type="button" data-on-click="archiveEvent"><?php echo translate('Archive') ?></button></div>
<div id="unarchiveEvent"<?php echo $Event->Archived() == 0 ? ' class="hidden"' : '' ?>><button type="button" data-on-click="unarchiveEvent"><?php echo translate('Unarchive') ?></button></div>
<?php
} // end if can edit Events
?>
<div id="framesEvent"><button type="button" data-on-click="showEventFrames"><?php echo translate('Frames') ?></button></div>
<div id="streamEvent" class="hidden"><button data-on-click="showStream"><?php echo translate('Stream') ?></button></div>
<div id="stillsEvent"><button type="button" data-on-click="showStills"><?php echo translate('Stills') ?></button></div>
<?php
if ( $Event->DefaultVideo() ) {
?>
<div id="downloadEventFile"><a class="btn-primary" href="<?php echo $Event->getStreamSrc(array('mode'=>'mp4'),'&amp;')?>" download>Download MP4</a></div>
<?php
} else {
?>
<div id="videoEvent"><button type="button" data-on-click="videoEvent"><?php echo translate('Video') ?></button></div>
<?php
} // end if Event->DefaultVideo
?>
<div id="exportEvent"><button type="button" data-on-click="exportEvent"><?php echo translate('Export') ?></button></div>
<div id="replayControl">
<label for="replayMode"><?php echo translate('Replay') ?></label>
<?php echo htmlSelect('replayMode', $replayModes, $replayMode, array('data-on-change'=>'changeReplayMode','id'=>'replayMode')); ?>
</div>
<div id="scaleControl">
<label for="scale"><?php echo translate('Scale') ?></label>
<?php echo htmlSelect('scale', $scales, $scale, array('data-on-change'=>'changeScale','id'=>'scale')); ?>
</div>
<div id="codecControl">
<label for="codec"><?php echo translate('Codec') ?></label>
<?php echo htmlSelect('codec', $codecs, $codec, array('data-on-change'=>'changeCodec','id'=>'codec')); ?>
</div>
<h2><?php echo translate('Event').' '.$Event->Id() ?></h2>
<div class="d-flex flex-row">
<div id="replayControl"><label for="replayMode"><?php echo translate('Replay') ?></label><?php echo htmlSelect('replayMode', $replayModes, $replayMode, array('data-on-change'=>'changeReplayMode','id'=>'replayMode')); ?></div>
<div id="scaleControl"><label for="scale"><?php echo translate('Scale') ?></label><?php echo htmlSelect('scale', $scales, $scale, array('data-on-change'=>'changeScale','id'=>'scale')); ?></div>
<div id="codecControl"><label for="codec"><?php echo translate('Codec') ?></label><?php echo htmlSelect('codec', $codecs, $codec, array('data-on-change'=>'changeCodec','id'=>'codec')); ?></div>
</div>
</div>
<div id="content">
<!-- BEGIN VIDEO CONTENT ROW -->
<div id="content" class="d-flex flex-row justify-content-center">
<div class="">
<!-- VIDEO STATISTICS TABLE -->
<table id="eventStatsTable" class="table-sm table-borderless">
<tbody>
<tr>
<th class="text-right"><?php echo translate('EventId') ?></th>
<td id="dataEventId"><?php echo $Event->Id() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('EventName') ?></th>
<td id="dataEventName"><?php echo $Event->Name() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrMonitorId') ?></th>
<td id="dataMonitorId"><?php echo $Monitor->Id() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrMonitorName') ?></th>
<td id="dataMonitorName"><?php echo validHtmlStr($Monitor->Name()) ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('Cause') ?></th>
<td id="dataCause"><?php echo validHtmlStr($Event->Cause()) ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrStartTime') ?></th>
<td id="dataStartTime"><?php echo strftime(STRF_FMT_DATETIME_SHORT, strtotime($Event->StartDateTime())) ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('Duration') ?></th>
<td id="dataDuration"><?php echo $Event->Length().'s' ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrFrames') ?></th>
<td id="dataFrames"><?php echo $Event->Frames() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrAlarmFrames') ?></th>
<td id="dataAlarmFrames"><?php echo $Event->AlarmFrames() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrTotalScore') ?></th>
<td id="dataTotalScore"><?php echo $Event->TotScore() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrAvgScore') ?></th>
<td id="dataAvgScore"><?php echo $Event->AvgScore() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('AttrMaxScore') ?></th>
<td id="dataMaxScore"><?php echo $Event->MaxScore() ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('DiskSpace') ?></th>
<td id="dataDiskSpace"><?php echo human_filesize($Event->DiskSpace(null)) ?></td>
</tr>
<tr>
<th class="text-right"><?php echo translate('Storage') ?></th>
<td id="dataStorage"><?php echo $storage?></td>
</tr>
</tbody>
</table>
</div>
<div class="">
<div id="eventVideo">
<!-- VIDEO CONTENT -->
<?php
if ( ($codec == 'MP4' || $codec == 'auto' ) && $Event->DefaultVideo() ) {
?>
@ -322,6 +349,8 @@ echo htmlSelect('rate', $rates, intval($rate), array('id'=>'rateValue'));
<?php
} // end if Event exists
?>
</div>
</div><!--content-->
</div><!--page-->
<?php xhtmlFooter() ?>

View File

@ -65,7 +65,7 @@ getBodyTopHTML();
</div>
<!-- Table styling handled by bootstrap-tables -->
<div class="row justify-content-center">
<div class="row justify-content-center table-responsive-sm">
<table
id="eventTable"
data-locale="<?php echo i18n() ?>"
@ -87,7 +87,6 @@ getBodyTopHTML();
data-show-fullscreen="true"
data-click-to-select="true"
data-maintain-meta-data="true"
data-mobile-responsive="true"
data-buttons-class="btn btn-normal"
data-show-jump-to="true"
data-show-refresh="true"

View File

@ -41,7 +41,7 @@ xhtmlHeaders(__FILE__, translate('Frames').' - '.$Event->Id());
</div>
<!-- Table styling handled by bootstrap-tables -->
<div class="row justify-content-center">
<div class="row justify-content-center table-responsive-sm">
<table
id="framesTable"
data-locale="<?php echo i18n() ?>"
@ -60,7 +60,6 @@ xhtmlHeaders(__FILE__, translate('Frames').' - '.$Event->Id());
data-toolbar="#toolbar"
data-show-fullscreen="true"
data-maintain-meta-data="true"
data-mobile-responsive="true"
data-buttons-class="btn btn-normal"
data-detail-view="true"
data-detail-formatter="detailFormatter"

View File

@ -1,7 +1,40 @@
$j.ajaxSetup({timeout: AJAX_TIMEOUT}); //sets timeout for all getJSON.
var table = $j('#eventStatsTable');
var backBtn = $j('#backBtn');
var renameBtn = $j('#renameBtn');
var archiveBtn = $j('#archiveBtn');
var unarchiveBtn = $j('#unarchiveBtn');
var editBtn = $j('#editBtn');
var exportBtn = $j('#exportBtn');
var downloadBtn = $j('#downloadBtn');
var deleteBtn = $j('#deleteBtn');
var prevEventId = 0;
var nextEventId = 0;
var prevEventStartTime = 0;
var nextEventStartTime = 0;
var PrevEventDefVideoPath = "";
var NextEventDefVideoPath = "";
var slider = null;
var scroll = null;
var currEventId = null;
var CurEventDefVideoPath = null;
var vid = null;
var spf = Math.round((eventData.Length / eventData.Frames)*1000000 )/1000000;//Seconds per frame for videojs frame by frame.
var intervalRewind;
var revSpeed = .5;
var cueFrames = null; //make cueFrames available even if we don't send another ajax query
var streamCmdTimer = null;
var streamStatus = null;
var lastEventId = 0;
var zmsBroke = false; //Use alternate navigation if zms has crashed
var streamParms = "view=request&request=stream&connkey="+connKey;
if ( auth_hash ) streamParms += '&auth='+auth_hash;
var frameBatch = 40;
var currFrameId = null;
var eventReq = new Request.JSON( {url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getEventResponse} );
var actReq = new Request.JSON( {url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getActResponse} );
var frameReq = new Request.JSON( {url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'chain', onSuccess: getFrameResponse} );
var streamReq = new Request.JSON( {url: monitorUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'chain', onSuccess: getCmdResponse} );
// Function called when video.js hits the end of the video
function vjsReplay() {
@ -44,10 +77,6 @@ function vjsReplay() {
}
} // end function vjsReplay
$j.ajaxSetup({timeout: AJAX_TIMEOUT}); //sets timeout for all getJSON.
var cueFrames = null; //make cueFrames available even if we don't send another ajax query
function initialAlarmCues(eventId) {
$j.getJSON(thisUrl + '?view=request&request=status&entity=frames&id=' + eventId, setAlarmCues) //get frames data for alarmCues and inserts into html
.fail(logAjaxFail);
@ -134,7 +163,6 @@ function renderAlarmCues(containerEl) {
return alarmHtml;
}
function changeCodec() {
location.replace(thisUrl + '?view=event&eid=' + eventData.Id + filterQuery + sortQuery+'&codec='+$j('#codec').val());
}
@ -217,16 +245,6 @@ function changeRate() {
Cookie.write('zmEventRate', rate, {duration: 10*365, samesite: 'strict'});
} // end function changeRate
var streamParms = "view=request&request=stream&connkey="+connKey;
if ( auth_hash ) {
streamParms += '&auth='+auth_hash;
}
var streamCmdTimer = null;
var streamStatus = null;
var lastEventId = 0;
var zmsBroke = false; //Use alternate navigation if zms has crashed
function getCmdResponse( respObj, respText ) {
if ( checkStreamForErrors('getCmdResponse', respObj) ) {
console.log('Got an error from getCmdResponse');
@ -291,14 +309,6 @@ function getCmdResponse( respObj, respText ) {
streamCmdTimer = streamQuery.delay(streamTimeout); //Timeout is refresh rate for progressBox and time display
} // end function getCmdResponse( respObj, respText )
var streamReq = new Request.JSON( {
url: monitorUrl,
method: 'get',
timeout: AJAX_TIMEOUT,
link: 'chain',
onSuccess: getCmdResponse
} );
function pauseClicked() {
if ( vid ) {
if ( intervalRewind ) {
@ -553,11 +563,6 @@ function streamQuery() {
streamReq.send( streamParms+"&command="+CMD_QUERY );
}
var slider = null;
var scroll = null;
var currEventId = null;
var CurEventDefVideoPath = null;
function getEventResponse(respObj, respText) {
if ( checkStreamForErrors('getEventResponse', respObj) ) {
console.log('getEventResponse: errors');
@ -572,28 +577,31 @@ function getEventResponse(respObj, respText) {
}
currEventId = eventData.Id;
$('dataId').set( 'text', eventData.Id );
$j('#dataEventId').text( eventData.Id );
$j('#dataEventName').text( eventData.Name );
$j('#dataMonitorId').text( eventData.MonitorId );
$j('#dataMonitorName').text( eventData.MonitorName );
$j('#dataCause').text( eventData.Cause );
if ( eventData.Notes ) {
$('dataCause').setProperty( 'title', eventData.Notes );
$j('#dataCause').prop( 'title', eventData.Notes );
} else {
$('dataCause').setProperty( 'title', causeString );
}
$('dataCause').set( 'text', eventData.Cause );
$('dataTime').set( 'text', eventData.StartDateTime );
$('dataDuration').set( 'text', eventData.Length );
$('dataFrames').set( 'text', eventData.Frames+"/"+eventData.AlarmFrames );
$('dataScore').set( 'text', eventData.TotScore+"/"+eventData.AvgScore+"/"+eventData.MaxScore );
$('eventName').setProperty( 'value', eventData.Name );
history.replaceState(null, null, '?view=event&eid=' + eventData.Id + filterQuery + sortQuery);//if popup removed, check if this allows forward
if ( canEditEvents ) {
if ( parseInt(eventData.Archived) ) {
$('archiveEvent').addClass( 'hidden' );
$('unarchiveEvent').removeClass( 'hidden' );
} else {
$('archiveEvent').removeClass( 'hidden' );
$('unarchiveEvent').addClass( 'hidden' );
}
$j('#dataCause').prop( 'title', causeString );
}
$j('#dataStartTime').text( eventData.StartDateTime );
$j('#dataDuration').text( eventData.Length );
$j('#dataFrames').text( eventData.Frames );
$j('#dataAlarmFrames').text( eventData.AlarmFrames );
$j('dataTotalScore').text( eventData.TotScore );
$j('dataAvgScore').text( eventData.AvgScore );
$j('dataMaxScore').text( eventData.MaxScore );
$j('dataDiskSpace').text( eventData.DiskSpace );
$j('dataStorage').text( eventData.Storage );
// Refresh the status of the archive buttons
archiveBtn.prop('disabled', !(!eventData.Archived && canEditEvents));
unarchiveBtn.prop('disabled', !(eventData.Archived && canEditEvents));
history.replaceState(null, null, '?view=event&eid=' + eventData.Id + filterQuery + sortQuery); //if popup removed, check if this allows forward
// Technically, events can be different sizes, so may need to update the size of the image, but it might be better to have it stay scaled...
//var eventImg = $('eventImage');
//eventImg.setStyles( { 'width': eventData.width, 'height': eventData.height } );
@ -613,8 +621,6 @@ function getEventResponse(respObj, respText) {
nearEventsQuery( eventData.Id );
} // end function getEventResponse
var eventReq = new Request.JSON( {url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getEventResponse} );
function eventQuery( eventId ) {
var eventParms = 'view=request&request=status&entity=event&id='+eventId;
if ( auth_hash ) {
@ -623,13 +629,6 @@ function eventQuery( eventId ) {
eventReq.send( eventParms );
}
var prevEventId = 0;
var nextEventId = 0;
var prevEventStartTime = 0;
var nextEventStartTime = 0;
var PrevEventDefVideoPath = "";
var NextEventDefVideoPath = "";
function getNearEventsResponse( respObj, respText ) {
if ( checkStreamForErrors('getNearEventsResponse', respObj) ) {
return;
@ -656,8 +655,6 @@ function nearEventsQuery( eventId ) {
nearEventsReq.send( parms );
}
var frameBatch = 40;
function loadEventThumb( event, frame, loadImage ) {
var thumbImg = $('eventThumb'+frame.FrameId);
if ( !thumbImg ) {
@ -789,15 +786,11 @@ function getFrameResponse(respObj, respText) {
loadEventThumb(eventData, frame, respObj.loopback=="true");
}
var frameReq = new Request.JSON( {url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'chain', onSuccess: getFrameResponse} );
function frameQuery( eventId, frameId, loadImage ) {
var parms = "view=request&request=status&entity=frameimage&id[0]="+eventId+"&id[1]="+frameId+"&loopback="+loadImage;
frameReq.send(parms);
}
var currFrameId = null;
function checkFrames( eventId, frameId, loadImage ) {
if ( !eventData ) {
console.error("No event "+eventId+" found");
@ -917,8 +910,6 @@ function getActResponse( respObj, respText ) {
}
}
var actReq = new Request.JSON( {url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getActResponse} );
function actQuery(action, parms) {
var actParms = "view=request&request=event&id="+eventData.Id+"&action="+action;
if ( auth_hash ) {
@ -930,55 +921,17 @@ function actQuery(action, parms) {
actReq.send(actParms);
}
function deleteEvent() {
pauseClicked(); //Provides visual feedback that your click happened.
var deleteReq = new Request.JSON({
url: thisUrl,
method: 'post',
timeout: AJAX_TIMEOUT,
onSuccess: function onDeleteSuccess(respObj, respText) {
getActResponse(respObj, respText);
// We must wait for the deletion to happen before navigating to the next
// event or this request will be cancelled.
streamNext(true);
},
});
deleteReq.send("view=request&request=event&id="+eventData.Id+"&action=delete");
}
function renameEvent() {
var newName = $('eventName').get('value');
var newName = $j('input').val();
actQuery('rename', {eventName: newName});
}
// Manage the EDIT button
function editEvent() {
$j.getJSON(thisUrl + '?request=modal&modal=eventdetail&eid='+eventData.Id)
.done(function(data) {
insertModalHtml('eventDetailModal', data.html);
$j('#eventDetailModal').modal('show');
// Manage the Save button
$j('#eventDetailSaveBtn').click(function(evt) {
evt.preventDefault();
$j('#eventDetailForm').submit();
});
})
.fail(logAjaxFail);
//FIXME: update the value of the event name rather than reload the whole page
window.location.reload(true);
}
function exportEvent() {
window.location.assign('?view=export&eid='+eventData.Id);
}
function archiveEvent() {
actQuery('archive');
}
function unarchiveEvent() {
actQuery('unarchive');
}
function showEventFrames() {
window.location.assign('?view=frames&eid='+eventData.Id);
}
@ -1079,7 +1032,56 @@ function handleClick( event ) {
}
}
// Load the Delete Confirmation Modal HTML via Ajax call
function getDelConfirmModal() {
$j.getJSON(thisUrl + '?request=modal&modal=delconfirm')
.done(function(data) {
insertModalHtml('deleteConfirm', data.html);
manageDelConfirmModalBtns();
})
.fail(logAjaxFail);
}
// Manage the DELETE CONFIRMATION modal button
function manageDelConfirmModalBtns() {
document.getElementById("delConfirmBtn").addEventListener("click", function onDelConfirmClick(evt) {
if ( ! canEditEvents ) {
enoperm();
return;
}
evt.preventDefault();
$j.getJSON(thisUrl + '?request=events&task=delete&eids[]='+eventData.Id)
.done( function(data) {
streamNext( true );
})
.fail(logAjaxFail);
});
// Manage the CANCEL modal button
document.getElementById("delCancelBtn").addEventListener("click", function onDelCancelClick(evt) {
$j('#deleteConfirm').modal('hide');
});
}
function getEvtStatsCookie() {
var cookie = 'zmEventStats';
var stats = getCookie(cookie);
if ( !stats ) {
stats = 'on';
setCookie(cookie, stats, 10*365);
}
return stats;
}
function initPage() {
// Load the delete confirmation modal into the DOM
getDelConfirmModal();
var stats = getEvtStatsCookie();
if ( stats != 'on' ) table.toggle(false);
//FIXME prevent blocking...not sure what is happening or best way to unblock
if ( $j('#videoobj').length ) {
vid = videojs('videoobj');
@ -1130,7 +1132,136 @@ function initPage() {
document.querySelectorAll('select[name="rate"]').forEach(function(el) {
el.onchange = window['changeRate'];
});
// enable or disable buttons based on current selection and user rights
renameBtn.prop('disabled', !canEditEvents);
archiveBtn.prop('disabled', !(!eventData.Archived && canEditEvents));
unarchiveBtn.prop('disabled', !(eventData.Archived && canEditEvents));
editBtn.prop('disabled', !canEditEvents);
exportBtn.prop('disabled', !canViewEvents);
downloadBtn.prop('disabled', !canViewEvents);
deleteBtn.prop('disabled', !canEditEvents);
// Don't enable the back button if there is no previous zm page to go back to
backBtn.prop('disabled', !document.referrer.length);
// Manage the BACK button
document.getElementById("backBtn").addEventListener("click", function onBackClick(evt) {
evt.preventDefault();
window.history.back();
});
// Manage the REFRESH Button
document.getElementById("refreshBtn").addEventListener("click", function onRefreshClick(evt) {
evt.preventDefault();
window.location.reload(true);
});
// Manage the Event RENAME button
document.getElementById("renameBtn").addEventListener("click", function onDownloadClick(evt) {
evt.preventDefault();
$j.getJSON(thisUrl + '?request=modal&modal=eventrename&eid='+eventData.Id)
.done(function(data) {
insertModalHtml('eventRenameModal', data.html);
$j('#eventRenameModal').modal('show');
// Manage the SAVE button
$j('#eventRenameBtn').click(renameEvent);
})
.fail(logAjaxFail);
});
// Manage the ARCHIVE button
document.getElementById("archiveBtn").addEventListener("click", function onArchiveClick(evt) {
evt.preventDefault();
$j.getJSON(thisUrl + '?request=events&task=archive&eids[]='+eventData.Id)
.done( function(data) {
//FIXME: update the status of the archive button reather than reload the whole page
window.location.reload(true);
})
.fail(logAjaxFail);
});
// Manage the UNARCHIVE button
document.getElementById("unarchiveBtn").addEventListener("click", function onUnarchiveClick(evt) {
if ( ! canEditEvents ) {
enoperm();
return;
}
evt.preventDefault();
$j.getJSON(thisUrl + '?request=events&task=unarchive&eids[]='+eventData.Id)
.done( function(data) {
//FIXME: update the status of the unarchive button reather than reload the whole page
window.location.reload(true);
})
.fail(logAjaxFail);
});
// Manage the EDIT button
document.getElementById("editBtn").addEventListener("click", function onEditClick(evt) {
if ( ! canEditEvents ) {
enoperm();
return;
}
evt.preventDefault();
$j.getJSON(thisUrl + '?request=modal&modal=eventdetail&eids[]='+eventData.Id)
.done(function(data) {
insertModalHtml('eventDetailModal', data.html);
$j('#eventDetailModal').modal('show');
// Manage the Save button
$j('#eventDetailSaveBtn').click(function(evt) {
evt.preventDefault();
$j('#eventDetailForm').submit();
});
})
.fail(logAjaxFail);
});
// Manage the EXPORT button
document.getElementById("exportBtn").addEventListener("click", function onExportClick(evt) {
evt.preventDefault();
window.location.assign('?view=export&eids[]='+eventData.Id);
});
// Manage the DOWNLOAD VIDEO button
document.getElementById("downloadBtn").addEventListener("click", function onDownloadClick(evt) {
evt.preventDefault();
$j.getJSON(thisUrl + '?request=modal&modal=download&eids[]='+eventData.Id)
.done(function(data) {
insertModalHtml('downloadModal', data.html);
$j('#downloadModal').modal('show');
// Manage the GENERATE DOWNLOAD button
$j('#exportButton').click(exportEvent);
})
.fail(logAjaxFail);
});
// Manage the Event STATISTICS Button
document.getElementById("statsBtn").addEventListener("click", function onStatsClick(evt) {
evt.preventDefault();
var cookie = 'zmEventStats';
// Toggle the visiblity of the stats table and write an appropriate cookie
if ( table.is(':visible') ) {
setCookie(cookie, 'off', 10*365);
table.toggle(false);
} else {
setCookie(cookie, 'on', 10*365);
table.toggle(true);
}
});
// Manage the DELETE button
document.getElementById("deleteBtn").addEventListener("click", function onDeleteClick(evt) {
if ( ! canEditEvents ) {
enoperm();
return;
}
evt.preventDefault();
$j('#deleteConfirm').modal('show');
});
}
// Kick everything off
window.addEventListener('DOMContentLoaded', initPage);
$j(document).ready(initPage);

View File

@ -40,6 +40,7 @@ var connKey = '<?php echo $connkey ?>';
var eventData = {
Id: '<?php echo $Event->Id() ?>',
Name: '<?php echo $Event->Name() ?>',
MonitorId: '<?php echo $Event->MonitorId() ?>',
Width: '<?php echo $Event->Width() ?>',
Height: '<?php echo $Event->Height() ?>',
@ -47,7 +48,10 @@ var eventData = {
StartDateTime: '<?php echo $Event->StartDateTime() ?>',
EndDateTime: '<?php echo $Event->EndDateTime() ?>',
Frames: '<?php echo $Event->Frames() ?>',
MonitorName: '<?php echo validJsStr($Monitor->Name()) ?>'
MonitorName: '<?php echo validJsStr($Monitor->Name()) ?>',
DiskSpace: '<?php echo human_filesize($Event->DiskSpace(null)) ?>',
Storage: '<?php validHtmlStr($Event->Storage()->Name()).( $Event->SecondaryStorageId() ? ', '.validHtmlStr($Event->SecondaryStorage()->Name()) : '' ) ?>',
Archived: <?php echo $Event->Archived?'true':'false' ?>
};
var monitorUrl = '<?php echo $Event->Storage()->Server()->UrlToIndex(); ?>';

View File

@ -31,11 +31,11 @@ function startDownload(file) {
function exportProgress() {
if ( exportTimer ) {
var tickerText = $('exportProgressTicker').get('text');
var tickerText = $j('#exportProgressTicker').text();
if ( tickerText.length < 1 || tickerText.length > 4 ) {
$('exportProgressTicker').set('text', '.');
$j('#exportProgressTicker').text('.');
} else {
$('exportProgressTicker').appendText('.');
$j('#exportProgressTicker').append('.');
}
}
}
@ -43,9 +43,9 @@ function exportProgress() {
function exportResponse(respObj, respText) {
clearInterval(exportTimer);
if ( respObj.result != 'Ok' ) {
$('exportProgressTicker').set('text', respObj.message);
$j('#exportProgressTicker').text(respObj.message);
} else {
$('exportProgressTicker').set('text', exportSucceededString);
$j('#exportProgressTicker').text(exportSucceededString);
startDownload.pass(decodeURIComponent(respObj.exportFile)).delay(1500);
}
return;

View File

@ -7,6 +7,7 @@ var settingsBtn = $j('#settingsBtn');
var enableAlmBtn = $j('#enableAlmBtn');
var forceAlmBtn = $j('#forceAlmBtn');
var table = $j('#eventList');
var filterQuery = '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId;
if ( monitorType != 'WebSite' ) {
var streamCmdParms = 'view=request&request=stream&connkey='+connKey;
@ -52,17 +53,14 @@ var params =
// Called by bootstrap-table to retrieve zm event data
function ajaxRequest(params) {
// Maintain legacy behavior of sorting by Id column only
delete params.data.order;
delete params.data.limit;
params.data.sort = 'Id desc';
params.data.count = maxDisplayEvents;
params.data.id = monitorId;
if ( auth_hash ) params.data.auth = auth_hash;
// Maintain legacy behavior by statically setting these parameters
params.data.order = 'desc';
params.data.limit = maxDisplayEvents;
params.data.sort = 'Id';
$j.getJSON(thisUrl + '?view=request&request=status&entity=events', params.data)
$j.getJSON(thisUrl + '?view=request&request=events&task=query'+filterQuery, params.data)
.done(function(data) {
var rows = processRows(data.events);
var rows = processRows(data.rows);
// rearrange the result into what bootstrap-table expects
params.success({total: data.total, totalNotFiltered: data.totalNotFiltered, rows: rows});
})
@ -72,19 +70,40 @@ function ajaxRequest(params) {
function processRows(rows) {
$j.each(rows, function(ndx, row) {
var eid = row.Id;
var filterQuery = '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId;
row.Delete = '<i class="fa fa-trash text-danger"></i>';
row.Id = '<a href="?view=event&amp;eid=' + eid + filterQuery + '">' + eid + '</a>';
row.Name = '<a href="?view=event&amp;eid=' + eid + filterQuery + '">' + row.Name + '</a>';
row.Frames = '<a href="?view=frames&amp;eid=' + eid + '">' + row.Frames + '</a>';
row.AlarmFrames = '<a href="?view=frames&amp;eid=' + eid + '">' + row.AlarmFrames + '</a>';
row.MaxScore = '<a href="?view=frame&amp;eid=' + eid + '&amp;fid=0">' + row.MaxScore + '</a>';
row.Delete = '<i class="fa fa-trash text-danger"></i>';
if ( LIST_THUMBS ) row.Thumbnail = '<a href="?view=event&amp;eid=' + eid + filterQuery + '&amp;page=1">' + row.imgHtml + '</a>';
});
return rows;
}
function thumbnail_onmouseover(event) {
var img = event.target;
img.src = '';
img.src = img.getAttribute('stream_src');
}
function thumbnail_onmouseout(event) {
var img = event.target;
img.src = '';
img.src = img.getAttribute('still_src');
}
function initThumbAnimation() {
if ( ANIMATE_THUMBS ) {
$j('.colThumbnail img').each(function() {
this.addEventListener('mouseover', thumbnail_onmouseover, false);
this.addEventListener('mouseout', thumbnail_onmouseout, false);
});
}
}
function showEvents() {
$('ptzControls').addClass('hidden');
$('events').removeClass('hidden');
@ -161,6 +180,7 @@ function setAlarmState( currentAlarmState ) {
var oldAlarm = ( !isAlarmed && wasAlarmed );
if ( newAlarm ) {
table.bootstrapTable('refresh');
if ( SOUND_ON_ALARM ) {
// Enable the alarm sound
if ( !canPlayPauseAudio ) {
@ -174,6 +194,7 @@ function setAlarmState( currentAlarmState ) {
}
}
if ( oldAlarm ) { // done with an event do a refresh
table.bootstrapTable('refresh');
if ( SOUND_ON_ALARM ) {
// Disable alarm sound
if ( !canPlayPauseAudio ) {
@ -182,7 +203,6 @@ function setAlarmState( currentAlarmState ) {
$('MediaPlayer').Stop();
}
}
table.bootstrapTable('refresh');
}
lastAlarmState = alarmState;
@ -874,6 +894,18 @@ function initPage() {
// Take appropriate action when the user clicks on a cell
table.on('click-cell.bs.table', processClicks);
// Some toolbar events break the thumbnail animation, so re-init eventlistener
table.on('all.bs.table', initThumbAnimation);
// Update table links each time after new data is loaded
table.on('post-body.bs.table', function(data) {
var thumb_ndx = $j('#eventList tr th').filter(function() {
return $j(this).text().trim() == 'Thumbnail';
}).index();
var thmbClass = ANIMATE_THUMBS ? 'colThumbnail zoom' : 'colThumbnail';
table.find("tr td:nth-child(" + (thumb_ndx+1) + ")").addClass(thmbClass);
});
} // initPage
// Kick everything off

View File

@ -50,6 +50,8 @@ var SCALE_BASE = <?php echo SCALE_BASE ?>;
var SOUND_ON_ALARM = <?php echo ZM_WEB_SOUND_ON_ALARM ?>;
var POPUP_ON_ALARM = <?php echo ZM_WEB_POPUP_ON_ALARM ?>;
var LIST_THUMBS = <?php echo ZM_WEB_LIST_THUMBS?'true':'false' ?>;
var ANIMATE_THUMBS = <?php echo ZM_WEB_ANIMATE_THUMBS?'true':'false' ?>;
var streamMode = "<?php echo $streamMode ?>";
var showMode = "<?php echo ($showPtzControls && !empty($control))?"control":"events" ?>";

View File

@ -27,7 +27,7 @@ xhtmlHeaders(__FILE__, translate('SystemLog'));
?>
<body>
<?php echo getNavBarHTML() ?>
<div id="page" class="px-3">
<div id="page" class="px-3 table-responsive-sm">
<div id="logSummary" class="text-center">
<?php echo translate('State') ?>:&nbsp;<span id="logState"></span>&nbsp;-&nbsp;
@ -62,7 +62,6 @@ xhtmlHeaders(__FILE__, translate('SystemLog'));
data-toolbar="#toolbar"
data-show-fullscreen="true"
data-maintain-meta-data="true"
data-mobile-responsive="true"
data-buttons-class="btn btn-normal"
data-show-jump-to="true"
data-auto-refresh="true"

View File

@ -369,8 +369,10 @@ $fastblendopts_alarm = array(
);
$label_size = array(
1 => translate('Default'),
2 => translate('Large'),
1 => translate('Small'),
2 => translate('Default'),
3 => translate('Large'),
4 => translate('Extra Large'),
);
$codecs = array(

View File

@ -44,7 +44,9 @@ xhtmlHeaders(__FILE__, translate('Stats')." - ".$eid." - ".$fid );
<div id="content" class="row justify-content-center">
<form name="contentForm" id="contentForm" method="get" action="?">
<input type="hidden" name="view" value="none"/>
<?php echo getStatsTableHTML($eid, $fid) ?>
<div class="table-responsive-sm">
<?php echo getStatsTableHTML($eid, $fid) ?>
</div>
</form>
</div>
</div>

View File

@ -160,7 +160,7 @@ if ( $showPtzControls ) {
if ( canView('Events') && ($monitor->Type() != 'WebSite') ) {
?>
<!-- Table styling handled by bootstrap-tables -->
<div id="events" class="row justify-content-center">
<div id="events" class="row justify-content-center table-responsive-sm">
<table
id="eventList"
data-locale="<?php echo i18n() ?>"
@ -172,17 +172,14 @@ if ( canView('Events') && ($monitor->Type() != 'WebSite') ) {
data-show-columns="true"
data-show-export="true"
data-uncheckAll="true"
data-mobile-responsive="true"
data-buttons-class="btn btn-normal"
data-auto-refresh="true"
data-auto-refresh-silent="true"
data-show-refresh="true"
data-auto-refresh-interval="5"
class="table-sm table-borderless"
>
<thead>
<!-- Row styling is handled by bootstrap-tables -->
<tr>
<th data-sortable="false" data-field="Delete"><?php echo translate('Delete') ?></th>
<th data-sortable="false" data-field="Id"><?php echo translate('Id') ?></th>
<th data-sortable="false" data-field="Name"><?php echo translate('Name') ?></th>
<th data-sortable="false" data-field="StartDateTime"><?php echo translate('AttrStartTime') ?></th>
@ -191,7 +188,7 @@ if ( canView('Events') && ($monitor->Type() != 'WebSite') ) {
<th data-sortable="false" data-field="AlarmFrames"><?php echo translate('AlarmBrFrames') ?></th>
<th data-sortable="false" data-field="AvgScore"><?php echo translate('AvgBrScore') ?></th>
<th data-sortable="false" data-field="MaxScore"><?php echo translate('MaxBrScore') ?></th>
<th data-sortable="false" data-field="Delete"><?php echo translate('Delete') ?></th>
<th data-sortable="false" data-field="Thumbnail"><?php echo translate('Thumbnail') ?></th>
</tr>
</thead>