1<?php 2// 3// ZoneMinder web timeline view file, $Date$, $Revision$ 4// 5// This program is free software; you can redistribute it and/or 6// modify it under the terms of the GNU General Public License 7// as published by the Free Software Foundation; either version 2 8// of the License, or (at your option) any later version. 9// 10// This program is distributed in the hope that it will be useful, 11// but WITHOUT ANY WARRANTY; without even the implied warranty of 12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13// GNU General Public License for more details. 14// 15// You should have received a copy of the GNU General Public License 16// along with this program; if not, write to the Free Software 17// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18// 19 20if ( !canView('Events') ) { 21 $view = 'error'; 22 return; 23} 24 25foreach ( getSkinIncludes('includes/timeline_functions.php') as $includeFile ) 26 require_once $includeFile; 27 28// 29// Date/time formats used in charts 30// 31// These are the time axis range text. The first of each pair is the start date/time 32// and the second is the last so often contains additional information 33// 34 35// When the chart range is years 36define( 'STRF_TL_AXIS_RANGE_YEAR1', '%b %Y' ); 37define( 'STRF_TL_AXIS_RANGE_YEAR2', STRF_TL_AXIS_RANGE_YEAR1 ); 38 39// When the chart range is months 40define( 'STRF_TL_AXIS_RANGE_MONTH1', '%b' ); 41define( 'STRF_TL_AXIS_RANGE_MONTH2', STRF_TL_AXIS_RANGE_MONTH1.' %Y' ); 42 43// When the chart range is days 44define( 'STRF_TL_AXIS_RANGE_DAY1', '%d' ); 45define( 'STRF_TL_AXIS_RANGE_DAY2', STRF_TL_AXIS_RANGE_DAY1.' %b %Y' ); 46 47// When the chart range is less than a day 48define( 'STRF_TL_AXIS_RANGE_TIME1', '%H:%M' ); 49define( 'STRF_TL_AXIS_RANGE_TIME2', STRF_TL_AXIS_RANGE_TIME1.', %d %b %Y' ); 50 51// 52// These are the time axis tick labels 53// 54define( 'STRF_TL_AXIS_LABEL_YEAR', '%Y' ); 55define( 'STRF_TL_AXIS_LABEL_MONTH', '%M' ); 56define( 'STRF_TL_AXIS_LABEL_WEEK', '%d/%m' ); 57define( 'STRF_TL_AXIS_LABEL_DAY', '%d' ); 58define( 'STRF_TL_AXIS_LABEL_4HOUR', '%H:00' ); 59define( 'STRF_TL_AXIS_LABEL_HOUR', '%H:00' ); 60define( 'STRF_TL_AXIS_LABEL_10MINUTE', '%H:%M' ); 61define( 'STRF_TL_AXIS_LABEL_MINUTE', '%H:%M' ); 62define( 'STRF_TL_AXIS_LABEL_10SECOND', '%S' ); 63define( 'STRF_TL_AXIS_LABEL_SECOND', '%S' ); 64 65$mouseover = isset($_REQUEST['mouseover']) ? $_REQUEST['mouseover'] : true; 66 67$mode = isset($_REQUEST['mode']) ? $_REQUEST['mode'] : 'overlay'; 68 69$minEventWidth = 3; 70$maxEventWidth = 6; 71 72$chart = array( 73 'width'=>700, 74 'height'=>460, 75 'image' => array( 76 'width'=>264, 77 'height'=>220, 78 'topOffset'=>20, 79 ), 80 'imageText' => array( 81 'width'=>400, 82 'height'=>30, 83 'topOffset'=>20, 84 ), 85 'graph' => array( 86 'width'=>600, 87 'height'=>160, 88 'topOffset'=>30, 89 ), 90 'title' => array( 91 'topOffset'=>50 92 ), 93 'key' => array( 94 'topOffset'=>50 95 ), 96 'axes' => array( 97 'x' => array( 98 'height' => 20, 99 ), 100 'y' => array( 101 'width' => 30, 102 ), 103 ), 104 'grid' => array( 105 'x' => array( 106 'major' => array( 107 'max' => 12, 108 'min' => 4, 109 ), 110 'minor' => array( 111 'max' => 48, 112 'min' => 12, 113 ), 114 ), 115 'y' => array( 116 'major' => array( 117 'max' => 8, 118 'min' => 1, 119 ), 120 'minor' => array( 121 'max' => 0, 122 'min' => 0, 123 ), 124 ), 125 ), 126); 127 128$monitors = array(); 129 130# The as E, and joining with Monitors is required for the filterSQL filters. 131$rangeSql = 'SELECT min(E.StartDateTime) AS MinTime, max(E.EndDateTime) AS MaxTime FROM Events AS E INNER JOIN Monitors AS M ON (E.MonitorId = M.Id) WHERE NOT isnull(E.StartDateTime) AND NOT isnull(E.EndDateTime)'; 132$eventsSql = 'SELECT E.* FROM Events AS E INNER JOIN Monitors AS M ON (E.MonitorId = M.Id) WHERE NOT isnull(StartDateTime)'; 133$eventIdsSql = 'SELECT E.Id FROM Events AS E INNER JOIN Monitors AS M ON (E.MonitorId = M.Id) WHERE NOT isnull(StartDateTime)'; 134$eventsValues = array(); 135 136if ( !empty($user['MonitorIds']) ) { 137 $monFilterSql = ' AND MonitorId IN ('.$user['MonitorIds'].')'; 138 139 $rangeSql .= $monFilterSql; 140 $eventsSql .= $monFilterSql; 141 $eventIdsSql .= $monFilterSql; 142} 143 144$tree = false; 145if ( isset($_REQUEST['filter']) ) { 146 $filter = ZM\Filter::parse($_REQUEST['filter']); 147 $tree = $filter->tree(); 148} 149 150if ( isset($_REQUEST['range']) ) 151 $range = validHtmlStr($_REQUEST['range']); 152if ( isset($_REQUEST['minTime']) ) 153 $minTime = validHtmlStr($_REQUEST['minTime']); 154if ( isset($_REQUEST['midTime']) ) 155 $midTime = validHtmlStr($_REQUEST['midTime']); 156if ( isset($_REQUEST['maxTime']) ) 157 $maxTime = validHtmlStr($_REQUEST['maxTime']); 158 159if ( isset($range) and validInt($range) ) { 160 $halfRange = (int)($range/2); 161 if ( isset($midTime) ) { 162 $midTimeT = strtotime($midTime); 163 $minTimeT = $midTimeT-$halfRange; 164 $maxTimeT = $midTimeT+$halfRange; 165 if ( !($range%1) ) { 166 $maxTimeT--; 167 } 168 $minTime = strftime(STRF_FMT_DATETIME_DB, $minTimeT); 169 $maxTime = strftime(STRF_FMT_DATETIME_DB, $maxTimeT); 170 } elseif ( isset($minTime) ) { 171 $minTimeT = strtotime($minTime); 172 $maxTimeT = $minTimeT + $range; 173 $midTimeT = $minTimeT + $halfRange; 174 $midTime = strftime(STRF_FMT_DATETIME_DB, $midTimeT); 175 $maxTime = strftime(STRF_FMT_DATETIME_DB, $maxTimeT); 176 } elseif ( isset($maxTime) ) { 177 $maxTimeT = strtotime($maxTime); 178 $minTimeT = $maxTimeT - $range; 179 $midTimeT = $minTimeT + $halfRange; 180 $minTime = strftime(STRF_FMT_DATETIME_DB, $minTimeT); 181 $midTime = strftime(STRF_FMT_DATETIME_DB, $midTimeT); 182 } 183} elseif ( isset($minTime) && isset($maxTime) ) { 184 $minTimeT = strtotime($minTime); 185 $maxTimeT = strtotime($maxTime); 186 $range = ($maxTimeT - $minTimeT) + 1; 187 $halfRange = (int)($range/2); 188 $midTimeT = $minTimeT + $halfRange; 189 $midTime = strftime(STRF_FMT_DATETIME_DB, $midTimeT); 190} 191 192if ( isset($minTime) && isset($maxTime) ) { 193 $tempMinTime = $tempMaxTime = $tempExpandable = false; 194 extractDatetimeRange($tree, $tempMinTime, $tempMaxTime, $tempExpandable); 195 $filterSql = parseTreeToSQL($tree); 196 197 if ( $filterSql ) { 198 $eventsSql .= ' AND '.$filterSql; 199 $eventIdsSql .= ' AND '.$filterSql; 200 } 201} else { 202 $filterSql = parseTreeToSQL($tree); 203 $tempMinTime = $tempMaxTime = $tempExpandable = false; 204 extractDatetimeRange($tree, $tempMinTime, $tempMaxTime, $tempExpandable); 205 206 if ( $filterSql ) { 207 $rangeSql .= ' AND '.$filterSql; 208 $eventsSql .= ' AND '.$filterSql; 209 $eventIdsSql .= ' AND '.$filterSql; 210 } 211 212 if ( !isset($minTime) || !isset($maxTime) ) { 213 // Dynamically determine range 214 $row = dbFetchOne($rangeSql); 215 if ( $row ) { 216 if ( !isset($minTime) ) 217 $minTime = $row['MinTime']; 218 if ( !isset($maxTime) ) 219 $maxTime = $row['MaxTime']; 220 } 221 } 222 223 if ( empty($minTime) ) 224 $minTime = $tempMinTime; 225 if ( empty($maxTime) ) 226 $maxTime = $tempMaxTime; 227 if ( empty($maxTime) ) 228 $maxTime = 'now'; 229 230 $minTimeT = strtotime($minTime); 231 $maxTimeT = strtotime($maxTime); 232 $range = ($maxTimeT - $minTimeT) + 1; 233 $halfRange = (int)($range/2); 234 $midTimeT = $minTimeT + $halfRange; 235 $midTime = strftime(STRF_FMT_DATETIME_DB, $midTimeT); 236} 237 238if ( $tree ) { 239 appendDatetimeRange($tree, $minTime, $maxTime); 240 241 $filterQuery = parseTreeToQuery($tree); 242} else { 243 $filterQuery = false; 244} 245 246$scales = array( 247 array( 'name'=>'year', 'factor'=>60*60*24*365, 'align'=>1, 'zoomout'=>2, 'label'=>STRF_TL_AXIS_LABEL_YEAR ), 248 array( 'name'=>'month', 'factor'=>60*60*24*30, 'align'=>1, 'zoomout'=>12, 'label'=>STRF_TL_AXIS_LABEL_MONTH ), 249 array( 'name'=>'week', 'factor'=>60*60*24*7, 'align'=>1, 'zoomout'=>4.25, 'label'=>STRF_TL_AXIS_LABEL_WEEK, 'labelCheck'=>'%W' ), 250 array( 'name'=>'day', 'factor'=>60*60*24, 'align'=>1, 'zoomout'=>7, 'label'=>STRF_TL_AXIS_LABEL_DAY ), 251 array( 'name'=>'hour4', 'factor'=>60*60, 'align'=>4, 'zoomout'=>6, 'label'=>STRF_TL_AXIS_LABEL_4HOUR, 'labelCheck'=>'%H' ), 252 array( 'name'=>'hour', 'factor'=>60*60, 'align'=>1, 'zoomout'=>4, 'label'=>STRF_TL_AXIS_LABEL_HOUR, 'labelCheck'=>'%H' ), 253 array( 'name'=>'minute10', 'factor'=>60, 'align'=>10, 'zoomout'=>6, 'label'=>STRF_TL_AXIS_LABEL_10MINUTE, 'labelCheck'=>'%M' ), 254 array( 'name'=>'minute', 'factor'=>60, 'align'=>1, 'zoomout'=>10, 'label'=>STRF_TL_AXIS_LABEL_MINUTE, 'labelCheck'=>'%M' ), 255 array( 'name'=>'second10', 'factor'=>1, 'align'=>10, 'zoomout'=>6, 'label'=>STRF_TL_AXIS_LABEL_10SECOND ), 256 array( 'name'=>'second', 'factor'=>1, 'align'=>1, 'zoomout'=>10, 'label'=>STRF_TL_AXIS_LABEL_SECOND ), 257); 258 259$majXScale = getDateScale($scales, $range, $chart['grid']['x']['major']['min'], $chart['grid']['x']['major']['max']); 260 261// Adjust the range etc for scale 262$minTimeT -= $minTimeT%($majXScale['factor']*$majXScale['align']); 263$minTime = strftime(STRF_FMT_DATETIME_DB, $minTimeT); 264$maxTimeT += (($majXScale['factor']*$majXScale['align'])-$maxTimeT%($majXScale['factor']*$majXScale['align']))-1; 265if ( $maxTimeT > time() ) 266 $maxTimeT = time(); 267$maxTime = strftime(STRF_FMT_DATETIME_DB, $maxTimeT); 268$range = ($maxTimeT - $minTimeT) + 1; 269$halfRange = (int)($range/2); 270$midTimeT = $minTimeT + $halfRange; 271$midTime = strftime(STRF_FMT_DATETIME_DB, $midTimeT); 272 273if ( isset($minTime) && isset($maxTime) ) { 274 $eventsSql .= " AND EndDateTime >= '$minTime' AND StartDateTime <= '$maxTime'"; 275 $eventIdsSql .= " AND EndDateTime >= '$minTime' AND StartDateTime <= '$maxTime'"; 276} 277 278if ( 0 ) { 279$framesByEventId = array(); 280$eventsSql .= ' ORDER BY E.Id ASC'; 281$framesSql = "SELECT EventId,FrameId,Delta,Score FROM Frames WHERE EventId IN($eventIdsSql) AND Score > 0 ORDER BY Score DESC"; 282$frames_result = dbQuery($framesSql); 283while ( $row = $frames_result->fetch(PDO::FETCH_ASSOC) ) { 284 if ( !isset($framesByEventId[$row['EventId']]) ) { 285 $framesByEventId[$row['EventId']] = array(); 286 } 287 $framesByEventId[$row['EventId']][] = $row; 288} 289} 290 291 292$chart['data'] = array( 293 'x' => array( 294 'lo' => strtotime($minTime), 295 'hi' => strtotime($maxTime), 296 ), 297 'y' => array( 298 'lo' => 0, 299 'hi' => 0, 300 ) 301); 302 303$chart['data']['x']['range'] = ($chart['data']['x']['hi'] - $chart['data']['x']['lo']) + 1; 304$chart['data']['x']['density'] = $chart['data']['x']['range']/$chart['graph']['width']; 305 306$monEventSlots = array(); 307$monFrameSlots = array(); 308$events_result = dbQuery($eventsSql); 309if ( !$events_result ) { 310 ZM\Fatal('SQL-ERR'); 311 return; 312} 313 314$max_aspect_ratio = 0; 315 316while( $event = $events_result->fetch(PDO::FETCH_ASSOC) ) { 317 if ( !isset($monitors[$event['MonitorId']]) ) { 318 $monitor = $monitors[$event['MonitorId']] = ZM\Monitor::find_one(array('Id'=>$event['MonitorId'])); 319 $monEventSlots[$event['MonitorId']] = array(); 320 $monFrameSlots[$event['MonitorId']] = array(); 321 $aspect_ratio = round($monitor->Width() / $monitor->Height(), 2); 322 if ( $aspect_ratio > $max_aspect_ratio ) 323 $max_aspect_ratio = $aspect_ratio; 324 } 325 326 $currEventSlots = &$monEventSlots[$event['MonitorId']]; 327 $currFrameSlots = &$monFrameSlots[$event['MonitorId']]; 328 329 $startTimeT = strtotime($event['StartDateTime']); 330 $startIndex = $rawStartIndex = (int)(($startTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']); 331 if ( $startIndex < 0 ) 332 $startIndex = 0; 333 334 if ( isset($event['EndDateTime']) ) 335 $endTimeT = strtotime($event['EndDateTime']); 336 else 337 $endTimeT = time(); 338 $endIndex = $rawEndIndex = (int)(($endTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']); 339 340 if ( $endIndex >= $chart['graph']['width'] ) 341 $endIndex = $chart['graph']['width'] - 1; 342 343 for ( $i = $startIndex; $i <= $endIndex; $i++ ) { 344 if ( !isset($currEventSlots[$i]) ) { 345 if ( $rawStartIndex == $rawEndIndex ) { 346 $offset = 1; 347 } else { 348 $offset = 1 + ($event['Frames']?((int)(($event['Frames']-1)*(($i-$rawStartIndex)/($rawEndIndex-$rawStartIndex)))):0); 349 } 350 $currEventSlots[$i] = array( 'count'=>0, 'width'=>1, 'offset'=>$offset, 'event'=>$event ); 351 } else { 352 $currEventSlots[$i]['count']++; 353 } 354 } 355 356 if ( $event['MaxScore'] > 0 ) { 357 if ( $startIndex == $endIndex ) { 358 # Only fills 1 slot, so just get the max Score 359 $framesSql = 'SELECT FrameId, Score FROM Frames WHERE EventId = ? AND Score > 0 ORDER BY Score DESC LIMIT 1'; 360 $frame = dbFetchOne($framesSql, NULL, array($event['Id'])); 361 362 $i = $startIndex; 363 if ( !isset($currFrameSlots[$i]) ) { 364 $currFrameSlots[$i] = array('count'=>1, 'value'=>$event['MaxScore'], 'event'=>$event, 'frame'=>$frame); 365 } else { 366 $currFrameSlots[$i]['count']++; 367 if ( $event['MaxScore'] > $currFrameSlots[$i]['value'] ) { 368 $currFrameSlots[$i]['value'] = $event['MaxScore']; 369 $currFrameSlots[$i]['event'] = $event; 370 $currFrameSlots[$i]['frame'] = $frame; 371 } 372 } 373 if ( $event['MaxScore'] > $chart['data']['y']['hi'] ) { 374 $chart['data']['y']['hi'] = $event['MaxScore']; 375 } 376 } else { 377 # Fills multiple Slots, so need multiple scores to generate the graph over multiple slots. 378 $framesSql = 'SELECT FrameId,Delta,Score FROM Frames WHERE EventId = ? AND Score > 0'; 379 $result = dbQuery($framesSql, array($event['Id'])); 380 while ( $frame = dbFetchNext($result) ) { 381 #foreach ( $framesByEventId[$event['Id']] as $frame ) { 382 $frameTimeT = $startTimeT + $frame['Delta']; 383 $frameIndex = (int)(($frameTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']); 384 if ( $frameIndex < 0 ) 385 continue; 386 if ( $frameIndex >= $chart['graph']['width'] ) 387 continue; 388 389 if ( !isset($currFrameSlots[$frameIndex]) ) { 390 $currFrameSlots[$frameIndex] = array('count'=>1, 'value'=>$frame['Score'], 'event'=>$event, 'frame'=>$frame); 391 } else { 392 $currFrameSlots[$frameIndex]['count']++; 393 if ( $frame['Score'] > $currFrameSlots[$frameIndex]['value'] ) { 394 $currFrameSlots[$frameIndex]['value'] = $frame['Score']; 395 $currFrameSlots[$frameIndex]['event'] = $event; 396 $currFrameSlots[$frameIndex]['frame'] = $frame; 397 } 398 } 399 if ( $frame['Score'] > $chart['data']['y']['hi'] ) { 400 $chart['data']['y']['hi'] = $frame['Score']; 401 } 402 } // end foreach frame 403 } 404 } // end if MaxScore > 0 405} // end foreach event 406 407//ksort( $monitorIds, SORT_NUMERIC ); 408ksort( $monEventSlots, SORT_NUMERIC ); 409ksort( $monFrameSlots, SORT_NUMERIC ); 410 411// No longer needed? 412if ( false ) { 413 // Add on missing frames 414 foreach( array_keys($monFrameSlots) as $monitorId ) { 415 unset( $currFrameSlots ); 416 $currFrameSlots = &$monFrameSlots[$monitorId]; 417 for ( $i = 0; $i < $chart['graph']['width']; $i++ ) { 418 if ( isset($currFrameSlots[$i]) ) { 419 if ( !isset($currFrameSlots[$i]['frame']) ) { 420 $framesSql = 'SELECT FrameId, Score FROM Frames WHERE EventId = ? AND Score > 0 ORDER BY FrameId LIMIT 1'; 421 $currFrameSlots[$i]['frame'] = dbFetchOne( $framesSql, NULL, array( $currFrameSlots[$i]['event']['Id'] ) ); 422 } 423 } 424 } 425 } 426} 427 428$chart['data']['y']['range'] = ($chart['data']['y']['hi'] - $chart['data']['y']['lo']) + 1; 429$chart['data']['y']['density'] = $chart['data']['y']['range']/$chart['graph']['height']; 430 431$majYScale = getYScale( 432 $chart['data']['y']['range'], 433 $chart['grid']['y']['major']['min'], 434 $chart['grid']['y']['major']['max']); 435 436// Optimise boxes 437foreach( array_keys($monEventSlots) as $monitorId ) { 438 unset( $currEventSlots ); 439 $currEventSlots = &$monEventSlots[$monitorId]; 440 for ( $i = 0; $i < $chart['graph']['width']; $i++ ) { 441 if ( isset($currEventSlots[$i]) ) { 442 if ( isset($currSlot) ) { 443 if ( $currSlot['event']['Id'] == $currEventSlots[$i]['event']['Id'] ) { 444 if ( $currSlot['width'] < $maxEventWidth ) { 445 // Merge slots for the same long event 446 $currSlot['width']++; 447 unset( $currEventSlots[$i] ); 448 continue; 449 } else if ( $currSlot['offset'] < $currEventSlots[$i]['offset'] ) { 450 // Split very long events 451 $currEventSlots[$i]['frame'] = array( 'FrameId'=>$currEventSlots[$i]['offset'] ); 452 } 453 } else if ( $currSlot['width'] < $minEventWidth ) { 454 // Merge multiple small events 455 $currSlot['width']++; 456 unset( $currEventSlots[$i] ); 457 continue; 458 } 459 } 460 $currSlot = &$currEventSlots[$i]; 461 } else { 462 unset($currSlot); 463 } 464 } # end foreach x 465 unset($currSlot); 466} // end foreach Event Monitors 467//print_r( $monEventSlots ); 468 469// Stack events 470$frameSlots = array(); 471$frameMonitorIds = array_keys($monFrameSlots); 472for ( $i = 0; $i < $chart['graph']['width']; $i++ ) { 473 foreach ( $frameMonitorIds as $frameMonitorId ) { 474 $currFrameSlots = &$monFrameSlots[$frameMonitorId]; 475 if ( isset($currFrameSlots[$i]) ) { 476 if ( !isset($frameSlots[$i]) ) { 477 $frameSlots[$i] = array(); 478 $frameSlots[$i][] = &$currFrameSlots[$i]; 479 } else { 480 $slotCount = count($frameSlots[$i]); 481 for ( $j = 0; $j < $slotCount; $j++ ) { 482 if ( $currFrameSlots[$i]['value'] > $frameSlots[$i][$j]['value'] ) { 483 for ( $k = $slotCount; $k > $j; $k-- ) { 484 $frameSlots[$i][$k] = $frameSlots[$i][$k-1]; 485 } 486 $frameSlots[$i][$j] = &$currFrameSlots[$i]; 487 break 2; 488 } 489 } 490 $frameSlots[$i][] = &$currFrameSlots[$i]; 491 } 492 } 493 unset($currFrameSlots); 494 } # end foreach MonitorId 495} # end foreach x 496 497//ZM\Debug(print_r( $monEventSlots,true )); 498//print_r( $monFrameSlots ); 499//print_r( $chart ); 500 501$graphHeight = $chart['graph']['height']; 502 503if ( $mode == 'overlay' ) { 504 $minEventBarHeight = 10; 505 $maxEventBarHeight = 40; 506 507 if ( count($monitors) ) { 508 $chart['graph']['eventBarHeight'] = $minEventBarHeight; 509 while ( ($chart['graph']['eventsHeight'] = (($chart['graph']['eventBarHeight'] * count($monitors)) + (count($monitors)-1))) < $maxEventBarHeight ) { 510 $chart['graph']['eventBarHeight']++; 511 } 512 } else { 513 $chart['graph']['eventBarHeight'] = $maxEventBarHeight; 514 $chart['graph']['eventsHeight'] = $maxEventBarHeight; 515 } 516 $chart['graph']['activityHeight'] = ($graphHeight - $chart['graph']['eventsHeight']); 517 $chart['data']['y']['density'] = $chart['data']['y']['range']/$chart['graph']['activityHeight']; 518 519 $chart['eventBars'] = array(); 520 $top = $chart['graph']['activityHeight']; 521 foreach ( array_keys($monitors) as $monitorId ) { 522 $chart['eventBars'][$monitorId] = array( 'top' => $top ); 523 $top += $chart['graph']['eventBarHeight']+1; 524 } 525} else if ( $mode == 'split' ) { 526 $minActivityBarHeight = 30; 527 $minEventBarHeight = 10; 528 $maxEventBarHeight = 40; 529 530 if ( count($monitors) ) { 531 $chart['graph']['eventBarHeight'] = $minEventBarHeight; 532 $chart['graph']['activityBarHeight'] = $minActivityBarHeight; 533 while ( ((($chart['graph']['eventBarHeight']+$chart['graph']['activityBarHeight']) * count($monitors)) + ((2*count($monitors))-1)) < $graphHeight ) { 534 $chart['graph']['activityBarHeight']++; 535 if ( $chart['graph']['eventBarHeight'] < $maxEventBarHeight ) { 536 $chart['graph']['eventBarHeight']++; 537 } 538 } 539 } else { 540 $chart['graph']['eventBarHeight'] = $maxEventBarHeight; 541 $chart['graph']['activityBarHeight'] = $graphHeight - $chart['graph']['eventBarHeight']; 542 } 543 $chart['data']['y']['density'] = $chart['data']['y']['range']/$chart['graph']['activityBarHeight']; 544 545 $chart['activityBars'] = array(); 546 $chart['eventBars'] = array(); 547 $top = 0; 548 $barCount = 1; 549 foreach ( array_keys($monitors) as $monitorId ) { 550 $chart['eventBars'][$monitorId] = array( 'top' => $top ); 551 $chart['eventBars'][$monitorId] = array( 'top' => $top+$chart['graph']['activityBarHeight']+1 ); 552 $top += $chart['graph']['activityBarHeight']+1+$chart['graph']['eventBarHeight']+1; 553 } 554} else { 555 ZM\Warning("No mode $mode"); 556} 557 558preg_match('/^(\d+)-(\d+)-(\d+) (\d+):(\d+)/', $minTime, $startMatches); 559preg_match('/^(\d+)-(\d+)-(\d+) (\d+):(\d+)/', $maxTime, $endMatches); 560 561if ( $startMatches[1] != $endMatches[1] ) { 562 // Different years 563 $title = strftime( STRF_TL_AXIS_RANGE_YEAR1, $chart['data']['x']['lo'] ).' - '.strftime( STRF_TL_AXIS_RANGE_YEAR2, $chart['data']['x']['hi'] ); 564} else if ( $startMatches[2] != $endMatches[2] ) { 565 // Different months 566 $title = strftime( STRF_TL_AXIS_RANGE_MONTH1, $chart['data']['x']['lo'] ).' - '.strftime( STRF_TL_AXIS_RANGE_MONTH2, $chart['data']['x']['hi'] ); 567} else if ( $startMatches[3] != $endMatches[3] ) { 568 // Different dates 569 $title = strftime( STRF_TL_AXIS_RANGE_DAY1, $chart['data']['x']['lo'] ).' - '.strftime( STRF_TL_AXIS_RANGE_DAY2, $chart['data']['x']['hi'] ); 570} else { 571 // Different times 572 $title = strftime( STRF_TL_AXIS_RANGE_TIME1, $chart['data']['x']['lo'] ).' - '.strftime( STRF_TL_AXIS_RANGE_TIME2, $chart['data']['x']['hi'] ); 573} 574 575function drawXGrid( $chart, $scale, $labelClass, $tickClass, $gridClass, $zoomClass=false ) { 576 $html = ''; 577 ob_start(); 578 $labelCount = 0; 579 $lastTick = 0; 580 unset( $lastLabel ); 581 $labelCheck = isset($scale['labelCheck'])?$scale['labelCheck']:$scale['label']; 582 echo '<div id="xScale">'; 583 for ( $i = 0; $i < $chart['graph']['width']; $i++ ) { 584 $x = round(100*(($i)/$chart['graph']['width']),1); 585 $timeOffset = (int)($chart['data']['x']['lo'] + ($i * $chart['data']['x']['density'])); 586 if ( $scale['align'] > 1 ) { 587 $label = (int)(strftime( $labelCheck, $timeOffset )/$scale['align']); 588 } else { 589 $label = strftime( $labelCheck, $timeOffset ); 590 } 591 if ( !isset($lastLabel) || ($lastLabel != $label) ) { 592 $labelCount++; 593 } 594 if ( $labelCount >= $scale['divisor'] ) { 595 $labelCount = 0; 596 if ( isset($lastLabel) ) { 597 if ( $labelClass ) { 598?> 599 <div class="<?php echo $labelClass ?>" style="left: <?php echo $x-round(100*(11/$chart['graph']['width']),1) ?>%;"><?php echo strftime( $scale['label'], $timeOffset ); ?></div> 600<?php 601 } 602 if ( $tickClass ) { 603?> 604 <div class="<?php echo $tickClass ?>" style="left: <?php echo $x ?>%;"></div> 605<?php 606 } 607 if ( $gridClass ) { 608?> 609 <div class="<?php echo $gridClass ?>" style="left: <?php echo $x ?>%;"></div> 610<?php 611 } 612 if ( $scale['name'] != 'second' && $zoomClass ) { 613 $zoomMinTime = strftime( STRF_FMT_DATETIME_DB, (int)($chart['data']['x']['lo'] + ($lastTick * $chart['data']['x']['density'])) ); 614 $zoomMaxTime = strftime( STRF_FMT_DATETIME_DB, (int)($chart['data']['x']['lo'] + ($i * $chart['data']['x']['density'])) ); 615?> 616 <div class="<?php echo $zoomClass ?>" style="left: <?php echo 100*($lastTick-1)/$chart['graph']['width'] ?>%; width: <?php echo round(100*($i-$lastTick)/$chart['graph']['width'],1) ?>%;" title="<?php echo translate('ZoomIn') ?>" data-on-click="tlZoomBounds" data-zoom-min-time="<?php echo $zoomMinTime ?>" data-zoom-max-time="<?php echo $zoomMaxTime ?>"></div> 617<?php 618 } 619 $lastTick = $i; 620 } # end if $lastLabel 621 } 622 $lastLabel = $label; 623 } # end foreach width segment 624 625 if ( $zoomClass ) { 626 $zoomMinTime = strftime( STRF_FMT_DATETIME_DB, (int)($chart['data']['x']['lo'] + ($lastTick * $chart['data']['x']['density'])) ); 627 $zoomMaxTime = strftime( STRF_FMT_DATETIME_DB, (int)($chart['data']['x']['lo'] + ($i * $chart['data']['x']['density'])) ); 628?> 629 <div class="<?php echo $zoomClass ?>" style="left: <?php echo $lastTick-1 ?>px; width: <?php echo $i-$lastTick ?>px;" title="<?php echo translate('ZoomIn') ?>" data-on-click="tlZoomBounds" data-zoom-min-time="<?php echo $zoomMinTime ?>" data-zoom-max-time="<?php echo $zoomMaxTime ?>"></div> 630<?php 631 } 632?> 633 </div> 634<?php 635 return ob_get_clean(); 636} # end function drawXGrid 637 638function drawYGrid( $chart, $scale, $labelClass, $tickClass, $gridClass ) { 639 ob_start(); 640?> 641 <div id="yScale"> 642<?php 643 for ( $i = 0; $i < $scale['lines']; $i++ ) { 644 $label = (int)($i * $scale['divisor']); 645 $y = $chart['graph']['eventsHeight']+(int)(($i * $scale['divisor'])/$chart['data']['y']['density'])-1; 646 if ( $labelClass ) { 647?> 648 <div class="<?php echo $labelClass ?>" style="top: <?php echo $chart['graph']['height']-($y+8) ?>px;"><?php echo $label ?></div> 649<?php 650 } 651 if ( $tickClass ) { 652?> 653 <div class="<?php echo $tickClass ?>" style="top: <?php echo $chart['graph']['height']-($y+2) ?>px;"></div> 654<?php 655 } 656 if ( $gridClass ) { 657?> 658 <div class="<?php echo $gridClass ?>" style="top: <?php echo $chart['graph']['height']-($y+2) ?>px;<?php echo $i <= 0?' border-top: solid 1px black;':'' ?>"></div> 659<?php 660 } 661 } # end foreach line segment 662?> 663 </div> 664<?php 665 return ob_get_clean(); 666} # end function drawYGrid 667 668$focusWindow = true; 669 670xhtmlHeaders(__FILE__, translate('Timeline')); 671?> 672<body> 673 <?php echo getNavBarHTML() ?> 674 <div id="page p-0"> 675 <div class="d-flex p-1"> 676 <div class="mr-auto" id="toolbar" > 677 <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> 678 <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> 679 <button id="listBtn" class="btn btn-normal" data-toggle="tooltip" data-placement="top" title="<?php echo translate('List') ?>" ><i class="fa fa-list"></i></button> 680 </div> 681 <h2 class="align-self-end"><?php echo translate('Timeline') ?></h2> 682 </div> 683 684 <div id="content" class="chartSize"> 685 <div id="topPanel" class="graphWidth"> 686 <div id="imagePanel"> 687 <div id="image" class="imageHeight"> 688 <img id="imageSrc" class="imageWidth" src="graphics/transparent.png" alt="<?php echo translate('ViewEvent') ?>" title="<?php echo translate('ViewEvent') ?>"/> 689 </div> 690 </div> 691 <div id="dataPanel"> 692 <div id="textPanel"> 693 <div id="instruction"> 694 <p><?php echo translate('TimelineTip1') ?></p> 695 <p><?php echo translate('TimelineTip2') ?></p> 696 <p><?php echo translate('TimelineTip3') ?></p> 697 <p><?php echo translate('TimelineTip4') ?></p> 698 </div> 699 <div id="eventData"> 700 </div> 701 </div> 702 <div id="navPanel"> 703 <button type="button" title="<?php echo translate('PanLeft') ?>" data-on-click="tlPanLeft"> 704 <i class="material-icons md-18">fast_rewind</i> 705 </button> 706 <button type="button" title="<?php echo translate('ZoomOut') ?>" data-on-click="tlZoomOut"> 707<i class="material-icons md-18">zoom_out</i> 708 </button> 709 <button type="button" title="<?php echo translate('PanRight') ?>" data-on-click="tlPanRight"> 710 <i class="material-icons md-18">fast_forward</i> 711 </button> 712 </div> 713 </div> 714 </div> 715 <div id="chartPanel"> 716 <div id="chart" class="graphSize"> 717<?php 718 719function drawSlot($slot,$index) { 720 global $chart; 721 global $monitors; 722 global $mouseover; 723 $height = (int)($slot['value']/$chart['data']['y']['density']); 724 725 if ( $height <= 0 ) 726 return ''; 727 $left = round(100*($index/$chart['graph']['width']),1); 728 729 return "<div class=\"activity monitorColour{$slot['event']['MonitorId']}\" 730 style=\"left:{$left}%; height: {$height}px;\" 731 data-event-id=\"{$slot['event']['Id']}\" data-frame-id=\"".getSlotFrame($slot)."\"". 732 ( $mouseover ? ' data-on-mouseover-this="previewEvent" data-on-click-this="showEvent"' : ' data-on-click-this="previewEvent"'). 733 '></div>'; 734} 735 736if ( $mode == 'overlay' ) { 737 echo drawYGrid( $chart, $majYScale, 'majLabelY', 'majTickY', 'majGridY graphWidth' ); 738} 739echo drawXGrid( $chart, $majXScale, 'majLabelX', 'majTickX', 'majGridX graphHeight', 'tlzoom graphHeight' ); 740if ( $mode == 'overlay' ) { 741?> 742 <div id="activity" class="activitySize"> 743<?php 744 foreach ( $frameSlots as $index=>$slots ) { 745 foreach ( $slots as $slot ) { 746 echo drawSlot($slot, $index); 747 } 748 } 749?> 750 </div> 751<?php 752} else if ( $mode == 'split' ) { 753 foreach ( array_keys($monFrameSlots) as $monitorId ) { 754?> 755 <div id="activity<?php echo $monitorId ?>" class="activitySize"> 756<?php 757 $currFrameSlots = &$monFrameSlots[$monitorId]; 758 foreach ( $currFrameSlots as $index=>$slot ) { 759 echo drawSlot($slot, $index); 760 } # end foreach $currFrameSlots 761 unset($currFrameSlots); 762?> 763 </div> 764<?php 765 } # end foreach $MonitorId 766} 767foreach ( array_keys($monEventSlots) as $monitorId ) { 768?> 769 <div id="events<?php echo $monitorId ?>" class="events eventsSize eventsPos<?php echo $monitorId ?>"> 770<?php 771 $currEventSlots = &$monEventSlots[$monitorId]; 772 for ( $i = 0; $i < $chart['graph']['width']; $i++ ) { 773 if ( isset($currEventSlots[$i]) ) { 774 $slot = &$currEventSlots[$i]; 775 776 $left = round(100*($i/$chart['graph']['width']),1); 777 $width = round(100*($slot['width']/$chart['graph']['width']),1); 778 779 echo "<div class=\"event monitorColour{$slot['event']['MonitorId']}\" 780 style=\"left:{$left}%; width: {$width}%;\" 781 data-event-id=\"{$slot['event']['Id']}\" data-frame-id=\"".getSlotFrame($slot)."\"". 782 ( $mouseover ? ' data-on-mouseover-this="previewEvent" data-on-click-this="showEvent"' : ' data-on-click-this="previewEvent"'). 783 '></div>'; 784 unset( $slot ); 785 } # end if isset($currEventSlots[$i]) 786 } # end foreach width segment 787 unset ($currEventSlots); 788?> 789 </div> 790<?php 791} 792?> 793 </div> 794 </div> 795 <div id="chartLabels" class="graphWidth"> 796 <div id="key"> 797<?php 798foreach( array_keys($monEventSlots) as $monitorId ) { 799?> 800 <span class="keyEntry"><?php echo $monitors[$monitorId]->Name() ?> 801 <div id="keyBox<?php echo $monitorId ?>" class="keyBox monitorColour<?php echo $monitorId ?>" title="<?php echo $monitors[$monitorId]->Name() ?>" style="background-color: <?php echo $monitors[$monitorId]->WebColour() ?>;"></div> 802 </span> 803<?php 804} 805?> 806 </div> 807 <div id="range"><?php echo $title ?></div> 808 </div> 809 </div> 810 </div> 811<?php xhtmlFooter() ?> 812