// Controller for the montage view /* jshint -W041 */ /* jslint browser: true*/ /* global cordova,StatusBar,angular,console,ionic,Packery, Draggabilly, imagesLoaded, ConnectSDK, moment */ angular.module('zmApp.controllers') .controller('zmApp.MontageCtrl', ['$scope', '$rootScope', 'NVR', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$ionicPopup', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$ionicPlatform', 'zm', '$ionicPopover', '$controller', 'imageLoadingDataShare', '$window', '$localstorage', '$translate', 'SecuredPopups', 'EventServer', function ($scope, $rootScope, NVR, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $ionicPopup, $stateParams, $ionicHistory, $ionicScrollDelegate, $ionicPlatform, zm, $ionicPopover, $controller, imageLoadingDataShare, $window, $localstorage, $translate, SecuredPopups, EventServer) { //--------------------------------------------------------------------- // Controller main //--------------------------------------------------------------------- var timeInMontage = new Date(); var intervalHandleMontage; // image re-load handler var intervalHandleAlarmStatus; // status of each alarm state var intervalHandleMontageCycle; var intervalHandleReloadPage; var intervalHandleEventStatus; var gridcontainer; var pckry, draggie; var draggies; var loginData; var timestamp; var sizeInProgress; var modalIntervalHandle; var ld; var refreshSec; var reloadPage = zm.forceMontageReloadDelay; var viewCleaned = false; var randToAvoidCacheMem; var streamState = { SNAPSHOT: 1, ACTIVE: 2, STOPPED: 3, PAUSED: 4 }; var currentStreamState = streamState.SNAPSHOT; // first load snapshot $scope.isModalStreamPaused = false; // used in Monitor Modal //var reloadPage = 30; var simulStreaming = false; // will be true if you multiport var broadcastHandles = []; var as = $scope.$on("auth-success", function () { NVR.debug ("Auth success, recomputing rand value..."); randEachTime(); /* var tnow = new Date(); var s = Math.round((tnow - timeInMontage) / 1000); NVR.debug ("Montage re-auth: time since we are here: " + s + " seconds"); if (s >= 20) {// lets not regen connkey if we just got into montage NVR.debug ("Montage-reauth: Regenerating connkeys"); $timeout(function () { // after render if (simulStreaming) { NVR.debug("Re-creating all stream connkeys in montage ..."); NVR.regenConnKeys(); $scope.monitors = NVR.getMonitorsNow(); $scope.MontageMonitors = angular.copy($scope.monitors); $timeout(function () // after render { initPackery(); }, zm.packeryTimer); } }); } else { NVR.debug ("Montage re-auth: ignoring as time is only "+s+" seconds"); }*/ // do nothing, we are reloading here anyway? /* if ($scope.singleMonitorModalOpen) { NVR.debug("Montage: Not creating streams, as modal is open"); return; } NVR.debug("Montage Re-auth handler"); //streamState = true; $timeout(function () { // after render if (simulStreaming) { NVR.debug("Re-creating all stream connkeys in montage ..."); NVR.regenConnKeys(); $scope.monitors = NVR.getMonitorsNow(); $scope.MontageMonitors = angular.copy($scope.monitors); $timeout(function () // after render { initPackery(); }, zm.packeryTimer); } }); broadcastHandles.push(as); //console.log (">>>>>>>>>>>>>>>>>>>>>>>>>>>AS="+as); */ }); //-------------------------------------------------------------------------------------- // Handles bandwidth change, if required // //-------------------------------------------------------------------------------------- var bc = $scope.$on("bandwidth-change", function (e, data) { // not called for offline, I'm only interested in BW switches NVR.debug("Got network change:" + data); var ds; if (data == 'lowbw') { ds = $translate.instant('kLowBWDisplay'); } else { ds = $translate.instant('kHighBWDisplay'); } NVR.displayBanner('net', [ds]); var ld = NVR.getLogin(); refreshSec = (NVR.getBandwidth() == 'lowbw') ? ld.refreshSecLowBW : ld.refreshSec; $interval.cancel(intervalHandleMontage); $interval.cancel(intervalHandleMontageCycle); $interval.cancel(intervalHandleReloadPage); intervalHandleMontage = $interval(function () { loadNotifications(); }.bind(this), refreshSec * 1000); intervalHandleMontageCycle = $interval(function () { cycleMontageProfiles(); }.bind(this), NVR.getLogin().cycleMontageInterval * 1000); intervalHandleReloadPage = $interval(function () { forceReloadPage(); }.bind(this), reloadPage); if (NVR.getBandwidth() == 'lowbw') { NVR.debug("Enabling low bandwidth parameters"); $scope.LoginData.montageQuality = zm.montageQualityLowBW; $scope.LoginData.singleImageQuality = zm.eventSingleImageQualityLowBW; $scope.LoginData.montageHistoryQuality = zm.montageQualityLowBW; } }); broadcastHandles.push(bc); // -------------------------------------------------------- // Handling of back button in case modal is open should // close the modal // -------------------------------------------------------- $ionicPlatform.registerBackButtonAction(function (e) { e.preventDefault(); if ($scope.modal != undefined && $scope.modal.isShown()) { // switch off awake, as liveview is finished NVR.debug("Modal is open, closing it"); NVR.setAwake(false); cleanupOnCloseModal(); } else { NVR.debug("Modal is closed, so toggling or exiting"); if (!$ionicSideMenuDelegate.isOpenLeft()) { $ionicSideMenuDelegate.toggleLeft(); } else { navigator.app.exitApp(); } } }, 1000); /*$scope.toggleHide = function(mon) { if (mon.Monitor.listDisplay == 'noshow') mon.Monitor.listDisplay = 'show'; else mon.Monitor.listDisplay = 'noshow'; };*/ function forceReloadPage() { //return; if ($scope.isDragabillyOn) { NVR.debug("not reloading, edit in progress"); return; } var ld = NVR.getLogin(); ld.reloadInMontage = true; NVR.log("Reloading view to keep memory in check..."); NVR.setLogin(ld) .then(function () { //window.location.reload(true); //location.reload(); //$ionicHistory.clearCache(); //$state.go('app.montage'); /*$ionicHistory.clearCache([$state.current.name]).then(function() { $state.go('app.montage', $stateParams, {reload:true, inherit:false}); });*/ $ionicHistory.nextViewOptions({ disableAnimate: true, disableBack: true }); $state.go('app.refresh', { "view": 'app.montage' }); }); /*$ionicHistory.nextViewOptions( { disableAnimate: true, disableBack: true }); $state.go("app.montage", { minimal: $scope.minimal, isRefresh: true });*/ } // called by afterEnter to load Packery function initPackery() { /* $ionicLoading.show( { template: $translate.instant('kArrangingImages'), noBackdrop: true, duration: zm.loadingTimeout });*/ currentStreamState = streamState.SNAPSHOT; $scope.areImagesLoading = true; var progressCalled = false; if (draggies) { draggies.forEach(function (drag) { drag.destroy(); }); } draggies = []; var layouttype = true; var ld = NVR.getLogin(); var positionsStr = ld.packeryPositions; var positions = {}; if (positionsStr == '' || positionsStr == undefined) { NVR.log("Did NOT find a packery layout"); layouttype = true; } else { //console.log ("POSITION STR IS " + positionsStr); positions = JSON.parse(positionsStr); NVR.log("found a packery layout"); layouttype = false; } var cnt = 0; $scope.MontageMonitors.forEach(function (elem) { if ((elem.Monitor.Enabled != '0') && (elem.Monitor.Function != 'None')) cnt++; }); NVR.log("Monitors that are active and not DOM hidden: " + cnt + " while grid has " + positions.length); if (cnt > NVR.getLogin().maxMontage) { cnt = NVR.getLogin().maxMontage; NVR.log("restricting monitor count to " + cnt + " due to max-montage setting"); } if (cnt != positions.length) { NVR.log("Whoops!! Monitors have changed. I'm resetting layouts, sorry!"); layouttype = true; positions = {}; } var elem = angular.element(document.getElementById("mygrid")); var loadCount = 0; //console.log ("**** mygrid is " + JSON.stringify(elem)); if (pckry) pckry.destroy(); pckry = new Packery('.grid', { itemSelector: '.grid-item', percentPosition: true, //columnWidth: '.grid-sizer', gutter: 0, initLayout: layouttype, shiftPercentResize: true, transitionDuration: 0 }); imagesLoaded(elem).on('progress', function (instance, img) { var result = img.isLoaded ? 'loaded' : 'broken'; // NVR.debug('~~loaded image is ' + result + ' for ' + img.img.src); // lay out every image if a pre-arranged position has not been found $timeout(function () { if (layouttype || 1) pckry.layout(); }, 100); progressCalled = true; loadCount++; // console.log ("loaded "+loadCount+" of "+positions.length); // if (layouttype) $timeout (function(){layout(pckry);},100); }); $timeout(function () { if ($scope.areImagesLoading) { NVR.debug("Images still loading after 15secs?"); allImagesLoadedOrFailed(); } }, 15000); imagesLoaded(elem).on('always', function () { //console.log ("******** ALL IMAGES LOADED"); // $scope.$digest(); NVR.debug("All images loaded, switching to snapshot..."); allImagesLoadedOrFailed(); }); imagesLoaded(elem).on('fail', function () { NVR.debug("All images loaded, but some broke, switching to snapshot..."); //console.log ("******** ALL IMAGES LOADED"); // $scope.$digest(); allImagesLoadedOrFailed(); }); function allImagesLoadedOrFailed() { $timeout(function () { $scope.areImagesLoading = false; }); currentStreamState = streamState.SNAPSHOT; if (simulStreaming) { $timeout(function () { NVR.debug("Switching mode to active..."); currentStreamState = streamState.ACTIVE; }, 100); } $ionicLoading.hide(); if (!progressCalled) { NVR.log("*** PROGRESS WAS NOT CALLED"); // pckry.reloadItems(); } $timeout(function () { pckry.getItemElements().forEach(function (itemElem) { draggie = new Draggabilly(itemElem); pckry.bindDraggabillyEvents(draggie); draggies.push(draggie); draggie.disable(); draggie.unbindHandles(); }); pckry.on('dragItemPositioned', itemDragged); if (!isEmpty(positions)) { NVR.log("Arranging as per packery grid"); for (var i = 0; i < $scope.MontageMonitors.length; i++) { for (var j = 0; j < positions.length; j++) { if ($scope.MontageMonitors[i].Monitor.Id == positions[j].attr) { if (isNaN(positions[j].size)) positions[j].size = 20; if (positions[j].size == 0) positions[j].size = 20; $scope.MontageMonitors[i].Monitor.gridScale = positions[j].size; $scope.MontageMonitors[i].Monitor.listDisplay = positions[j].display; // NVR.debug("Setting monitor ID: " + $scope.MontageMonitors[i].Monitor.Id + " to size: " + positions[j].size + " and display:" + positions[j].display); } //console.log ("Index:"+positions[j].attr+ " with size: " + positions[j].size); } } NVR.debug("All images loaded, doing image layout"); } $timeout(function () { //NVR.log("Force calling resize"); ///pckry.reloadItems(); ///positions is defined only if layouttype was false //(">>> Positions is " + JSON.stringify(positions)); if (!layouttype && positions) pckry.initShiftLayout(positions, "data-item-id"); // now do a jiggle $timeout(function () { NVR.debug("doing the jiggle and dance..."); pckry.resize(true); $scope.squeezeMonitors(); }, 500); }, 100); //pckry.onresize(); }, 20); } function itemDragged(item) { NVR.debug("drag complete"); $timeout(function () { pckry.shiftLayout(); }, 20); pckry.once('layoutComplete', function () { var positions = pckry.getShiftPositions('data-item-id'); //console.log ("POSITIONS MAP " + JSON.stringify(positions)); var ld = NVR.getLogin(); ld.packeryPositions = JSON.stringify(positions); // console.log ("Saving " + ld.packeryPositions); // console.log ("FULL OBJECT "+ JSON.stringify(ld)); ld.currentMontageProfile = ""; $scope.currentProfileName = $translate.instant('kMontage'); NVR.setLogin(ld); NVR.debug("saved new positions"); }); //pckry.getItemElements().forEach(function (itemElem) { //console.log (itemElem.attributes['data-item-id'].value+" size "+itemElem.attributes['data-item-size'].value ); // }); } } function isEmpty(obj) { for (var prop in obj) { return false; } return true; } //----------------------------------------------------------------------- // color for monitor state in montage //----------------------------------------------------------------------- $scope.stateColor = function () { //console.log ("***MONSTATUS**"+$scope.monStatus+"**"); var attr = ""; switch ($scope.monStatus) { case "": attr = "color:rgba(0, 0, 0, 0)"; break; case "idle": attr = "color:rgba(0, 0, 0, 0)"; break; case "pre-alarm": attr = "color:#e67e22"; break; case "alarmed": attr = "color:#D91E18"; break; case "alert": attr = "color:#e67e22"; break; case "record": attr = "color:#26A65B"; break; } return attr; }; function findNext(key, obj) { // console.log (" key is: "+ key); // console.log ("array is " + JSON.stringify (obj)); var keys = Object.keys(obj); var len = keys.length; var curindex = keys.indexOf(key); var modulus = (curindex + 1) % len; //console.log ("*********** len="+len+" curr="+curindex+" next="+modulus); //console.log ("Keys array "+ JSON.stringify(keys)); //console.log ("Current index: "+ keys.indexOf(key) ); //console.log ("returning index of " + (keys.indexOf(key) + 1) % (keys.length)); // console.log ("keys length is "+ keys.length); return keys[modulus]; /* var size = Object.keys(obj).length; var i; for (i=0; i