summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.xml2
-rwxr-xr-xwww/js/DataModel.js43
-rw-r--r--www/js/MomentCtrl.js826
-rw-r--r--www/js/MontageCtrl.js3
-rw-r--r--www/js/PortalLoginCtrl.js4
-rwxr-xr-xwww/js/app.js18
-rw-r--r--www/lang/locale-en.json10
-rw-r--r--www/lang/locale-pl.json7
-rw-r--r--www/templates/image-modal.html13
-rw-r--r--www/templates/moment-mask.html24
-rw-r--r--www/templates/moment-popover.html8
-rw-r--r--www/templates/moment.html62
12 files changed, 725 insertions, 295 deletions
diff --git a/config.xml b/config.xml
index 8bcafef4..acd4c982 100644
--- a/config.xml
+++ b/config.xml
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
-<widget android-packageName="com.pliablepixels.zmninja_pro" id="com.pliablepixels.zmninjapro" ios-CFBundleIdentifier="com.pliablepixels.zmninja-pro" version="1.2.514" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+<widget android-packageName="com.pliablepixels.zmninja_pro" id="com.pliablepixels.zmninjapro" ios-CFBundleIdentifier="com.pliablepixels.zmninja-pro" version="1.2.515" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>zmNinja</name>
<description>
High performance ZoneMinder client
diff --git a/www/js/DataModel.js b/www/js/DataModel.js
index a9602ed1..2c7e640b 100755
--- a/www/js/DataModel.js
+++ b/www/js/DataModel.js
@@ -169,6 +169,9 @@ angular.module('zmApp.controllers')
'enableSlowLoading': false,
'isFullScreen': false,
'reloadInMontage': false,
+ 'momentGridSize': 40,
+ 'momentMonitorFilter': []
+
};
@@ -352,11 +355,11 @@ angular.module('zmApp.controllers')
if (!positionFound) {
if (loginData.currentMontageProfile != $translate.instant('kMontageDefaultProfile')) {
monitors[m].Monitor.listDisplay = 'noshow';
- console.log("*************DISABLE NEW MONITOR");
+ //console.log("*************DISABLE NEW MONITOR");
} else // make sure we add it because its show all view
{
monitors[m].Monitor.listDisplay = 'show';
- console.log("*************ENABLE NEW MONITOR");
+ //console.log("*************ENABLE NEW MONITOR");
}
@@ -484,7 +487,7 @@ angular.module('zmApp.controllers')
},
setLatestBlogPostChecked: function (val) {
- console.log(">>>>>>>>>>>> Setting blog date: " + val);
+ //console.log(">>>>>>>>>>>> Setting blog date: " + val);
latestBlogPostChecked = val;
localforage.setItem("latestBlogPostChecked", latestBlogPostChecked);
},
@@ -1018,6 +1021,22 @@ angular.module('zmApp.controllers')
}
+ if (typeof loginData.momentGridSize == 'undefined') {
+
+ loginData.momentGridSize = 40;
+
+ }
+
+ if (typeof loginData.momentMonitorFilter == 'undefined') {
+
+ loginData.momentMonitorFilter = JSON.stringify([]);
+
+ }
+
+
+
+
+
log("DataModel init recovered this loginData as " + JSON.stringify(loginData));
} else {
log("defaultServer configuration NOT found. Keeping login at defaults");
@@ -1456,10 +1475,10 @@ angular.module('zmApp.controllers')
var apiurl = loginData.apiurl;
var myurl = apiurl + "/monitors.json";
//console.log ("API:"+myurl);
- console.log ("gettign zms port");
+ // console.log ("gettign zms port");
getZmsMultiPortSupport()
.then(function (zmsPort) {
- debug ("ZMS Multiport reported: "+zmsPort);
+ //debug ("ZMS Multiport reported: "+zmsPort);
$http.get(myurl /*,{timeout:15000}*/ )
.success(function (data) {
//console.log("HTTP success got " + JSON.stringify(data.monitors));
@@ -1523,14 +1542,14 @@ angular.module('zmApp.controllers')
}
st += s.host;
- console.log ("STEP 1: ST="+st);
+ // console.log ("STEP 1: ST="+st);
if (zmsPort <=0 )
{
if (p.port || s.port) {
st += (s.port ? ":" + s.port : ":" + p.port);
streamingurl = st;
- console.log ("STEP 2 no ZMS: ST="+st);
+ // console.log ("STEP 2 no ZMS: ST="+st);
}
}
@@ -1540,7 +1559,7 @@ angular.module('zmApp.controllers')
if (p.port || s.port)
st += (s.port ? ":" + s.port : ":" + p.port);
- console.log ("STEP 2: ST="+st);
+ // console.log ("STEP 2: ST="+st);
}
@@ -1550,17 +1569,17 @@ angular.module('zmApp.controllers')
st += (s.path ? s.path : p.path);
streamingurl += (s.path ? s.path : p.path);
- console.log ("STEP 3: ST="+st);
+ //console.log ("STEP 3: ST="+st);
- console.log ("----------STREAMING URL PARSED AS " + st);
+ //console.log ("----------STREAMING URL PARSED AS " + st);
monitors[i].Monitor.streamingURL = st;
monitors[i].Monitor.baseURL = baseurl;
- console.log ("** Streaming="+st+" **base="+baseurl);
+ //console.log ("** Streaming="+st+" **base="+baseurl);
// starting 1.30 we have fid=xxx mode to return images
monitors[i].Monitor.imageMode = (versionCompare($rootScope.apiVersion, "1.30") == -1) ? "path" : "fid";
- debug("API " + $rootScope.apiVersion + ": Monitor " + monitors[i].Monitor.Id + " will use " + monitors[i].Monitor.imageMode + " for direct image access");
+ // debug("API " + $rootScope.apiVersion + ": Monitor " + monitors[i].Monitor.Id + " will use " + monitors[i].Monitor.imageMode + " for direct image access");
//debug ("Streaming URL for Monitor " + monitors[i].Monitor.Id + " is " + monitors[i].Monitor.streamingURL );
//debug ("Base URL for Monitor " + monitors[i].Monitor.Id + " is " + monitors[i].Monitor.baseURL );
diff --git a/www/js/MomentCtrl.js b/www/js/MomentCtrl.js
index f1e1d283..fcf7ad64 100644
--- a/www/js/MomentCtrl.js
+++ b/www/js/MomentCtrl.js
@@ -1,335 +1,655 @@
/* jshint -W041 */
+/*jshint -W069 */
+/*jshint sub:true*/
/* jslint browser: true*/
/* global cordova,StatusBar,angular,console, Masonry */
//https:///zm/api/events/index/AlarmFrames%20%3E=:1/StartTime%20%3E=:2017-12-16%2009:08:50.json?sort=TotScore&direction=desc
-angular.module('zmApp.controllers').controller('zmApp.MomentCtrl', ['$scope', '$rootScope', '$ionicModal', 'NVRDataModel', '$ionicSideMenuDelegate', '$ionicHistory', '$state', '$translate', '$q', '$templateRequest', '$sce', '$compile', '$http', '$ionicLoading', 'zm', '$timeout', '$q', '$ionicPopover','$ionicPopup','message', function($scope, $rootScope, $ionicModal, NVRDataModel, $ionicSideMenuDelegate, $ionicHistory, $state, $translate, $q, $templateRequest, $sce, $compile, $http, $ionicLoading,zm, $timeout, $q, $ionicPopover, $ionicPopup, message)
-{
+angular.module('zmApp.controllers').controller('zmApp.MomentCtrl', ['$scope', '$rootScope', '$ionicModal', 'NVRDataModel', '$ionicSideMenuDelegate', '$ionicHistory', '$state', '$translate', '$templateRequest', '$sce', '$compile', '$http', '$ionicLoading', 'zm', '$timeout', '$q', '$ionicPopover', '$ionicPopup', 'message', '$ionicScrollDelegate', function ($scope, $rootScope, $ionicModal, NVRDataModel, $ionicSideMenuDelegate, $ionicHistory, $state, $translate, $templateRequest, $sce, $compile, $http, $ionicLoading, zm, $timeout, $q, $ionicPopover, $ionicPopup, message, $ionicScrollDelegate) {
+
+ var timeFrom;
+ var timeTo;
+ var moments = [];
+ var monitors = [];
+ var excludeMonitors = [];
+ var excludeMonitorsFilter = "";
+ var momentType = "StartTime";
+
+ $scope.openMenu = function () {
+ $ionicSideMenuDelegate.toggleLeft();
+ };
+
+ //----------------------------------------------------------------
+ // Alarm notification handling
+ //----------------------------------------------------------------
+ $scope.handleAlarms = function () {
+ $rootScope.isAlarm = !$rootScope.isAlarm;
+ if (!$rootScope.isAlarm) {
+ $rootScope.alarmCount = "0";
+ $ionicHistory.nextViewOptions({
+ disableBack: true
+ });
+ $state.go("app.events", {
+ "id": 0,
+ "playEvent": false
+ }, {
+ reload: true
+ });
+ return;
+ }
+ };
- var timeFrom;
- var timeTo;
- var moments = [];
- var monitors = [];
- $scope.openMenu = function()
- {
- $ionicSideMenuDelegate.toggleLeft();
- };
+ function constructMask() {
+
+ excludeMonitorsFilter = "";
+ for (var i=0; i < excludeMonitors.length; i++) {
+ excludeMonitorsFilter = excludeMonitorsFilter + "/MonitorId !=:"+excludeMonitors[i];
+ }
+ NVRDataModel.debug ("Constructed Monitor Filter ="+excludeMonitorsFilter);
+ }
+
+ function process(rawdata) {
+ // console.log (JSON.stringify(data));
+
+ var data = rawdata.data;
+ NVRDataModel.debug("--------> attempting PAGE " + data.pagination.page + " of " + data.pagination.pageCount);
+ for (var i = 0; i < data.events.length; i++) {
+ // console.log ("pushing "+ JSON.stringify(data.data.events[i]));
+
+ var d = getMonitorDimensions(data.events[i].Event.MonitorId);
+ if (d) {
+ data.events[i].Event.width = d.width;
+ data.events[i].Event.height = d.height;
+
+
+ var ratio;
+ var mw = d.width;
+ var mh = d.height;
+ var mo = d.orientation;
+
+ // scale by X if width > height
+ if (mw > mh) {
+ ratio = mw / zm.thumbWidth;
+ data.events[i].Event.thumbWidth = 200;
+ data.events[i].Event.thumbHeight = Math.round(mh / ratio);
+ } else {
+
+ ratio = mh / zm.thumbWidth;
+ data.events[i].Event.thumbHeight = 200;
+ data.events[i].Event.thumbWidth = Math.round(mw / ratio);
- //----------------------------------------------------------------
- // Alarm notification handling
- //----------------------------------------------------------------
- $scope.handleAlarms = function()
- {
- $rootScope.isAlarm = !$rootScope.isAlarm;
- if (!$rootScope.isAlarm)
- {
- $rootScope.alarmCount = "0";
- $ionicHistory.nextViewOptions(
- {
- disableBack: true
- });
- $state.go("app.events",
- {
- "id": 0,
- "playEvent": false
- },
- {
- reload: true
- });
- return;
}
- };
+ if (mo != 0) {
- function getMonitorDimensions(mid) {
+ /*myevents[i].Event.Rotation = {
+ 'transform' : 'rotate('+mo+'deg'+')'
+ }; */
- for (var i=0; i < monitors.length; i++) {
-
- if (mid==monitors[i].Monitor.Id) {
- return {
- width: monitors[i].Monitor.Width,
- height:monitors[i].Monitor.Height
- }
- }
+ var tmp = data.events[i].Event.thumbHeight;
+ data.events[i].Event.thumbHeight = data.events[i].Event.thumbWidth;
+ data.events[i].Event.thumbWidth = tmp;
+
+ } // swap
+
+ //console.log (d.width+"*"+d.height);
+
+ }
+
+ data.events[i].Event.hide = false;
+ data.events[i].Event.icon = "ion-code-working";
+ data.events[i].Event.baseURL = NVRDataModel.getBaseURL(data.events[i].Event.MonitorId);
+ data.events[i].Event.monitorName = NVRDataModel.getMonitorName(data.events[i].Event.MonitorId);
+
+ data.events[i].Event.dateObject = new Date(data.events[i].Event.StartTime);
+
+ data.events[i].Event.humanizeTime = humanizeTime(data.events[i].Event.StartTime);
+
+ var mid = data.events[i].Event.MonitorId;
+ data.events[i].Event.order = i;
+
+
+ // console.log ("---> PUSHING "+data.events[i].Event.StartTime);
+ moments.push(data.events[i]);
+
+
+ }
+
+
+
+
+ }
+
+ // credit https://stackoverflow.com/a/17265125/1361529
+ function objSort() {
+ var args = arguments,
+ array = args[0],
+ case_sensitive, keys_length, key, desc, a, b, i;
+
+ if (typeof arguments[arguments.length - 1] === 'boolean') {
+ case_sensitive = arguments[arguments.length - 1];
+ keys_length = arguments.length - 1;
+ } else {
+ case_sensitive = false;
+ keys_length = arguments.length;
+ }
+
+ return array.sort(function (obj1, obj2) {
+
+ // console.log ("obj1="+JSON.stringify(obj1));
+ // console.log ("obj2="+JSON.stringify(obj2));
+ for (i = 1; i < keys_length; i++) {
+ key = args[i];
+ if (typeof key !== 'string') {
+ // console.log ("ARGS I 0"+args[i][0]);
+ desc = key[1];
+ key = key[0];
+ a = obj1["Event"][args[i][0]];
+ b = obj2["Event"][args[i][0]];
+ } else {
+ desc = false;
+ a = obj1["Event"][args[i]];
+ b = obj2["Event"][args[i]];
+ }
+ // console.log ("a="+a);
+ // console.log ("b="+b);
+
+ if (case_sensitive === false && typeof a === 'string') {
+ a = a.toLowerCase();
+ b = b.toLowerCase();
}
+ if (!desc) {
+ if (a < b) return -1;
+ if (a > b) return 1;
+ } else {
+ if (a > b) return -1;
+ if (a < b) return 1;
+ }
+ }
+ return 0;
+ });
+ } //end of objSort() function
+
+ function getMonitorDimensions(mid) {
+
+ for (var i = 0; i < monitors.length; i++) {
+
+ if (mid == monitors[i].Monitor.Id) {
+ return {
+ width: monitors[i].Monitor.Width,
+ height: monitors[i].Monitor.Height,
+ orientation: monitors[i].Monitor.Orientation
+ };
+ }
}
+ }
- $scope.reLayout = function () {
- NVRDataModel.log ("relaying masonry");
- $timeout (function () {masonry.layout();},300);
-
- };
+ $scope.toggleSubMenu = function () {
- $scope.toggleCollapse = function(mid,eid) {
- NVRDataModel.debug ("toggling collapse for:"+mid);
- var collapseCount=0; collapseId=0;
- for (var i=0; i < $scope.moments.length; i++) {
- if ($scope.moments[i].Event.Id == eid ) {
-
- $scope.moments[i].Event.hide = false;
- collapseId = i;
- $scope.moments[i].Event.icon = $scope.moments[i].Event.icon == "ion-code-working" ? "ion-images" :"ion-code-working" ;
+ $scope.isSubMenu = !$scope.isSubMenu;
+ //($scope.isSubMenu);
+ };
+ $scope.sizeChanged = function (dirn) {
+ var sz = $scope.gridSize;
+ sz = sz + 5 * dirn;
+ if (sz < 5) sz = 5;
+ if (sz > 100) sz = 100;
+ $scope.gridSize = sz;
-
+ var ld = NVRDataModel.getLogin();
+ ld.momentGridSize = $scope.gridSize;
+ NVRDataModel.setLogin(ld);
- }
- else if ($scope.moments[i].Event.MonitorId == mid ) {
- // same monitor, but different ID
-
- $scope.moments[i].Event.hide = !$scope.moments[i].Event.hide ;
- if ($scope.moments[i].Event.hide) collapseCount++;
- //console.log ("Hiding " + i);
- }
- } //for
- if (collapseCount) {
- $scope.moments[collapseId].Event.collapseCount = collapseCount
- } else {
- $scope.moments[collapseId].Event.collapseCount="";
- }
+ $timeout(function () {
+ masonry.layout();
+ }, 300);
-
-
- $timeout ( function () {
- masonry.reloadItems();
-
- },100);
- masonry.once( 'layoutComplete', function( laidOutItems ) {
- $timeout ( function () {masonry.layout();},300);
- } )
+ };
- $timeout ( function () {masonry.layout();},300);
+ $scope.reLayout = function () {
+ NVRDataModel.log("relaying masonry");
+ $timeout(function () {
+ masonry.layout();
+ }, 300);
- };
+ };
- $scope.hourmin = function(str) {
- return moment(str).format(NVRDataModel.getTimeFormat());
+// When a user taps on collapse on an eid,
+// all events after that for the same monitor should be collapsed
+// events before that should remain
- };
+// when a user expands back on an eid, all eids after that for the
+// same monitor should expand, even if they were grouped earlier
- function humanizeTime(str)
- {
- //console.log ("Time:"+str+" TO LOCAL " + moment(str).local().toString());
- //if (NVRDataModel.getLogin().useLocalTimeZone)
- return moment.tz(str, NVRDataModel.getTimeZoneNow()).fromNow();
- // else
- // return moment(str).fromNow();
+ $scope.toggleCollapse = function (mid, eid, ndx) {
+ NVRDataModel.debug("toggling collapse for:" + mid);
+ var collapseCount = 0;
+ var hide = false;
+
+ $scope.moments[ndx].Event.hide = false;
+ if ($scope.moments[ndx].Event.icon == 'ion-code-working') {
+ // we want to hide
+ hide = true;
+ $scope.moments[ndx].Event.icon = 'ion-images';
+ }
+ else { // we want to show
+ hide = false;
+ $scope.moments[ndx].Event.icon = 'ion-code-working';
}
- function initMasonry()
- {
- $ionicLoading.show(
- {
- template: $translate.instant('kArrangingImages'),
- noBackdrop: true,
- duration: zm.loadingTimeout
- });
- var progressCalled = false;
+ for (var i = ndx+1; i < $scope.moments.length; i++ ) {
+ if ($scope.moments[i].Event.MonitorId == mid) {
+ $scope.moments[i].Event.hide = hide;
+ $scope.moments[i].Event.icon = "ion-code-working";
+ $scope.moments[i].Event.collapseCount = "";
+ if (hide) collapseCount++;
+ //console.log ("Hiding " + i);
+ }
+ } //for
+ if (hide) {
+ $scope.moments[ndx].Event.collapseCount = collapseCount;
+ } else {
+ $scope.moments[ndx].Event.collapseCount = "";
+ }
- var ld = NVRDataModel.getLogin();
- var elem = angular.element(document.getElementById("mygrid"));
- masonry = new Masonry('.grid',
- {
- itemSelector: '.grid-item',
- percentPosition: true,
- columnWidth: '.grid-sizer',
- horizontalOrder: true,
- gutter: 2,
- initLayout: true
- });
- //console.log ("**** mygrid is " + JSON.stringify(elem));
- imagesLoaded(elem).on('progress', function(instance, img)
- {
- masonry.layout();
-
- });
- imagesLoaded(elem).once('always', function()
- {
-
- NVRDataModel.debug("All images loaded");
- $ionicLoading.hide();
+ $timeout(function () {
+ masonry.reloadItems();
- $timeout (function() {masonry.layout();},300);
+ }, 100);
- if (!progressCalled)
- {
- NVRDataModel.log("*** PROGRESS WAS NOT CALLED");
- masonry.reloadItems();
- }
+ masonry.once('layoutComplete', function (laidOutItems) {
+ $timeout(function () {
+ masonry.layout();
+ }, 300);
+ });
+
+ $timeout(function () {
+ masonry.layout();
+ // $ionicScrollDelegate.$getByHandle("moment-delegate").scrollTop();
+
+ }, 600);
+
+ };
+
+
+ $scope.hourmin = function (str) {
+ return moment(str).format(NVRDataModel.getTimeFormat());
+
+ };
+
+ $scope.cancelMask = function () {
+ $scope.modal.remove();
+ };
+
+ $scope.saveMask = function () {
+ $scope.modal.remove();
+ excludeMonitors = [];
+
+
+ for (var i=0; i < $scope.monitors.length; i++) {
+ if ($scope.monitors[i].Monitor.listDisplay == 'noshow') {
+ excludeMonitors.push($scope.monitors[i].Monitor.Id);
+ }
- });
}
+ console.log ("ENDED");
+ constructMask();
+
+ var ld = NVRDataModel.getLogin();
+ ld.momentMonitorFilter = JSON.stringify(excludeMonitors);
+ NVRDataModel.setLogin(ld);
+
+ getMoments(momentType);
+
+
+ };
- $scope.closeModal = function()
+
+ $scope.toggleHide = function(i)
{
- NVRDataModel.debug(">>>MomentCtrl:Close & Destroy Modal");
- NVRDataModel.setAwake(false);
- if ($scope.modal !== undefined)
- {
- $scope.modal.remove();
+
+ if ($scope.monitors[i].Monitor.listDisplay == 'show') {
+ $scope.monitors[i].Monitor.listDisplay = 'noshow';
+
+ }
+
+ else {
+ $scope.monitors[i].Monitor.listDisplay = 'show';
+
}
+
+ NVRDataModel.debug("index " + i + " is now " + $scope.monitors[i].Monitor.listDisplay);
+
};
- $scope.showThumbnail = function (b,f) {
- if (!f) {// api update needed
+ $scope.hideUnhide = function() {
- $ionicPopup.alert(
- {
- title: $translate.instant('kNote'),
- template: "{{'kApiUpgrade' | translate }}",
- okText: $translate.instant('kButtonOk'),
- cancelText: $translate.instant('kButtonCancel'),
- });
- return;
+ //console.log (JSON.stringify(monitors));
- }
+
- $scope.thumbnailLarge=b+'/index.php?view=image&fid='+f;
- $ionicModal.fromTemplateUrl('templates/image-modal.html',
- {
- scope: $scope,
- animation: 'slide-in-up',
- id: 'thumbnail',
- })
- .then(function(modal)
- {
- $scope.modal = modal;
-
+ $scope.monitors = monitors;
+ $ionicModal.fromTemplateUrl('templates/moment-mask.html',
+ {
+ scope: $scope,
+ animation: 'slide-in-up',
+ id:'reorder',
+ })
+ .then(function(modal)
+ {
+ $scope.modal = modal;
+ $scope.modal.show();
+ });
- $scope.modal.show();
+ };
- var ld = NVRDataModel.getLogin();
+ function humanizeTime(str) {
+ //console.log ("Time:"+str+" TO LOCAL " + moment(str).local().toString());
+ //if (NVRDataModel.getLogin().useLocalTimeZone)
+ return moment.tz(str, NVRDataModel.getTimeZoneNow()).fromNow();
+ // else
+ // return moment(str).fromNow();
- });
+ }
- };
+ function initMasonry() {
+ $ionicLoading.show({
+ template: $translate.instant('kArrangingImages'),
+ noBackdrop: true,
+ duration: zm.loadingTimeout
+ });
+ var progressCalled = false;
+
+ var ld = NVRDataModel.getLogin();
+
+ var elem = angular.element(document.getElementById("mygrid"));
+ masonry = new Masonry('.grid', {
+ itemSelector: '.grid-item',
+ // columnWidth: 10
+ horizontalOrder: true,
+ gutter: 2,
+ initLayout: true,
+ percentPosition: true,
+
+ });
+ //console.log ("**** mygrid is " + JSON.stringify(elem));
+ imagesLoaded(elem).on('progress', function (instance, img) {
+ masonry.layout();
+
+ });
+ imagesLoaded(elem).once('always', function () {
+
+ NVRDataModel.debug("All images loaded");
+ $ionicLoading.hide();
- $scope.getMoments = function (cond) {
- getMoments (cond);
+ $timeout(function () {
+ masonry.layout();
+ }, 300);
+
+ if (!progressCalled) {
+ NVRDataModel.log("*** PROGRESS WAS NOT CALLED");
+ masonry.reloadItems();
+ }
+
+ });
+ }
+
+ $scope.closeModal = function () {
+ NVRDataModel.debug(">>>MomentCtrl:Close & Destroy Modal");
+ NVRDataModel.setAwake(false);
+ if ($scope.modal !== undefined) {
+ $scope.modal.remove();
}
- function getMoments(sortCondition) {
+ };
- if (sortCondition == 'MaxScore')
- $scope.type = $translate.instant('kMomentMenuByScore');
- else if (sortCondition == 'StartTime')
- $scope.type = $translate.instant('kMomentMenuByTime');
+ $scope.showThumbnail = function (b, f) {
- $scope.apiurl = NVRDataModel.getLogin().apiurl;
- moments.length = 0;
-
- NVRDataModel.setAwake(false);
- var tmptimeto = moment();
- var tmptimefrom = tmptimeto.subtract(24, 'hours');
- var page = 1;
- timeFrom = tmptimefrom.format('YYYY-MM-DD HH:mm:ss');
- timeTo = tmptimeto.format('YYYY-MM-DD HH:mm:ss');
+ if (!f) { // api update needed
- NVRDataModel.debug ("Moments from "+timeFrom+" to "+timeTo);
+ $ionicPopup.alert({
+ title: $translate.instant('kNote'),
+ template: "{{'kApiUpgrade' | translate }}",
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
+ });
+ return;
- //https:///zm/api/events/index/AlarmFrames%20%3E=:1/StartTime%20%3E=:2017-12-16%2009:08:50.json?sort=TotScore&direction=desc
+ }
+
+ $scope.thumbnailLarge = b + '/index.php?view=image&fid=' + f;
+ $ionicModal.fromTemplateUrl('templates/image-modal.html', {
+ scope: $scope,
+ animation: 'slide-in-up',
+ id: 'thumbnail',
+ })
+ .then(function (modal) {
+ $scope.modal = modal;
+
+
+ $scope.modal.show();
var ld = NVRDataModel.getLogin();
- var myurl = ld.apiurl + "/events/index/AlarmFrames >=:1/StartTime >=:"+timeFrom+".json?sort="+sortCondition+"direction=desc";
- NVRDataModel.debug ("Retrieving "+ myurl);
-
- $http.get(myurl+'/&page='+page)
- .then (function (rawdata) {
-
- // console.log (JSON.stringify(data));
- var data = rawdata.data;
- console.log ("--------> PAGES=" + data.pagination.pageCount);
- for (var i=0; i < data.events.length; i++) {
- // console.log ("pushing "+ JSON.stringify(data.data.events[i]));
-
- var d = getMonitorDimensions(data.events[i].Event.MonitorId);
- if (d) {
- data.events[i].Event.width = d.width;
- data.events[i].Event.height= d.height;
- //console.log (d.width+"*"+d.height);
-
- }
-
- data.events[i].Event.hide = false;
- data.events[i].Event.icon = "ion-code-working";
- data.events[i].Event.baseURL = NVRDataModel.getBaseURL(data.events[i].Event.MonitorId);
- data.events[i].Event.monitorName = NVRDataModel.getMonitorName(data.events[i].Event.MonitorId);
-
- data.events[i].Event.dateObject = new Date(data.events[i].Event.StartTime);
-
- data.events[i].Event.humanizeTime = humanizeTime(data.events[i].Event.StartTime);
-
- var mid = data.events[i].Event.MonitorId;
- data.events[i].Event.order = i;
- moments.push (data.events[i]);
- }
-
- // not really sure we need this
- // will see later
- if (sortCondition == "StartTime") {
- moments.sort(function(a, b) {
- var da = a.Event.dateObject;
- var db = b.Event.dateObject;
- return da>db ? -1 : da<db ? 1 : 0;
- });
- }
-
+ });
- $scope.moments = moments;
+ };
- $timeout (function() {initMasonry();},300 );
-
+ $scope.getMoments = function (cond) {
+ momentType = cond;
+ getMoments(cond);
+ };
- },
- function (err) {
- console.log ("ERROR=" + JSON.stringify(err));
- NVRDataModel.displayBanner('error',[translate.instant('kMomentLoadError')]);
+ function noop(err) {
+ //console.log("ERROR NOOP=" + JSON.stringify(err));
+ }
- });
-
+ $scope.changeFrom = function (dirn) {
+ var f;
+ if (dirn==1) { // add a day
+ t = moment(timeTo);
+ t.add (1, "day");
+ if (t > moment()) {
+ NVRDataModel.log ("Future date selected, ignoring");
+ return;
+ }
+ console.log ("T="+t.format("MMM DD,YYYY HH:mm"));
+ }
+ else {
+ t = moment(timeTo);
+ t.subtract (1, "day");
+ console.log ("T="+t.format("MMM DD,YYYY HH:mm"));
- };
+ }
+ var newTo = t.format("YYYY-MM-DD HH:mm:ss");
+ getMoments(momentType,newTo);
- $scope.$on('$ionicView.beforeLeave', function()
- {
- NVRDataModel.debug ("Destroying masonry");
- masonry.destroy();
- });
+ };
-
- $scope.$on('$ionicView.afterEnter', function()
- {
+ function getMoments(sortCondition, to) {
+
+ if (sortCondition == 'MaxScore')
+ $scope.type = $translate.instant('kMomentMenuByScore');
+ else if (sortCondition == 'StartTime')
+ $scope.type = $translate.instant('kMomentMenuByTime');
+ else if (sortCondition == 'MonitorId')
+ $scope.type = $translate.instant('kMomentMenuByMonitor');
+
+ $scope.apiurl = NVRDataModel.getLogin().apiurl;
+ moments.length = 0;
+
+ NVRDataModel.setAwake(false);
+
+ var tmptimeto, tmptimefrom;
+
+ if (!to) {// assume current time
+ tmptimeto = moment();
+ }
+ else {
+ tmptimeto = moment(to);
+ }
- monitors = message;
- $ionicPopover.fromTemplateUrl('templates/moment-popover.html',
- {
- scope: $scope,
- }).then(function(popover)
- {
- $scope.popover = popover;
- });
+ tmptimefrom = angular.copy(tmptimeto);
+ tmptimefrom.subtract(24, 'hours'); // mutable, hence deep copy above
+
+
+ var page = 1;
+ timeFrom = tmptimefrom.format('YYYY-MM-DD HH:mm:ss');
+ timeTo = tmptimeto.format('YYYY-MM-DD HH:mm:ss');
+
+
+ $scope.displayTimeFrom = moment(timeFrom).format("MMM DD,"+NVRDataModel.getTimeFormat());
+ $scope.displayTimeTo = moment(timeTo).format("MMM DD,"+NVRDataModel.getTimeFormat());
+
+ NVRDataModel.debug("Moments from " + timeFrom + " to " + timeTo);
+
+ //https:///zm/api/events/index/AlarmFrames%20%3E=:1/StartTime%20%3E=:2017-12-16%2009:08:50.json?sort=TotScore&direction=desc
- getMoments("StartTime");
+ var ld = NVRDataModel.getLogin();
+
+ // in API, always sort by StartTime so all monitors are represented
+ var myurl = ld.apiurl + "/events/index/AlarmFrames >=:1"+excludeMonitorsFilter+"/StartTime >=:" + timeFrom + "/StartTime <=:"+timeTo+ ".json?sort=" + "StartTime" + "&direction=desc";
+ NVRDataModel.debug("Retrieving " + myurl);
+
+
+
+ $q.all([
+ $http.get(myurl + '&page=1').then(process).catch(noop),
+ $http.get(myurl + '&page=2').then(process).catch(noop),
+ $http.get(myurl + '&page=3').then(process).catch(noop),
+ $http.get(myurl + '&page=4').then(process).catch(noop)
+
+ ])
+ .then(function () {
+ NVRDataModel.debug ("$a.all Parallel queries completed");
+
+ if (!moments.length) {
+ $scope.loadingStatus = $translate.instant('kMomentNoneFound');
+ }
+
+ // not really sure we need this
+ // will see later
+ if (sortCondition == "StartTime") {
+ moments.sort(function (a, b) {
+ var da = a.Event.dateObject;
+ var db = b.Event.dateObject;
+ return da > db ? -1 : da < db ? 1 : 0;
+ });
+ }
+
+ // if we use any other condition, we need to first sort by cond and then time
+ if (sortCondition != "StartTime") {
+ var ascordesc = true;
+ if (sortCondition == 'monitorName') ascordesc = false;
+ //console.log("SORTING BY " + sortCondition);
+ moments = objSort(moments, [sortCondition, ascordesc], ["dateObject", true]);
+ }
+
+ // check the very first element for presence of maxscoreframe id
+ // if its not there, we can't show snuff
+ if (moments.length && !moments[0].Event.MaxScoreFrameId) {
+ $ionicPopup.alert({
+ title: $translate.instant('kNote'),
+ template: "{{'kApiUpgrade' | translate }}",
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
+ });
+ }
+
+ else {
+ $scope.moments = moments;
+ $timeout(function () {
+ initMasonry();
+ }, 300);
+
+ }
- //getMoments ("MaxScore");
-
+ /* if (sortCondition == "MonitorId") {
+ moments.sort(function(a, b) {
+ var da = a.Event.MonitorId;
+ var db = b.Event.MonitorId;
+ return da>db ? -1 : da<db ? 1 : 0;
+ });
+ }*/
+ });
+
+
+
+
+ }
+
+ $scope.$on('$ionicView.beforeLeave', function () {
+ NVRDataModel.debug("Destroying masonry");
+ masonry.destroy();
+ });
+
+
+
+ $scope.$on('$ionicView.beforeEnter', function () {
+ /*var w = Math.round(parseInt($rootScope.devWidth) / parseInt($rootScope.pixelRatio)) ;
+
+ w = $rootScope.devWidth;
+
+ var p = w / 100;
+
+ console.log ("old P="+p);
+ p = Math.ceil(p/5)*5;
+ console.log ("P="+p);*/
+
+ var ld = NVRDataModel.getLogin();
+
+ $scope.loadingStatus = $translate.instant('kLoading');
+ $scope.gridSize = ld.momentGridSize;
+ excludeMonitors = JSON.parse(ld.momentMonitorFilter || []);
+ console.log ("RETRIEVED EXCLUDE="+JSON.stringify(excludeMonitors));
+ constructMask();
+ $scope.isSubMenu = false;
+
+ monitors = angular.copy(message);
+
+ for (var i=0; i < monitors.length; i++) {
+ if (excludeMonitors.indexOf(monitors[i].Monitor.Id) != -1) {
+ monitors[i].Monitor.listDisplay = 'noshow';
+ console.log ("Marking monitor " + monitors[i].Monitor.Id+ " as noshow");
+ }
+
+ }
+
+ });
+
+ $scope.$on('$ionicView.afterEnter', function () {
+
+
+
+
+ $ionicPopover.fromTemplateUrl('templates/moment-popover.html', {
+ scope: $scope,
+ }).then(function (popover) {
+ $scope.popover = popover;
});
+ getMoments(momentType);
+
+ //getMoments ("MaxScore");
+
+
+ });
+
}]);
diff --git a/www/js/MontageCtrl.js b/www/js/MontageCtrl.js
index 49cd60cd..9c09b807 100644
--- a/www/js/MontageCtrl.js
+++ b/www/js/MontageCtrl.js
@@ -1,6 +1,7 @@
// Controller for the montage view
/* jshint -W041 */
+
/* jslint browser: true*/
/* global cordova,StatusBar,angular,console,ionic,Packery, Draggabilly, imagesLoaded, ConnectSDK, moment */
@@ -243,7 +244,7 @@ angular.module('zmApp.controllers')
{
var result = img.isLoaded ? 'loaded' : 'broken';
- NVRDataModel.debug('~~loaded image is ' + result + ' for ' + img.img.src);
+ // NVRDataModel.debug('~~loaded image is ' + result + ' for ' + img.img.src);
// lay out every image if a pre-arranged position has not been found
diff --git a/www/js/PortalLoginCtrl.js b/www/js/PortalLoginCtrl.js
index 4500ff9e..96e0690f 100644
--- a/www/js/PortalLoginCtrl.js
+++ b/www/js/PortalLoginCtrl.js
@@ -432,9 +432,9 @@ angular.module('zmApp.controllers').controller('zmApp.PortalLoginCtrl', ['$ionic
EventServer.refresh();
if ($rootScope.tappedNotification == 0)
{
- console.log ("NOTIFICATION TAPPED INSIDE CHECK IS "+$rootScope.tappedNotification);
+ //console.log ("NOTIFICATION TAPPED INSIDE CHECK IS "+$rootScope.tappedNotification);
var statetoGo = $rootScope.lastState ? $rootScope.lastState : 'app.montage';
- NVRDataModel.debug("logging state transition");
+ // NVRDataModel.debug("logging state transition");
NVRDataModel.debug("Transitioning state to: " +
statetoGo + " with param " + JSON.stringify($rootScope.lastStateParam));
diff --git a/www/js/app.js b/www/js/app.js
index d8c78a0d..f01fd11f 100755
--- a/www/js/app.js
+++ b/www/js/app.js
@@ -1405,6 +1405,7 @@ angular.module('zmApp', [
var checkOrientation = function () {
var pixelRatio = window.devicePixelRatio || 1;
+ $rootScope.pixelRatio = pixelRatio;
$rootScope.devWidth = ((window.innerWidth > 0) ? window.innerWidth : screen.width);
$rootScope.devHeight = ((window.innerHeight > 0) ? window.innerHeight : screen.height);
//console.log("********NEW Computed Dev Width & Height as" + $rootScope.devWidth + "*" + $rootScope.devHeight);
@@ -1441,7 +1442,7 @@ angular.module('zmApp', [
var requireLogin = toState.data.requireLogin;
- console.log("HERE");
+ //console.log("HERE");
if ($rootScope.apiValid == false && toState.name != 'invalidapi' && toState.data.requireLogin == true) {
event.preventDefault();
@@ -1658,8 +1659,9 @@ angular.module('zmApp', [
});
function continueInitialInit() {
- console.log("continueinit");
+ // console.log("continueinit");
var pixelRatio = window.devicePixelRatio || 1;
+ $rootScope.pixelRatio = pixelRatio;
$rootScope.devWidth = ((window.innerWidth > 0) ? window.innerWidth : screen.width);
$rootScope.devHeight = ((window.innerHeight > 0) ? window.innerHeight : screen.height);
// for making sure we canuse $state.go with ng-click
@@ -1668,16 +1670,16 @@ angular.module('zmApp', [
$rootScope.$stateParams = $stateParams;
if (window.cordova && window.cordova.plugins.Keyboard) {
- console.log("no keyboard");
+ // console.log("no keyboard");
// cordova.plugins.Keyboard.disableScroll(true);
}
if (window.StatusBar) {
// org.apache.cordova.statusbar required
- console.log("statusbar");
+ // console.log("statusbar");
NVRDataModel.log("Updating statusbar");
StatusBar.styleDefault();
if ($rootScope.platformOS=='ios') {
- console.log ("<<<<<<<<<<<<<<<< OVERLAY");
+ // console.log ("<<<<<<<<<<<<<<<< OVERLAY");
StatusBar.overlaysWebView(false);
}
@@ -1685,12 +1687,12 @@ angular.module('zmApp', [
}
if (window.cordova) {
- console.log("Hiding splash");
+ // console.log("Hiding splash");
$cordovaSplashscreen.hide();
- console.log("app version");
+ // console.log("app version");
cordova.getAppVersion.getVersionNumber().then(function (version) {
appVersion = version;
NVRDataModel.log("App Version: " + appVersion);
@@ -1698,7 +1700,7 @@ angular.module('zmApp', [
});
}
- console.log("file logger");
+ // console.log("file logger");
$fileLogger.checkFile().then(function (resp) {
if (parseInt(resp.size) > zm.logFileMaxSize) {
console.log("inside file logger");
diff --git a/www/lang/locale-en.json b/www/lang/locale-en.json
index be205729..60277461 100644
--- a/www/lang/locale-en.json
+++ b/www/lang/locale-en.json
@@ -210,10 +210,12 @@
"kMinVersion" :"Minimum Required Version",
"kMinimumIntervals" :"minimum interval",
"kMode" :"Mode",
- "kMoment24Heading" : "24hr Preview",
- "kMomentLoadError" : "Error retrieving moments",
- "kMomentMenuByTime" : "by time",
- "kMomentMenuByScore" : "by score",
+ "kMoment24Heading" :"24hr Preview",
+ "kMomentLoadError" :"Error retrieving moments",
+ "kMomentMenuByMonitor" :"by camera",
+ "kMomentMenuByTime" :"by time",
+ "kMomentMenuByScore" :"by score",
+ "kMomentNoneFound" :"No events found",
"kMonAlarmed" :"alarmed",
"kMonAlert" :"alert",
"kMonIdle" :"idle",
diff --git a/www/lang/locale-pl.json b/www/lang/locale-pl.json
index 9cdb38e9..c81f1f28 100644
--- a/www/lang/locale-pl.json
+++ b/www/lang/locale-pl.json
@@ -9,6 +9,7 @@
"kAlarms" :"Alarmy",
"kAll" :"Wszystko",
"kAnalyze" :"Analizuj",
+ "kApiUpgrade" :"potrzebny upgrade API",
"kApiUrl" :"Adres url API ZM",
"kApiUrlExample" :"np. server/zm/api",
"kApplyingChanges" :"Stosuję zmiany. Proszę czekać",
@@ -91,6 +92,7 @@
"kErrorRetrievingState" :"błąd odczytu stanu",
"kErrorSave" :"Błąd - nie mogłem zapisać",
"kEvent" :"zdarzenie",
+ "kEventStillRecording" :"nadal nagrywa",
"kEventHistFaster" :"szybciej",
"kEventHistHrs" :"godzin temu",
"kEventHistPause" :"pauza",
@@ -207,6 +209,11 @@
"kMinVersion" :"Minimalna Wymagana Wersja",
"kMinimumIntervals" :"minimalna przerwa",
"kMode" :"Tryb",
+ "kMoment24Heading" :"Podgląd 24h",
+ "kMomentLoadError" :"Błąd pobierania podglądów",
+ "kMomentMenuByMonitor" :"po kamerze",
+ "kMomentMenuByTime" :"po czasie",
+ "kMomentMenuByScore" :"po punktacji",
"kMonAlarmed" :"zaalarmowany",
"kMonAlert" :"alert",
"kMonIdle" :"bezczynny",
diff --git a/www/templates/image-modal.html b/www/templates/image-modal.html
index d38ca5b9..a682a8e6 100644
--- a/www/templates/image-modal.html
+++ b/www/templates/image-modal.html
@@ -1,9 +1,14 @@
<ion-modal-view cache-view="false" style="background-color:#444444">
- <ion-content>
- <ion-scroll direction="xy" scrollbar-x="false" scrollbar-y="false" overflow-scroll="false" zooming="true">
- <img on-double-tap="closeModal();" ng-src="{{thumbnailLarge}}" style="display:block; width:100%" />
+ <ion-content ng-cloak on-double-tap="closeModal();" scroll="false">
- </ion-scroll>
+
+ <div id="imagecontainer">
+ <ion-scroll has-bouncing=false min-zoom=1 zooming="true"
+ direction="xy" style="width: 100%;" overflow-scroll="false">
+ <img style="width:100vw; height:100vh;" on-double-tap="closeModal();" ng-src="{{thumbnailLarge}}" style="display:block; width:100%" />
+
+ </ion-scroll>
+ </div>
</ion-content>
</ion-modal-view>
diff --git a/www/templates/moment-mask.html b/www/templates/moment-mask.html
new file mode 100644
index 00000000..51657b19
--- /dev/null
+++ b/www/templates/moment-mask.html
@@ -0,0 +1,24 @@
+<ion-modal-view cache-view="false" style="width: 90%; height: 90%; top: 5%; left: 5%; right: 5%; bottom: 5%;">
+ <ion-header-bar class="bar-stable">
+ <h1 class="title"></h1>
+ <div class="buttons">
+ <button class="button button-icon icon ion-checkmark" ng-click="saveMask()"></button>
+ <button class="button button-icon icon ion-close" ng-click="cancelMask()"></button>
+ </div>
+ </ion-header-bar>
+ <ion-content>
+ <div class="list">
+ <span ng-repeat="item in monitors">
+
+ <a ng-class="{ 'item item-icon-right' : item.Monitor.listDisplay == 'show', 'item item-icon-right eye-background-red' : item.Monitor.listDisplay!='show' }" ng-click="toggleHide($index)" href="" >
+
+
+
+ <i ng-class="{'icon ion-eye':item.Monitor.listDisplay=='show','eye-red icon ion-eye-disabled':item.Monitor.listDisplay!='show'}"> </i><h2><span ng-class="{'eye-red':item.Monitor.listDisplay!='show'}">{{item.Monitor.Name}}</span></h2><p>{{'kId' | translate}}:{{item.Monitor.Id}}, {{'kMode' | translate}}:{{item.Monitor.Function}}</p>
+
+ </a>
+
+ </span>
+ </div>
+ </ion-content>
+</ion-modal-view>
diff --git a/www/templates/moment-popover.html b/www/templates/moment-popover.html
index 49dbea89..2da37112 100644
--- a/www/templates/moment-popover.html
+++ b/www/templates/moment-popover.html
@@ -4,9 +4,15 @@
<a class="item" ng-href="" ng-click="popover.hide();getMoments('StartTime');">
{{'kMomentMenuByTime' | translate}}
+
</a>
+
+ <a class="item" ng-href="" ng-click="popover.hide();getMoments('monitorName');">{{'kMomentMenuByMonitor' | translate}}</a>
+
+
<a class="item" ng-href="" ng-click="popover.hide();getMoments('MaxScore');">{{'kMomentMenuByScore' | translate}}</a>
-
+
+
</div>
</ion-content>
</ion-popover-view>
diff --git a/www/templates/moment.html b/www/templates/moment.html
index 40881ae2..12cc2c95 100644
--- a/www/templates/moment.html
+++ b/www/templates/moment.html
@@ -2,28 +2,72 @@
<ion-nav-title>{{'kMoment24Heading' | translate}} {{type}}</ion-nav-title>
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" ng-click="openMenu()"></button>
+ <button class="button button-icon button-clear ion-gear-b" ng-click="hideUnhide()"></button>
+
<button data-badge="{{$root.alarmCount}}" class="animated infinite tada button button-icon button-clear ion-ios-bell notification-badge" ng-click="handleAlarms();" ng-if="$root.isAlarm"></button>
</ion-nav-buttons>
<ion-nav-buttons side="right">
- <a class="button button-icon icon ion-grid" ng-click="reLayout()" ;></a>
+ <a class="button button-icon icon ion-chevron-down" ng-click="toggleSubMenu()" ;></a>
+
+ <a class="button button-icon icon ion-android-more-vertical " ng-click="popover.show($event)" ;>&nbsp;&nbsp;&nbsp;</a>
+
- <a class="button button-icon icon ion-android-more-vertical" ng-click="popover.show($event)" ;>&nbsp;&nbsp;&nbsp;</a>
</ion-nav-buttons>
- <ion-content class="padding" scroll-sista delegate-handle="none" overflow-scroll="false" mouse-wheel-scroll style="background-color:#444444" >
+
+ <div class="events-float-filter" >
+ {{displayTimeFrom}} - {{displayTimeTo}}</span>
+ </div>
+
+
+ <ion-content class="padding" delegate-handle="moment-delegate" overflow-scroll="false" mouse-wheel-scroll style="background-color:#444444" >
+
+
+ <div ng-if="isSubMenu">
+ <br/>
+ <div id="flyoutmenu" style="float:left">
+ <ul>
+ <li>
+ <a href="" ng-click="sizeChanged(1)"> <i class="ion-plus-circled"></i></a>
+ </li>
+ <li>
+ <a href="" ng-click="sizeChanged(-1)"> <i class="ion-minus-circled"></i></a>
+ </li>
+ <li>
+ <a href="" ng-click="reLayout()"> <i class="ion-grid"></i></a>
+ </li>
+ </ul>
+ </div>
+
+ <div id="flyoutmenu" style="float:right">
+ <ul>
+ <li>
+ <a href="" ng-click="changeFrom(-1)"> <i class="ion-chevron-left"></i></a>
+ </li>
+ <li>
+ <a href="" ng-click="changeFrom(1)"> <i class="ion-chevron-right"></i></a>
+ </li>
+
+ </ul>
+ </div>
+
+ <div style="clear: both;"></div>
+ <br/>
+ </div>
+
- <div style="color:white" ng-if="!moments.length">
- Nothing to show yet...
+ <div style="color:white; text-align:center" ng-if="!moments.length">
+ {{loadingStatus}}
</div>
<div class="grid" id="mygrid">
- <div class="grid-sizer grid-item-10"></div>
- <figure class="grid-item" ng-repeat="moment in moments | onlyEnabledMoments">
- <figcaption class="normal-figheader">{{moment.Event.monitorName}}<span style="float:right"><button class="button button-small button-icon icon {{moment.Event.icon}}" ng-click="toggleCollapse(moment.Event.MonitorId, moment.Event.Id)"></button>{{moment.Event.collapseCount}}&nbsp;</span></figcaption>
- <img image-spinner-src="{{moment.Event.baseURL}}/index.php?view=image&fid={{moment.Event.MaxScoreFrameId}}" img-spinner-w="{{moment.Event.width}}"
+
+ <figure class="grid-item grid-item-{{gridSize}}" ng-repeat="moment in moments | onlyEnabledMoments">
+ <figcaption class="normal-figheader">{{moment.Event.monitorName}}<span style="float:right"><button class="button button-small button-icon icon {{moment.Event.icon}}" ng-click="toggleCollapse(moment.Event.MonitorId, moment.Event.Id, $index)"></button>{{moment.Event.collapseCount}}&nbsp;</span></figcaption>
+ <img image-spinner-src="{{moment.Event.baseURL}}/index.php?view=image&fid={{moment.Event.MaxScoreFrameId}}&width={{moment.Event.thumbWidth*2}}&height={{moment.Event.thumbHeight*2}}" img-spinner-w="{{moment.Event.width}}"
img-spinner-h="{{moment.Event.height}}" image-spinner-loader="lines"
on-tap="showThumbnail(moment.Event.baseURL,moment.Event.MaxScoreFrameId)"/>
<figcaption class="normal-figcaption">{{moment.Event.humanizeTime}}<span style="float:right">{{hourmin(moment.Event.StartTime)}}</span></figcaption>