summaryrefslogtreecommitdiff
path: root/www/js/DataModel.js
diff options
context:
space:
mode:
Diffstat (limited to 'www/js/DataModel.js')
-rwxr-xr-xwww/js/DataModel.js2193
1 files changed, 2193 insertions, 0 deletions
diff --git a/www/js/DataModel.js b/www/js/DataModel.js
new file mode 100755
index 00000000..8f702c66
--- /dev/null
+++ b/www/js/DataModel.js
@@ -0,0 +1,2193 @@
+/* jshint -W041 */
+
+/* jslint browser: true*/
+/* global cordova,StatusBar,angular,console, URI, moment, localforage, CryptoJS, Connection */
+
+// This is my central data respository and common functions
+// that many other controllers use
+// It's grown over time. I guess I may have to split this into multiple services in the future
+
+angular.module('zmApp.controllers')
+
+.service('NVRDataModel', ['$http', '$q', '$ionicLoading', '$ionicBackdrop', '$fileLogger', 'zm', '$rootScope', '$ionicContentBanner', '$timeout', '$cordovaPinDialog', '$ionicPopup', '$localstorage', '$state', '$ionicNativeTransitions', '$translate', '$cordovaSQLite',
+ function($http, $q, $ionicLoading, $ionicBackdrop, $fileLogger,
+ zm, $rootScope, $ionicContentBanner, $timeout, $cordovaPinDialog,
+ $ionicPopup, $localstorage, $state, $ionicNativeTransitions, $translate)
+ {
+
+ var zmAppVersion = "unknown";
+ var isBackground = false;
+ var justResumed = false;
+ var monitorsLoaded = 0;
+ //var montageSize = 3;
+ var monitors = [];
+ var multiservers = [];
+ var oldevents = [];
+ var migrationComplete = false;
+
+ var tz = "";
+ var isTzSupported = false;
+
+ var languages = [
+ {
+ text: 'English',
+ value: 'en'
+ },
+ {
+ text: 'العربية',
+ value: 'ar'
+ },
+ {
+ text: 'Deutsch',
+ value: 'de'
+ },
+ {
+ text: 'Español',
+ value: 'es'
+ },
+
+ {
+ text: 'Français',
+ value: 'fr'
+ },
+
+ {
+ text: 'Italiano',
+ value: 'it'
+ },
+ {
+ text:'Magyar',
+ value:'hu'
+ },
+ {
+ text: 'Nederlands',
+ value: 'nl'
+ },
+ {
+ text: 'Polski',
+ value: 'pl'
+ },
+ {
+ text: 'Portugese',
+ value: 'pt'
+ },
+ {
+ text: 'Русский',
+ value: 'ru'
+ },
+
+
+
+ /* {
+ text: 'Hindi',
+ value: 'hi'
+ }*/
+ ];
+
+ var serverGroupList = {};
+ var defaultLang = 'en';
+ var isFirstUse = true;
+ var lastUpdateCheck = null;
+ var latestBlogPostChecked = null;
+ var loginData = {
+ 'serverName': '',
+ 'username': '',
+ 'password': '',
+ 'fallbackConfiguration': '',
+ 'url': '', // This is the ZM portal path
+ 'apiurl': '', // This is the API path
+ 'eventServer': '', //experimental Event server address
+ 'maxMontage': "100", //total # of monitors to display in montage
+ 'streamingurl': "",
+ 'maxFPS': "3", // image streaming FPS
+ 'montageQuality': "50", // montage streaming quality in %
+ 'singleImageQuality': "100", // event single streaming quality in %
+ 'monSingleImageQuality': "100", // live view quality
+ 'montageHistoryQuality': "50",
+ 'useSSL': false, // "1" if HTTPS
+ 'keepAwake': true, // don't dim/dim during live view
+ 'isUseAuth': true, // true if user wants ZM auth
+ 'isUseEventServer': false, // true if you configure the websocket event server
+ 'disablePush': false, // true if only websocket mode is desired
+ 'eventServerMonitors': '', // list of monitors to notify from ES
+ 'eventServerInterval': '', // list of intervals for all monitors
+ 'refreshSec': '2', // timer value for frame change in sec
+ 'refreshSecLowBW': 8,
+ 'enableLogs': true,
+ 'enableDebug': true, // if enabled with log messages with "debug"
+ 'usePin': false,
+ 'pinCode': '',
+ 'canSwipeMonitors': true,
+ 'persistMontageOrder': false,
+ 'onTapScreen': "",
+ 'enableh264': true,
+ 'gapless': false,
+ 'montageOrder': '',
+ 'montageHiddenOrder': '',
+ 'montageArraySize': '0',
+ 'showMontageSubMenu': false,
+ 'graphSize': 2000,
+ 'enableAlarmCount': true,
+ 'minAlarmCount': 1,
+ 'montageSize': '3',
+ 'useNphZms': true,
+ 'useNphZmsForEvents': true,
+ 'packMontage': false,
+ 'exitOnSleep': false,
+ 'forceNetworkStop': false,
+ 'defaultPushSound': false,
+ 'enableBlog': true,
+ 'use24hr': false,
+ 'packeryPositions': '',
+ 'currentMontageProfile': '',
+ 'packeryPositionsArray': {},
+ 'EHpackeryPositions': '',
+ 'packerySizes': '',
+ 'timelineModalGraphType': 'all',
+ 'resumeDelay': 0,
+ 'language': 'en',
+ 'reachability': true,
+ 'forceImageModePath': false,
+ 'disableNative': false,
+ 'vibrateOnPush': true,
+ 'soundOnPush': true,
+ 'cycleMonitors': false,
+ 'cycleMontage': false,
+ 'cycleMontageInterval': 10, // 10sec
+ 'cycleMonitorsInterval': 10, // 10sec
+ 'enableLowBandwidth': false,
+ 'autoSwitchBandwidth': false,
+ 'disableAlarmCheckMontage': false,
+ 'useLocalTimeZone': true,
+ 'fastLogin': true,
+ 'followTimeLine': false,
+ 'timelineScale': -1,
+ 'hideArchived': false,
+ 'videoPlaybackSpeed': 2,
+ 'enableGIFMP4': false,
+ 'enableStrictSSL': false,
+ 'enableSlowLoading': false,
+
+ };
+
+ var defaultLoginData = angular.copy(loginData);
+
+ var configParams = {
+ 'ZM_EVENT_IMAGE_DIGITS': '-1',
+ 'ZM_PATH_ZMS': ''
+ };
+
+
+ function setSSLCerts()
+ {
+ if (!window.cordova) return;
+ if (!loginData.enableStrictSSL)
+ {
+
+ //alert("Enabling insecure SSL");
+ log(">>>> Disabling strict SSL checking (turn off in Dev Options if you can't connect)");
+ cordova.plugins.certificates.trustUnsecureCerts(true);
+
+ }
+ else
+ {
+
+ log(">>>> Enabling strict SSL checking (turn off in Dev Options if you can't connect)");
+ cordova.plugins.certificates.trustUnsecureCerts(false);
+ }
+ }
+
+
+ // credit: http://stackoverflow.com/questions/4994201/is-object-empty
+ function isEmpty(obj)
+ {
+
+ // null and undefined are "empty"
+ if (obj == null) return true;
+
+ // Assume if it has a length property with a non-zero value
+ // that that property is correct.
+ if (obj.length > 0) return false;
+ if (obj.length === 0) return true;
+
+ // Otherwise, does it have any properties of its own?
+ // Note that this doesn't handle
+ // toString and valueOf enumeration bugs in IE < 9
+ for (var key in obj)
+ {
+ if (hasOwnProperty.call(obj, key)) return false;
+ }
+
+ return true;
+ }
+
+ function getBandwidth()
+ {
+ // if mode is not on always return high
+ if (loginData.enableLowBandwidth == false)
+ {
+ return "highbw";
+ }
+ // if mode is force on, return low
+ if (loginData.enableLowBandwidth == true && loginData.autoSwitchBandwidth != true)
+ {
+ return "lowbw";
+ }
+ if (loginData.enableLowBandwidth == true && loginData.autoSwitchBandwidth == true && $rootScope.platformOS == 'desktop')
+ {
+ return "highbw";
+ }
+ // else return real state
+
+ var networkState = navigator.connection.type;
+ var strState;
+ switch (networkState)
+ {
+
+ case Connection.WIFI:
+ strState = "highbw";
+ break;
+ case Connection.ETHERNET:
+ strState = "highbw";
+ break;
+ default:
+ strState = "lowbw";
+ break;
+
+ }
+ return strState;
+ }
+
+ //--------------------------------------------------------------------------
+ // uses fileLogger to write logs to file for later investigation
+ //--------------------------------------------------------------------------
+
+ // separate out a debug so we don't do this if comparison for normal logs
+ function debug(val)
+ {
+ if (loginData.enableDebug && loginData.enableLogs)
+ {
+ if (val !== undefined)
+ {
+ var regex1 = /"password":".*?"/g;
+ var regex2 = /&pass=.*?(?=["&]|$)/g;
+
+ //console.log ("VAL IS " + val);
+ val = val.replace(regex1, "<password removed>");
+ val = val.replace(regex2, "<password removed>");
+ }
+ $fileLogger.debug(val);
+ //console.log (val);
+ }
+ }
+
+ function log(val, logtype)
+ {
+ if (loginData.enableLogs)
+ {
+ if (val !== undefined)
+ {
+ var regex1 = /"password":".*?"/g;
+ var regex2 = /&pass=.*?(?=["&]|$)/g;
+
+ //console.log ("VAL IS " + val);
+ val = val.replace(regex1, "<password removed>");
+ val = val.replace(regex2, "<password removed>");
+
+ }
+ // make sure password is removed
+ //"username":"zmninja","password":"xyz",
+ //val = val.replace(/\"password:\",
+ $fileLogger.log(logtype, val);
+ // console.log (val);
+ }
+ }
+
+ function reloadMonitorDisplayStatus()
+ {
+ debug("Loading hidden/unhidden status for profile:"+loginData.currentMontageProfile);
+
+ var positionsStr = loginData.packeryPositions;
+ //console.log ("positionStr="+positionsStr);
+ var positions = {};
+ if (loginData.packeryPositions != '' && loginData.packeryPositions != undefined)
+ {
+ console.log ("positions="+loginData.packeryPositions);
+
+
+ positions = JSON.parse(positionsStr);
+ for (var m = 0; m < monitors.length; m++)
+ {
+ var positionFound = false;
+ for (var p = 0; p < positions.length; p++)
+ {
+ if (monitors[m].Monitor.Id == positions[p].attr)
+ {
+ monitors[m].Monitor.listDisplay = positions[p].display;
+ positionFound = true;
+ debug("DataModel: Setting MID:" + monitors[m].Monitor.Id + " to " + monitors[m].Monitor.listDisplay);
+ }
+
+ }
+ if (!positionFound)
+ {
+ if (loginData.currentMontageProfile != $translate.instant('kMontageDefaultProfile'))
+ {
+ monitors[m].Monitor.listDisplay = 'noshow';
+ 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");
+ }
+
+
+ }
+
+ }
+
+ }
+ else // if there are no packery positions, make sure all are displayed!
+ {
+ debug ("no packery profile, making sure monitors are show");
+ for (var m1 = 0; m1 < monitors.length; m1++)
+ {
+ monitors[m1].Monitor.listDisplay = 'show';
+
+ }
+
+
+ }
+ }
+
+ function setLogin(newLogin)
+ {
+ loginData = angular.copy(newLogin);
+ serverGroupList[loginData.serverName] = angular.copy(loginData);
+
+ var ct = CryptoJS.AES.encrypt(JSON.stringify(serverGroupList), zm.cipherKey).toString();
+
+ //console.log ("****serverLogin was encrypted to " + ct);
+ //$localstorage.setObject("serverGroupList", serverGroupList);
+ localforage.setItem("serverGroupList", ct, function(err)
+ {
+ if (err) log("localforage store error " + JSON.stringify(err));
+ });
+ //$localstorage.set("defaultServerName", loginData.serverName);
+ localforage.setItem("defaultServerName", loginData.serverName, function(err)
+ {
+ if (err) log("localforage store error " + JSON.stringify(err));
+ });
+
+ }
+
+ //credit: https://gist.github.com/alexey-bass/1115557
+ function versionCompare(left, right)
+ {
+ if (typeof left + typeof right != 'stringstring')
+ return false;
+
+ var a = left.split('.');
+ var b = right.split('.');
+ var i = 0;
+ var len = Math.max(a.length, b.length);
+
+ for (; i < len; i++)
+ {
+ if ((a[i] && !b[i] && parseInt(a[i]) > 0) || (parseInt(a[i]) > parseInt(b[i])))
+ {
+ return 1;
+ }
+ else if ((b[i] && !a[i] && parseInt(b[i]) > 0) || (parseInt(a[i]) < parseInt(b[i])))
+ {
+ return -1;
+ }
+ }
+
+ return 0;
+ }
+
+ //--------------------------------------------------------------------------
+ // Banner display of messages
+ //--------------------------------------------------------------------------
+ function displayBanner(mytype, mytext, myinterval, mytimer)
+ {
+
+ var contentBannerInstance =
+ $ionicContentBanner.show(
+ {
+ text: mytext || 'no text',
+ interval: myinterval || 2000,
+ //autoClose: mytimer || 6000,
+ type: mytype || 'info',
+ transition: 'vertical',
+ //cancelOnStateChange: false
+ });
+
+ $timeout(function()
+ {
+ contentBannerInstance();
+ }, mytimer || 6000);
+ }
+
+ return {
+
+ //-------------------------------------------------------------
+ // used by various controllers to log messages to file
+ //-------------------------------------------------------------
+
+ migrationComplete: function()
+ {
+ migrationComplete = true;
+ },
+
+ isEmpty: function(obj)
+ {
+ return isEmpty(obj);
+ },
+
+ log: function(val, type)
+ {
+ var logtype = 'info';
+ if (type != undefined)
+ logtype = type;
+ log(val, logtype);
+
+ },
+
+ debug: function(val)
+ {
+
+ debug(val);
+ },
+
+ setLastUpdateCheck: function(val)
+ {
+ lastUpdateCheck = val;
+ localforage.setItem("lastUpdateCheck", lastUpdateCheck);
+ },
+
+ getLastUpdateCheck: function()
+ {
+ return lastUpdateCheck;
+ },
+
+ setLatestBlogPostChecked: function(val)
+ {
+ console.log (">>>>>>>>>>>> Setting blog date: " + val);
+ latestBlogPostChecked = val;
+ localforage.setItem("latestBlogPostChecked", latestBlogPostChecked);
+ },
+
+ getLatestBlogPostChecked: function()
+ {
+ return latestBlogPostChecked;
+ },
+
+ // This function is called when the app is ready to run
+ // sets up various variables
+ // including persistent login data for the ZM apis and portal
+ // The reason I need both is because as of today, there is no way
+ // to access images using the API and they are authenticated via
+ // the ZM portal authentication, which is pretty messy. But unless
+ // the ZM authors fix this and streamline the access of images
+ // from APIs, I don't have an option
+
+ zmStateGo: function(state, p1, p2)
+ {
+ if ($rootScope.platformOS == 'desktop')
+ $state.go(state, p1, p2);
+ else
+ $ionicNativeTransitions.stateGo(state, p1, p2);
+ },
+
+ // used when an empty server profile is created
+ getDefaultLoginObject: function()
+ {
+ return angular.copy(defaultLoginData);
+ },
+
+ getReachableConfig: function(skipFirst)
+ {
+ var d = $q.defer();
+ if (loginData.serverName == "")
+ {
+ log("Reachable: No server name configured, likely first use?");
+ d.reject("No servers");
+ return d.promise;
+ }
+
+ var chainURLs = [];
+ var savedLoginData = angular.copy(loginData);
+
+ //log ("Making sure " + loginData.serverName + " is reachable...");
+ var tLd = serverGroupList[loginData.serverName];
+ if (skipFirst && tLd.fallbackConfiguration)
+ {
+ tLd = serverGroupList[tLd.fallbackConfiguration];
+ if (!tLd)
+ {
+ d.reject("No available severs");
+ loginData = savedLoginData;
+ return d.promise;
+
+ }
+ }
+
+ var keepBuilding = true;
+ while (keepBuilding == true && tLd)
+ {
+ if (arrayObjectIndexOf(chainURLs, tLd.url + "/index.php", "url") == -1 && tLd.url !== undefined && tLd.url != '') // no loop
+ {
+ log("Adding to chain stack: " + tLd.serverName + ">" + tLd.url);
+ chainURLs.push(
+ {
+ url: tLd.url + "/index.php",
+ server: tLd.serverName
+ });
+ log("Fallback of " + tLd.serverName + " is " + tLd.fallbackConfiguration);
+ if (tLd.fallbackConfiguration)
+ {
+ tLd = serverGroupList[tLd.fallbackConfiguration];
+ if (tLd === undefined)
+ {
+ // This can happen if the fallback profile was deleted
+ log("Looks like a server object was deleted, but is still in fallback");
+ keepBuilding = false;
+ }
+ }
+ else
+ {
+ log("reached end of chain loop");
+ }
+ }
+ else
+ {
+ log("detected loop when " + tLd.serverName + " fallsback to " + tLd.fallbackConfiguration);
+ keepBuilding = false;
+ }
+ }
+
+ //contactedServers.push(loginData.serverName);
+ findFirstReachableUrl(chainURLs).then(function(firstReachableUrl)
+ {
+ d.resolve(firstReachableUrl);
+ // also make sure loginData points to this now
+
+ loginData = angular.copy(serverGroupList[firstReachableUrl.server]);
+
+ setLogin(loginData);
+ //$localstorage.set("defaultServerName",firstReachableUrl.server);
+
+ log("Based on reachability, first serverName will be " + firstReachableUrl.server);
+ //console.log("set login Data to " + JSON.stringify(loginData));
+
+ return d.promise;
+ // OK: do something with firstReachableUrl
+ }, function()
+ {
+ d.reject("No servers reachable");
+ loginData = savedLoginData;
+ return d.promise;
+ // KO: no url could be reached
+ });
+
+ function arrayObjectIndexOf(myArray, searchTerm, property)
+ {
+ for (var i = 0, len = myArray.length; i < len; i++)
+ {
+ if (myArray[i][property] === searchTerm)
+ return i;
+ }
+ return -1;
+ }
+
+ function findFirstReachableUrl(urls)
+ {
+ if (urls.length > 0 && $rootScope.userCancelledAuth != true)
+ {
+ $ionicLoading.show(
+ {
+ template: $translate.instant('kTrying') + ' ' + urls[0].server
+ });
+ log("Reachability test.." + urls[0].url);
+
+ if (loginData.reachability)
+ {
+
+ //console.log ("************* AUGH");
+ var hDelay = loginData.enableSlowLoading? zm.largeHttpTimeout:zm.httpTimeout;
+ return $http({method:'GET', timeout:hDelay, url:urls[0].url}).then(function()
+ {
+ log("Success: reachability on " + urls[0].url);
+ $ionicLoading.hide();
+ return urls[0];
+ }, function(err)
+ {
+ log("Failed reachability on " + urls[0].url + " with error " + JSON.stringify(err));
+ return findFirstReachableUrl(urls.slice(1));
+ });
+ }
+ else
+ {
+ log("Reachability is disabled in config, faking this test and returning success on " + urls[0]);
+ return urls[0];
+ }
+ }
+ else
+ {
+ $ionicLoading.hide();
+ return $q.reject("No reachable URL");
+
+ }
+
+ }
+
+ return d.promise;
+
+ },
+
+ init: function()
+ {
+ // console.log("****** DATAMODEL INIT SERVICE CALLED ********");
+
+ log("ZMData init: checking for stored variables & setting up log file");
+
+ localforage.getItem("latestBlogPostChecked")
+ .then (function (val) {latestBlogPostChecked = val;},
+ function (err) {latestBlogPostChecked = null;});
+
+
+ $ionicLoading.show(
+ {
+ template: $translate.instant('kRetrievingProfileData'),
+ });
+
+ localforage.getItem("serverGroupList").then(function(val)
+ {
+ // decrypt it now
+
+ var decodedVal;
+
+ if (typeof val == 'string')
+ {
+ log("user profile encrypted, decoding...");
+ var bytes = CryptoJS.AES.decrypt(val.toString(), zm.cipherKey);
+ decodedVal = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
+
+ }
+ else
+ {
+ log("user profile not encrypted");
+ decodedVal = val;
+ }
+
+ //decodedVal = val;
+
+ // debug("user profile retrieved:" + JSON.stringify(decodedVal));
+
+ $ionicLoading.hide();
+ serverGroupList = decodedVal;
+
+ // console.log(">>>> DECRYPTED serverGroupList " + JSON.stringify(serverGroupList));
+ var demoServer = "{\"serverName\":\"zmNinjaDemo\",\"username\":\"zmninja\",\"password\":\"zmNinja$xc129\",\"url\":\"https://demo.zoneminder.com/zm\",\"apiurl\":\"https://demo.zoneminder.com/zm/api\",\"eventServer\":\"\",\"maxMontage\":\"40\",\"streamingurl\":\"https://demo.zoneminder.com/cgi-bin-zm\",\"maxFPS\":\"3\",\"montageQuality\":\"50\",\"singleImageQuality\":\"100\",\"montageHistoryQuality\":\"50\",\"useSSL\":true,\"keepAwake\":true,\"isUseAuth\":\"1\",\"isUseEventServer\":false,\"disablePush\":false,\"eventServerMonitors\":\"\",\"eventServerInterval\":\"\",\"refreshSec\":\"2\",\"enableDebug\":false,\"usePin\":false,\"pinCode\":\"\",\"canSwipeMonitors\":true,\"persistMontageOrder\":false,\"onTapScreen\":\"Events\",\"enableh264\":true,\"gapless\":false,\"montageOrder\":\"\",\"montageHiddenOrder\":\"\",\"montageArraySize\":\"0\",\"graphSize\":2000,\"enableAlarmCount\":true,\"montageSize\":\"3\",\"useNphZms\":true,\"useNphZmsForEvents\":true,\"packMontage\":false,\"exitOnSleep\":false,\"forceNetworkStop\":false,\"defaultPushSound\":false,\"enableBlog\":true,\"use24hr\":false, \"packeryPositions\":\"\"}";
+ var demoS = JSON.parse(demoServer);
+ //console.log("JSON parsed demo" + JSON.stringify(demoS));
+
+ var isFoundDemo = false;
+ var as = Object.keys(serverGroupList);
+ for (var x = 0; x < as.length; x++)
+ {
+ if (as[x] == 'zmNinjaDemo')
+ isFoundDemo = true;
+ //console.log ("************ FOUND SERVER NAME " + as[x]);
+ // if serverGroupList[x]
+ }
+
+ // Don't add the demo if there is another server
+ // because this means the user deleted it
+
+ if (!isFoundDemo && as.length == 0)
+ {
+ debug("Pushing demo server config to server groups");
+ //serverGroupList.push(demoS);
+ serverGroupList[demoS.serverName] = angular.copy(demoS);
+ }
+
+ var sname;
+ $ionicLoading.show(
+ {
+ template: $translate.instant('kRetrievingProfileData'),
+ });
+ localforage.getItem("defaultServerName")
+ .then(function(val)
+ {
+ $ionicLoading.hide();
+ //console.log ("!!!!!!!!!!!!!!!!!!default server name is " + sname);
+ sname = val;
+ // console.log("!!!!!!!!!!!!!!!!!!!Got VAL " + sname);
+ var loadedData = serverGroupList[sname];
+ // console.log(">>>>>>>>>>> loadedData is: " + JSON.stringify(loadedData));
+ if (!isEmpty(loadedData))
+ {
+ loginData = loadedData;
+
+ // old version hacks for new variables
+
+ // always true Oct 27 2016
+ loginData.persistMontageOrder = true;
+ loginData.enableh264 = true;
+
+ if (typeof loginData.enableAlarmCount === 'undefined')
+ {
+ debug("enableAlarmCount does not exist, setting to true");
+ loginData.enableAlarmCount = true;
+ }
+
+ if (typeof loginData.onTapScreen == 'undefined')
+ {
+ loginData.onTapScreen = $translate.instant('kTapMontage');
+ }
+
+ if (loginData.onTapScreen != $translate.instant('kTapMontage') &&
+ loginData.onTapScreen != $translate.instant('kTapEvents') &&
+ loginData.onTapScreen != $translate.instant('kTapLiveMonitor'))
+ {
+ log("Invalid onTap setting found, resetting");
+ loginData.onTapScreen = $translate.instant('kMontage');
+ }
+
+ if (typeof loginData.minAlarmCount === 'undefined')
+ {
+ debug("minAlarmCount does not exist, setting to true");
+ loginData.minAlarmCount = 1;
+ }
+
+ if (typeof loginData.montageSize == 'undefined')
+ {
+ debug("montageSize does not exist, setting to 2 (2 per col)");
+ loginData.montageSize = 2;
+ }
+
+ if (typeof loginData.useNphZms == 'undefined')
+ {
+ debug("useNphZms does not exist. Setting to true");
+ loginData.useNphZms = true;
+ }
+
+ if (typeof loginData.useNphZmsForEvents == 'undefined')
+ {
+ debug("useNphZmsForEvents does not exist. Setting to true");
+ loginData.useNphZmsForEvents = true;
+ }
+
+ if (typeof loginData.forceImageModePath == 'undefined')
+ {
+ debug("forceImageModePath does not exist. Setting to false");
+ loginData.forceImageModePath = false;
+ }
+
+ if (typeof loginData.reachability == 'undefined')
+ {
+ debug("reachability does not exist. Setting to true");
+ loginData.reachability = true;
+ }
+ // force it - this may not be the problem
+ loginData.reachability = true;
+
+ // and now, force enable it
+ loginData.useNphZms = true;
+ loginData.useNphZmsForEvents = true;
+
+ if (typeof loginData.packMontage == 'undefined')
+ {
+ debug("packMontage does not exist. Setting to false");
+ loginData.packMontage = false;
+ }
+
+ if (typeof loginData.forceNetworkStop == 'undefined')
+ {
+ debug("forceNetwork does not exist. Setting to false");
+ loginData.forceNetworkStop = false;
+ }
+
+ if (typeof loginData.enableLogs == 'undefined')
+ {
+ debug("enableLogs does not exist. Setting to true");
+ loginData.enableLogs = true;
+ }
+
+ if (typeof loginData.defaultPushSound == 'undefined')
+ {
+ debug("defaultPushSound does not exist. Setting to false");
+ loginData.defaultPushSound = false;
+ }
+
+ if (typeof loginData.exitOnSleep == 'undefined')
+ {
+ debug("exitOnSleep does not exist. Setting to false");
+ loginData.exitOnSleep = false;
+ }
+
+ if (typeof loginData.enableBlog == 'undefined')
+ {
+ debug("enableBlog does not exist. Setting to true");
+ loginData.enableBlog = true;
+
+ }
+
+ if (typeof loginData.packeryPositionsArray == 'undefined')
+ {
+ debug("packeryPositionsArray does not exist. Setting to empty");
+ loginData.packeryPositionsArray = {};
+
+ }
+
+
+ if (typeof loginData.packeryPositions == 'undefined')
+ {
+ debug("packeryPositions does not exist. Setting to empty");
+ loginData.packeryPositions = "";
+
+ }
+
+ if (typeof loginData.EHpackeryPositions == 'undefined')
+ {
+ debug("EHpackeryPositions does not exist. Setting to empty");
+ loginData.EHpackeryPositions = "";
+
+ }
+
+ if (typeof loginData.packerySizes == 'undefined')
+ {
+ debug("packerySizes does not exist. Setting to empty");
+ loginData.packerySizes = "";
+
+ }
+
+ if (typeof loginData.use24hr == 'undefined')
+ {
+ debug("use24hr does not exist. Setting to false");
+ loginData.use24hr = false;
+
+ }
+
+ if (typeof timelineModalGraphType == 'undefined')
+ {
+ debug("timeline graph type not set. Setting to all");
+ loginData.timelineModalGraphType = $translate.instant('kGraphAll');
+ //console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" + loginData.timelineModalGraphType);
+ }
+
+ if (typeof loginData.resumeDelay == 'undefined')
+ {
+ debug("resumeDelay does not exist. Setting to 0");
+ loginData.resumeDelay = 0;
+
+ }
+ // override resumeDelay - it was developed on a wrong assumption
+ loginData.resumeDelay = 0;
+
+ if (typeof loginData.montageHistoryQuality == 'undefined')
+ {
+ debug("montageHistoryQuality does not exist. Setting to 50");
+ loginData.montageHistoryQuality = "50";
+
+ }
+
+ if (typeof loginData.disableNative == 'undefined')
+ {
+ debug("disableNative not found, setting to false");
+ loginData.disableNative = false;
+
+ }
+
+ if (typeof loginData.vibrateOnPush == 'undefined')
+ {
+ debug("vibrate on push not found, setting to true");
+ loginData.vibrateOnPush = true;
+
+ }
+
+ if (typeof loginData.soundOnPush == 'undefined')
+ {
+ debug("sound on push not found, setting to true");
+ loginData.soundOnPush = true;
+
+ }
+
+ if (typeof loginData.cycleMonitors == 'undefined')
+ {
+
+ loginData.cycleMonitors = false;
+
+ }
+
+ if (typeof loginData.cycleMonitorsInterval == 'undefined')
+ {
+
+ loginData.cycleMonitorsInterval = 10;
+
+ }
+
+ if (typeof loginData.cycleMontage == 'undefined')
+ {
+
+ loginData.cycleMontage = false;
+
+ }
+
+ if (typeof loginData.cycleMontageInterval == 'undefined')
+ {
+
+ loginData.cycleMontageInterval = 10;
+
+ }
+
+ if (typeof loginData.enableLowBandwidth == 'undefined')
+ {
+
+ loginData.enableLowBandwidth = false;
+
+ }
+ // wtf is wrong with this ternary?
+ //$rootScope.runMode = (loginData.enableLowBandwith==true)? "low": "normal";
+
+ if (typeof loginData.autoSwitchBandwidth == 'undefined')
+ {
+
+ loginData.autoSwitchBandwidth = false;
+
+ }
+
+ $rootScope.runMode = getBandwidth();
+ log("Setting DataModel init bandwidth to: " + $rootScope.runMode);
+
+ if (typeof loginData.refreshSecLowBW == 'undefined')
+ {
+
+ loginData.refreshSecLowBW = 8;
+
+ }
+
+ if (typeof loginData.disableAlarmCheckMontage == 'undefined')
+ {
+
+ loginData.disableAlarmCheckMontage = false;
+
+ }
+
+ if (typeof loginData.useLocalTimeZone == 'undefined')
+ {
+
+ loginData.useLocalTimeZone = true;
+
+ }
+
+ if (typeof loginData.fastLogin == 'undefined')
+ {
+
+ loginData.fastLogin = true;
+
+ }
+
+ if (typeof loginData.currentMontageProfile == 'undefined')
+ {
+
+ loginData.currentMontageProfile = '';
+
+ }
+
+ if (typeof loginData.followTimeLine == 'undefined')
+ {
+
+ loginData.followTimeLine = false;
+
+ }
+
+ if (typeof loginData.timelineScale == 'undefined')
+ {
+
+ loginData.timelineScale = -1;
+
+ }
+
+
+ if (typeof loginData.showMontageSubMenu == 'undefined')
+ {
+
+ loginData.showMontageSubMenu = false;
+
+ }
+
+
+
+ if (typeof loginData.monSingleImageQuality == 'undefined')
+ {
+
+ loginData.monSingleImageQuality = 100;
+
+ }
+
+ if (typeof loginData.hideArchived == 'undefined')
+ {
+
+ loginData.hideArchived = false;
+
+ }
+
+ if (typeof loginData.videoPlaybackSpeed == 'undefined')
+ {
+
+ loginData.videoPlaybackSpeed = 2;
+
+ }
+
+ if (typeof loginData.enableGIFMP4 == 'undefined')
+ {
+
+ loginData.enableGIFMP4 = true;
+
+ }
+
+ if (typeof loginData.enableSlowLoading == 'undefined')
+ {
+
+ loginData.enableSlowLoading = false;
+
+ }
+ log ("SlowDelay is: "+loginData.enableSlowLoading);
+
+ if (typeof loginData.enableStrictSSL == 'undefined')
+ {
+
+ loginData.enableStrictSSL = false;
+
+ }
+
+ log("DataModel init recovered this loginData as " + JSON.stringify(loginData));
+ }
+ else
+ {
+ log("defaultServer configuration NOT found. Keeping login at defaults");
+ }
+
+ // now set up SSL - need to do it after data return
+ // from local forage
+ setSSLCerts();
+
+
+ // FIXME: HACK: This is the latest entry point into dataModel init, so start portal login after this
+ // not the neatest way
+ $rootScope.$emit('init-complete');
+ });
+
+ monitorsLoaded = 0;
+ //console.log("Getting out of NVRDataModel init");
+ $rootScope.showBlog = loginData.enableBlog;
+ //debug("loginData structure values: " + JSON.stringify(loginData));
+
+ });
+
+ },
+
+ isForceNetworkStop: function()
+ {
+ return loginData.forceNetworkStop;
+ },
+
+ setJustResumed: function(val)
+ {
+ justResumed = true;
+ },
+
+ stopNetwork: function(str)
+ {
+ var s = "";
+ if (str) s = str + ":";
+ if (justResumed)
+ {
+ // we don't call stop as we did stop on pause
+ log(s + " Not calling window stop as we just resumed");
+ justResumed = false;
+ }
+ else
+ {
+ log(s + " Calling window.stop()");
+ window.stop();
+ }
+ },
+
+ isLoggedIn: function()
+ {
+
+ if ((loginData.username != "" && loginData.password != "" && loginData.url != "" &&
+ loginData.apiurl != "") || (loginData.isUseAuth != '1'))
+ {
+ return 1;
+ }
+ else
+ {
+
+ return 0;
+
+ }
+ },
+
+ getLanguages: function()
+ {
+ return languages;
+ },
+
+ setDefaultLanguage: function(l, permanent)
+ {
+
+ if (!l) l = 'en';
+ defaultLang = l;
+ var d = $q.defer();
+ if (permanent)
+ {
+ //window.localStorage.setItem("defaultLang", l);
+
+ //console.log("setting default lang");
+ localforage.setItem("defaultLang", l)
+ .then(function(val)
+ {
+ log("Set language in localforage to: " + val);
+ });
+ }
+
+ //console.log("invoking translate use with " + l);
+ $translate.use(l).then(function(data)
+ {
+ log("Device Language is:" + data);
+ moment.locale(data);
+ $translate.fallbackLanguage('en');
+ d.resolve(data);
+ return d.promise;
+ }, function(error)
+ {
+ log("Device Language error: " + error);
+ $translate.use('en');
+ moment.locale('en');
+ d.resolve('en');
+ return d.promise;
+ });
+ return d.promise;
+ },
+
+ getDefaultLanguage: function()
+ {
+ return defaultLang;
+ //return window.localStorage.getItem("defaultLang");
+
+ },
+
+ reloadMonitorDisplayStatus: function()
+ {
+ return reloadMonitorDisplayStatus();
+ },
+
+ getLogin: function()
+ {
+
+ return angular.copy(loginData);
+ },
+
+ getServerGroups: function()
+ {
+ return angular.copy(serverGroupList);
+ },
+
+ setServerGroups: function(sg)
+ {
+ serverGroupList = angular.copy(sg);
+ },
+
+ getKeepAwake: function()
+ {
+ return (loginData.keepAwake == '1') ? true : false;
+ },
+
+ setAppVersion: function(ver)
+ {
+ zmAppVersion = ver;
+ },
+
+ getAppVersion: function()
+ {
+ return (zmAppVersion);
+ },
+
+ setBackground: function(val)
+ {
+ isBackground = val;
+ },
+
+ isBackground: function()
+ {
+ return isBackground;
+ },
+
+ isFirstUse: function()
+ {
+ // console.log("isFirstUse is " + isFirstUse);
+ return isFirstUse;
+ // return ((window.localStorage.getItem("isFirstUse") == undefined) ? true : false);
+
+ },
+
+ versionCompare: function(l, r)
+ {
+ return versionCompare(l, r);
+ },
+
+ //-----------------------------------------------------------------
+ // Allow the option to reset first use if I need it in future
+ //-----------------------------------------------------------------
+ setFirstUse: function(val)
+ {
+ //window.localStorage.setItem("isFirstUse", val ? "1" : "0");
+ //localforage.setItem("isFirstUse", val,
+ // function(err) {if (err) log ("localforage error, //storing isFirstUse: " + JSON.stringify(err));});
+ isFirstUse = val;
+ localforage.setItem("isFirstUse", val);
+ //console.log (">>>>>>setting isFirstUse to " + val);
+
+ },
+
+ getTimeFormat: function()
+ {
+ return (loginData.use24hr ? "HH:mm" : "hh:mm a");
+ },
+
+ getTimeFormatSec: function()
+ {
+ return (loginData.use24hr ? "HH:mm:ss" : "hh:mm:ss a");
+ },
+
+ //------------------------------------------------------------------
+ // switches screen to 'always on' or 'auto'
+ //------------------------------------------------------------------
+ setAwake: function(val)
+ {
+
+ //console.log ("**** setAwake called with:" + val);
+ // log("Switching screen always on to " + val);
+ if (val)
+ {
+
+ if (window.cordova != undefined)
+ {
+ window.plugins.insomnia.keepAwake();
+ }
+ else
+ {
+ //console.log ("Skipping insomnia, cordova does not exist");
+ }
+ }
+ else
+ {
+ if (window.cordova != undefined)
+ {
+ window.plugins.insomnia.allowSleepAgain();
+ }
+ else
+ {
+ //console.log ("Skipping insomnia, cordova does not exist");
+ }
+
+ }
+
+ },
+
+ //--------------------------------------------------------------------------
+ // writes all params to local storage. FIXME: Move all of this into a JSON
+ // object
+ //--------------------------------------------------------------------------
+ setLogin: function(newLogin)
+ {
+
+ setLogin(newLogin);
+ $rootScope.showBlog = newLogin.enableBlog;
+
+ },
+
+ //-------------------------------------------------------
+ // returns API version or none
+ //-------------------------------------------------------
+ getAPIversion: function()
+ {
+ debug("getAPIversion called");
+ var d = $q.defer();
+ var apiurl = loginData.apiurl + '/host/getVersion.json';
+ $http.get(apiurl)
+ .then(function(success)
+ {
+ if (success.data.version)
+ {
+ $rootScope.apiValid = true;
+ d.resolve(success.data.version);
+ }
+ else
+ {
+ $rootScope.apiValid = false;
+ d.reject("-1.-1.-1");
+ }
+ return (d.promise);
+
+ },
+ function(error)
+ {
+ debug("getAPIversion error handler " + JSON.stringify(error));
+ d.reject("-1.-1.-1");
+ $rootScope.apiValid = false;
+ return (d.promise);
+ });
+ return (d.promise);
+
+ },
+
+ displayBanner: function(mytype, mytext, myinterval, mytimer)
+ {
+ displayBanner(mytype, mytext, myinterval, mytimer);
+ },
+
+ isReCaptcha: function()
+ {
+ var d = $q.defer();
+
+ var myurl = loginData.url;
+ log("Checking if reCaptcha is enabled in ZM...");
+ $http.get(myurl)
+ .then(function(success)
+ {
+ if (success.data.search("g-recaptcha") != -1)
+ {
+ // recaptcha enable. zmNinja won't work
+ log("ZM has recaptcha enabled", "error");
+ displayBanner('error', ['Recaptcha must be disabled in Zoneminder', $rootScope.appName + ' will not work with recaptcha'], "", 8000);
+ d.resolve(true);
+ return (d.promise);
+
+ }
+ else
+ {
+ d.resolve(false);
+ log("ZM has recaptcha disabled - good");
+ return (d.promise);
+ }
+ });
+ return (d.promise);
+ },
+
+ //-----------------------------------------------------------------------------
+ // Grabs the computed auth key for streaming
+ // FIXME: Currently a hack - does a screen parse - convert to API based support
+ //-----------------------------------------------------------------------------
+
+ // need a mid as restricted users won't be able to get
+ // auth with just &watch
+ getAuthKey: function(mid, ck)
+ {
+ var d = $q.defer();
+
+ if (!mid)
+ {
+ log("Deferring auth key, as monitorId unknown");
+ d.resolve("");
+ return (d.promise);
+ }
+
+ // Skipping monitor number as I only need an auth key
+ // so no need to generate an image
+ var myurl = loginData.url + "/index.php?view=watch&mid=" + mid + "&connkey=" + ck;
+ debug("DataModel: Getting auth from " + myurl + " with mid=" + mid);
+ $http.get(myurl)
+ .then(function(success)
+ {
+ // console.log ("**** RESULT IS " + JSON.stringify(success));
+ // Look for auth=
+ var auth = success.data.match("auth=(.*?)&");
+ if (auth && (auth[1] != null))
+ {
+ log("DataModel: Extracted a stream authentication key of: " + auth[1]);
+ d.resolve("&auth=" + auth[1]);
+ }
+ else
+ {
+ log("DataModel: Did not find a stream auth key, looking for user=");
+ auth = success.data.match("user=(.*?)&");
+ if (auth && (auth[1] != null))
+ {
+ log("DataModel: Found simple stream auth mode (user=)");
+ d.resolve("&user=" + loginData.username + "&pass=" + loginData.password);
+ }
+ else
+ {
+ log("Data Model: Did not find any stream mode of auth");
+ d.resolve("");
+ }
+ return (d.promise);
+ }
+
+ },
+ function(error)
+ {
+ log("DataModel: Error resolving auth key " + JSON.stringify(error));
+ d.resolve("");
+ return (d.promise);
+ });
+ return (d.promise);
+
+ },
+
+ //-----------------------------------------------------------------------------
+ // This function returns the numdigits for padding capture images
+ //-----------------------------------------------------------------------------
+
+ getKeyConfigParams: function(forceReload)
+ {
+
+ var d = $q.defer();
+
+ if (forceReload == 1 || configParams.ZM_EVENT_IMAGE_DIGITS == '-1')
+ {
+ var apiurl = loginData.apiurl;
+ var myurl = apiurl + '/configs/viewByName/ZM_EVENT_IMAGE_DIGITS.json';
+ debug("Config URL for digits is:" + myurl);
+ $http.get(myurl)
+ .success(function(data)
+ {
+ log("ZM_EVENT_IMAGE_DIGITS is " + data.config.Value);
+ configParams.ZM_EVENT_IMAGE_DIGITS = data.config.Value;
+ d.resolve(configParams.ZM_EVENT_IMAGE_DIGITS);
+ return (d.promise);
+
+ })
+ .error(function(err)
+ {
+ log("Error retrieving ZM_EVENT_IMAGE_DIGITS" + JSON.stringify(err), "error");
+ log("Taking a guess, setting ZM_EVENT_IMAGE_DIGITS to 5");
+ // FIXME: take a plunge and keep it at 5?
+ configParams.ZM_EVENT_IMAGE_DIGITS = 5;
+ d.resolve(configParams.ZM_EVENT_IMAGE_DIGITS);
+ return (d.promise);
+ });
+ }
+ else
+ {
+ log("ZM_EVENT_IMAGE_DIGITS is already configured for " +
+ configParams.ZM_EVENT_IMAGE_DIGITS);
+ d.resolve(configParams.ZM_EVENT_IMAGE_DIGITS);
+ }
+ return (d.promise);
+
+ },
+
+ //--------------------------------------------------------------------------
+ // Useful to know what ZMS is using as its cgi-bin. If people misconfigure
+ // the setting in the app, they can check their logs
+ //--------------------------------------------------------------------------
+ getPathZms: function()
+ {
+ var d = $q.defer();
+ var apiurl = loginData.apiurl;
+ var myurl = apiurl + '/configs/viewByName/ZM_PATH_ZMS.json';
+ debug("Config URL for ZMS PATH is:" + myurl);
+ $http.get(myurl)
+ .success(function(data)
+ {
+ configParams.ZM_PATH_ZMS = data.config.Value;
+ d.resolve(configParams.ZM_PATH_ZMS);
+ return (d.promise);
+ })
+ .error(function(error)
+ {
+ log("Can't retrieving ZM_PATH_ZMS: " + JSON.stringify(error));
+ d.resolve("");
+ return (d.promise);
+ });
+ return (d.promise);
+
+ },
+ //--------------------------------------------------------------------------
+ // returns high or low BW mode
+ //--------------------------------------------------------------------------
+ getBandwidth: function()
+ {
+ return getBandwidth();
+ },
+
+ //-----------------------------------------------------------------------------
+ // This function returns a list of monitors
+ // if forceReload == 1 then it will force an HTTP API request to get a list of monitors
+ // if 0. then it will return back the previously loaded monitor list if one exists, else
+ // will issue a new HTTP API to get it
+
+ // I've wrapped this function in my own promise even though http returns a promise.
+ //-----------------------------------------------------------------------------
+ //
+
+ // returns a non promise version
+ // so if monitors is null, it will return null
+ // As of now, this is only used by EventServer.js to
+ // send the right list of monitors after registration
+ // token
+ getMonitorsNow: function()
+ {
+ return monitors;
+ },
+
+ getMonitors: function(forceReload)
+ {
+ //console.log("** Inside ZMData getMonitors with forceReload=" + forceReload);
+
+ $ionicLoading.show(
+ {
+ template: $translate.instant('kLoadingMonitors'),
+ animation: 'fade-in',
+ showBackdrop: true,
+ duration: zm.loadingTimeout,
+ maxWidth: 200,
+ showDelay: 0
+ });
+
+ var d = $q.defer();
+ if ((monitorsLoaded == 0) || (forceReload == 1)) // monitors are empty or force reload
+ {
+ //console.log("NVRDataModel: Invoking HTTP get to load monitors");
+ log((forceReload == 1) ? "getMonitors:Force reloading all monitors" : "getMonitors:Loading all monitors");
+ var apiurl = loginData.apiurl;
+ var myurl = apiurl + "/monitors.json";
+ //console.log ("API:"+myurl);
+ $http.get(myurl /*,{timeout:15000}*/ )
+ .success(function(data)
+ {
+ //console.log("HTTP success got " + JSON.stringify(data.monitors));
+ monitors = data.monitors;
+ monitors.sort(function(a, b)
+ {
+ return parseInt(a.Monitor.Sequence) - parseInt(b.Monitor.Sequence);
+ });
+ //console.log("promise resolved inside HTTP success");
+ monitorsLoaded = 1;
+
+ reloadMonitorDisplayStatus();
+
+ debug("Now trying to get multi-server data, if present");
+ $http.get(apiurl + "/servers.json")
+ .success(function(data)
+ {
+ // We found a server list API, so lets make sure
+ // we get the hostname as it will be needed for playback
+ log("multi server list loaded" + JSON.stringify(data));
+ multiservers = data.servers;
+
+ for (var i = 0; i < monitors.length; i++)
+ {
+
+ // make them all show for now
+ monitors[i].Monitor.listDisplay = 'show';
+ monitors[i].Monitor.isAlarmed = false;
+ monitors[i].Monitor.connKey = (Math.floor((Math.random() * 999999) + 1)).toString();
+
+ var serverFound = false;
+ for (var j = 0; j < multiservers.length; j++)
+ {
+ //console.log ("Comparing " + multiservers[j].Server.Id + " AND " + monitors[i].Monitor.ServerId);
+ if (multiservers[j].Server.Id == monitors[i].Monitor.ServerId)
+ {
+ //console.log ("Found match");
+ serverFound = true;
+ break;
+ }
+
+ }
+ if (serverFound)
+ {
+
+ debug("Monitor " + monitors[i].Monitor.Id + " has a recording server hostname of " + multiservers[j].Server.Hostname);
+
+ // Now here is the logic, I need to retrieve serverhostname,
+ // and slap on the host protocol and path. Meh.
+
+ var p = URI.parse(loginData.streamingurl);
+ var s = URI.parse(multiservers[j].Server.Hostname);
+
+ debug("recording server parsed is " + JSON.stringify(s));
+ debug("portal parsed is " + JSON.stringify(p));
+
+ var st = "";
+ var baseurl = "";
+
+ st += (s.scheme ? s.scheme : p.scheme) + "://"; // server scheme overrides
+
+ // if server doesn't have a protocol, what we want is in path
+ if (!s.host)
+ {
+ s.host = s.path;
+ s.path = undefined;
+ }
+
+ st += s.host;
+
+ if (p.port || s.port)
+ {
+ st += (s.port ? ":" + s.port : ":" + p.port);
+
+ }
+
+ baseurl = st;
+
+ st += (s.path ? s.path : p.path);
+
+ //console.log ("----------STREAMING URL PARSED AS " + st);
+
+ monitors[i].Monitor.streamingURL = st;
+ monitors[i].Monitor.baseURL = 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 ("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 );
+
+ }
+ else
+ {
+ //monitors[i].Monitor.listDisplay = 'show';
+ monitors[i].Monitor.isAlarmed = false;
+ monitors[i].Monitor.connKey = (Math.floor((Math.random() * 999999) + 1)).toString();
+ monitors[i].Monitor.streamingURL = loginData.streamingurl;
+ monitors[i].Monitor.baseURL = loginData.url;
+ monitors[i].Monitor.imageMode = (versionCompare($rootScope.apiVersion, "1.30") == -1) ? "path" : "fid";
+
+ // but now check if forced path
+ if (loginData.forceImageModePath)
+ {
+ debug("Overriding, setting image mode to true as you have requested force enable");
+ monitors[i].Monitor.imageMode = 'path';
+ }
+
+ debug("API " + $rootScope.apiVersion + ": Monitor " + monitors[i].Monitor.Id + " will use " + monitors[i].Monitor.imageMode + " for direct image access");
+ }
+ }
+ // now get packery hide if applicable
+ reloadMonitorDisplayStatus();
+ d.resolve(monitors);
+ })
+ .error(function(err)
+ {
+ log("multi server list loading error");
+ multiservers = [];
+
+ for (var i = 0; i < monitors.length; i++)
+ {
+ //monitors[i].Monitor.listDisplay = 'show';
+ monitors[i].Monitor.isAlarmed = false;
+ monitors[i].Monitor.connKey = (Math.floor((Math.random() * 999999) + 1)).toString();
+ monitors[i].Monitor.streamingURL = loginData.streamingurl;
+ monitors[i].Monitor.baseURL = loginData.url;
+ 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");
+
+ }
+ d.resolve(monitors);
+
+ });
+
+ $ionicLoading.hide();
+ log("Monitor load was successful, loaded " + monitors.length + " monitors");
+
+ })
+ .error(function(err)
+ {
+ //console.log("HTTP Error " + err);
+ log("Monitor load failed " + JSON.stringify(err), "error");
+ // To keep it simple for now, I'm translating an error
+ // to imply no monitors could be loaded. FIXME: conver to proper error
+ monitors = [];
+ //console.log("promise resolved inside HTTP fail");
+ displayBanner('error', ['error retrieving monitor list', 'please try again']);
+ d.resolve(monitors);
+ $ionicLoading.hide();
+ monitorsLoaded = 0;
+ });
+ return d.promise;
+
+ }
+ else // monitors are loaded
+ {
+ //console.log("Returning pre-loaded list of " + monitors.length + " monitors");
+ log("Returning pre-loaded list of " + monitors.length + " monitors");
+ d.resolve(monitors);
+ //console.log ("Returning"+JSON.stringify(monitors));
+ $ionicLoading.hide();
+ return d.promise;
+ }
+
+ },
+
+ //-----------------------------------------------------------------------------
+ //
+ //-----------------------------------------------------------------------------
+ setMonitors: function(mon)
+ {
+ //console.log("ZMData setMonitors called with " + mon.length + " monitors");
+ monitors = mon;
+ },
+
+ processFastLogin: function()
+ {
+ var d = $q.defer();
+ if (1)
+ {
+ d.reject("not implemented");
+ return d.promise;
+ }
+ console.log("inside processFastLogin");
+ if (!loginData.fastLogin)
+ {
+ console.log("Fast login not set");
+ d.reject("fast login not enabled");
+ debug("fast login not enabled");
+ return d.promise;
+
+ }
+ else //fastlogin is on
+ {
+ localforage.getItem("lastLogin")
+ .then(function(succ)
+ {
+ console.log("fast login DB found");
+ var dt = moment(succ);
+
+ if (dt.isValid())
+ {
+ debug("Got last login as " + dt.toString());
+ if (moment.duration(moment().diff(dt)).asHours() >= 2)
+ {
+ d.reject("duration since last login >=2hrs, need to relogin");
+ return d.promise;
+ }
+ else
+ {
+ d.resolve("fast login is valid, less then 2 hrs");
+ return d.promise;
+ }
+ }
+ else
+ {
+ console.log("Invalid date found");
+ d.reject("last-login invalid");
+ return d.promise;
+
+ }
+ },
+ function(e)
+ {
+ console.log("fastlogin DB not found");
+ d.reject("last-login not found, fastlogin rejected");
+ return d.promise;
+ });
+
+ }
+ return d.promise;
+ },
+
+ // returns if this mid is hidden or not
+ isNotHidden: function(mid)
+ {
+ var notHidden = true;
+ for (var i = 0; i < monitors.length; i++)
+ {
+ if (monitors[i].Monitor.Id == mid)
+ {
+ notHidden = (monitors[i].Monitor.listDisplay == 'show') ? true : false;
+ break;
+ }
+
+ }
+ return notHidden;
+
+ },
+
+ getLocalTimeZoneNow: function()
+ {
+ return moment.tz.guess();
+ },
+ //returns TZ value immediately (sync)
+
+ getTimeZoneNow: function()
+ {
+ // console.log ("getTimeZoneNow: " + tz ? tz : moment.tz.guess());
+ return tz ? tz : moment.tz.guess();
+ },
+
+ // returns server timezone, failing which local timezone
+ // always resolves true
+
+ isTzSupported: function()
+ {
+ return isTzSupported;
+ },
+
+ getTimeZone: function(isForce)
+ {
+
+ var d = $q.defer();
+ if (!tz || isForce)
+ {
+
+ log("First invocation of TimeZone, asking server");
+ var apiurl = loginData.apiurl + '/host/getTimeZone.json';
+ $http.get(apiurl)
+ .then(function(success)
+ {
+ tz = success.data.tz;
+ d.resolve(tz);
+ debug("Timezone API response is:" + success.data.tz);
+ if (success.data.tz !== undefined)
+ isTzSupported = true;
+ else
+ isTzSupported = false;
+ $rootScope.$emit('tz-updated');
+ return (d.promise);
+
+ },
+ function(error)
+ {
+ tz = moment.tz.guess();
+ debug("Timezone API error handler, guessing local:" + tz);
+ d.resolve(tz);
+ isTzSupported = false;
+ return (d.promise);
+ });
+
+ }
+ else
+ {
+ d.resolve(tz);
+ return d.promise;
+ }
+
+ return d.promise;
+ },
+
+ //-----------------------------------------------------------------------------
+ // When I display events in the event controller, this is the first function I call
+ // This returns the total number of pages
+ // I then proceed to display pages in reverse order to display the latest events first
+ // I also reverse sort them in NVRDataModel to sort by date
+ // All this effort because the ZM APIs return events in sorted order, oldest first. Yeesh.
+ //-----------------------------------------------------------------------------
+
+ getEventsPages: function(monitorId, startTime, endTime)
+ {
+ //console.log("********** INSIDE EVENTS PAGES ");
+ var apiurl = loginData.apiurl;
+
+ var myurl = apiurl + "/events/index";
+ if (monitorId != 0)
+ myurl = myurl + "/MonitorId:" + monitorId;
+ if (startTime)
+ myurl = myurl + "/StartTime >=:" + startTime;
+ if (endTime)
+ myurl = myurl + "/EndTime <=:" + endTime;
+
+ myurl = myurl + "/AlarmFrames >=:" + (loginData.enableAlarmCount ? loginData.minAlarmCount : 0);
+
+ myurl = myurl + ".json";
+ //console.log (">>>>>Constructed URL " + myurl);
+
+ $ionicLoading.show(
+ {
+ template: $translate.instant('kCalcEventSize') + '...',
+ animation: 'fade-in',
+ showBackdrop: true,
+ duration: zm.loadingTimeout,
+ maxWidth: 200,
+ showDelay: 0
+ });
+
+ //var myurl = (monitorId == 0) ? apiurl + "/events.json?page=1" : apiurl + "/events/index/MonitorId:" + monitorId + ".json?page=1";
+ var d = $q.defer();
+ $http.get(myurl)
+ .success(function(data)
+ {
+ $ionicLoading.hide();
+ //console.log ("**** EVENTS PAGES I GOT "+JSON.stringify(data));
+ //console.log("**** PAGE COUNT IS " + data.pagination.pageCount);
+ d.resolve(data.pagination);
+ return d.promise;
+ })
+ .error(function(error)
+ {
+ $ionicLoading.hide();
+ // console.log("*** ERROR GETTING TOTAL PAGES ***");
+ log("Error retrieving page count of events " + JSON.stringify(error), "error");
+ displayBanner('error', ['error retrieving event page count', 'please try again']);
+
+ d.reject(error);
+ return d.promise;
+ });
+ return d.promise;
+
+ },
+
+ //-----------------------------------------------------------------------------
+ // This function returns events for specific monitor or all monitors
+ // You get here by tapping on events in the monitor screen or from
+ // the menu events option
+ // monitorId == 0 means all monitors (ZM starts from 1)
+ //-----------------------------------------------------------------------------
+
+ getEvents: function(monitorId, pageId, loadingStr, startTime, endTime)
+ {
+
+ //console.log("ZMData getEvents called with ID=" + monitorId + "and Page=" + pageId);
+
+ if (!loadingStr)
+ {
+ loadingStr = $translate.instant('kLoadingEvents')+"...";
+ }
+ //if (loadingStr) loa
+
+ if (loadingStr != 'none')
+ {
+ $ionicLoading.show(
+ {
+ template: loadingStr,
+ animation: 'fade-in',
+ showBackdrop: true,
+ maxWidth: 200,
+ showDelay: 0,
+ duration: zm.loadingTimeout, //specifically for Android - http seems to get stuck at times
+ });
+ }
+
+ var d = $q.defer();
+ var myevents = [];
+ var apiurl = loginData.apiurl;
+
+ var myurl = apiurl + "/events/index";
+ if (monitorId != 0)
+ myurl = myurl + "/MonitorId:" + monitorId;
+ if (startTime)
+ myurl = myurl + "/StartTime >=:" + startTime;
+ if (endTime)
+ myurl = myurl + "/EndTime <=:" + endTime;
+
+ myurl = myurl + "/AlarmFrames >=:" + (loginData.enableAlarmCount ? loginData.minAlarmCount : 0);
+ myurl = myurl + ".json";
+
+ if (pageId)
+ {
+ myurl = myurl + "?page=" + pageId;
+ }
+ else
+ {
+ //console.log("**** PAGE WAS " + pageId);
+ }
+
+ // Simulated data
+
+ // myurl = "https://api.myjson.com/bins/4jx44.json";
+
+ //console.log (">>>>>Constructed URL " + myurl);
+
+ $http.get(myurl /*,{timeout:15000}*/ )
+ .success(function(data)
+ {
+ if (loadingStr != 'none') $ionicLoading.hide();
+ //myevents = data.events;
+ myevents = data.events.reverse();
+ if (monitorId == 0)
+ {
+ oldevents = myevents;
+ }
+ //console.log (JSON.stringify(data));
+ // console.log("DataModel Returning " + myevents.length + "events for page" + pageId);
+ d.resolve(myevents);
+ return d.promise;
+
+ })
+ .error(function(err)
+ {
+ if (loadingStr != 'none') $ionicLoading.hide();
+ displayBanner('error', ['error retrieving event list', 'please try again']);
+ //console.log("HTTP Events error " + err);
+ log("Error fetching events for page " + pageId + " Err: " + JSON.stringify(err), "error");
+ // I need to reject this as I have infinite scrolling
+ // implemented in EventCtrl.js --> and if it does not know
+ // it got an error going to the next page, it will get into
+ // an infinite loop as we are at the bottom of the list always
+
+ d.reject(myevents);
+
+ // FIXME: Check what pagination does to this logic
+ if (monitorId == 0)
+ {
+ oldevents = [];
+ }
+ return d.promise;
+ });
+ return d.promise;
+ },
+
+ //-----------------------------------------------------------------------------
+ //
+ //-----------------------------------------------------------------------------
+ getMontageSize: function()
+ {
+ return loginData.montageSize;
+ },
+
+ //-----------------------------------------------------------------------------
+ //
+ //-----------------------------------------------------------------------------
+ setMontageSize: function(montage)
+ {
+ loginData.montageSize = montage;
+ },
+
+ //-----------------------------------------------------------------------------
+ //
+ //-----------------------------------------------------------------------------
+ getMonitorsLoaded: function()
+ {
+ // console.log("**** Inside promise function ");
+ var deferred = $q.defer();
+ if (monitorsLoaded != 0)
+ {
+ deferred.resolve(monitorsLoaded);
+ }
+
+ return deferred.promise;
+ },
+
+ //-----------------------------------------------------------------------------
+ //
+ //-----------------------------------------------------------------------------
+ setMonitorsLoaded: function(loaded)
+ {
+ // console.log("ZMData.setMonitorsLoaded=" + loaded);
+ monitorsLoaded = loaded;
+ },
+
+ //-----------------------------------------------------------------------------
+ // returns the next monitor ID in the list
+ // used for swipe next
+ //-----------------------------------------------------------------------------
+ getNextMonitor: function(monitorId, direction)
+ {
+ var id = parseInt(monitorId);
+ var foundIndex = -1;
+ for (var i = 0; i < monitors.length; i++)
+ {
+ if (parseInt(monitors[i].Monitor.Id) == id)
+ {
+ foundIndex = i;
+ break;
+ }
+ }
+ if (foundIndex != -1)
+ {
+ foundIndex = foundIndex + direction;
+ // wrap around if needed
+ if (foundIndex < 0) foundIndex = monitors.length - 1;
+ if (foundIndex >= monitors.length) foundIndex = 0;
+ return (monitors[foundIndex].Monitor.Id);
+ }
+ else
+ {
+ log("getNextMonitor could not find monitor " + monitorId);
+ return (monitorId);
+ }
+
+ },
+
+ //-----------------------------------------------------------------------------
+ // Given a monitor Id it returns the monitor name
+ // FIXME: Can I do a better job with associative arrays?
+ //-----------------------------------------------------------------------------
+ getMonitorName: function(id)
+ {
+ var idnum = parseInt(id);
+ for (var i = 0; i < monitors.length; i++)
+ {
+ if (parseInt(monitors[i].Monitor.Id) == idnum)
+ {
+ // console.log ("Matched, exiting getMonitorname");
+ return monitors[i].Monitor.Name;
+ }
+
+ }
+ return "(Unknown)";
+ },
+
+ getMonitorObject: function(id)
+ {
+ var idnum = parseInt(id);
+ for (var i = 0; i < monitors.length; i++)
+ {
+ if (parseInt(monitors[i].Monitor.Id) == idnum)
+ {
+ // console.log ("Matched, exiting getMonitorname");
+ return monitors[i];
+ }
+
+ }
+ return "(Unknown)";
+ },
+
+ getImageMode: function(id)
+ {
+ var idnum = parseInt(id);
+ for (var i = 0; i < monitors.length; i++)
+ {
+ if (parseInt(monitors[i].Monitor.Id) == idnum)
+ {
+ // console.log ("Matched, exiting getMonitorname");
+ return monitors[i].Monitor.imageMode;
+ }
+
+ }
+ return "(Unknown)";
+ },
+
+ getStreamingURL: function(id)
+ {
+ var idnum = parseInt(id);
+ for (var i = 0; i < monitors.length; i++)
+ {
+ if (parseInt(monitors[i].Monitor.Id) == idnum)
+ {
+ // console.log ("Matched, exiting getMonitorname");
+ return monitors[i].Monitor.streamingURL;
+ }
+
+ }
+ return "(Unknown)";
+ },
+
+ getBaseURL: function(id)
+ {
+ var idnum = parseInt(id);
+ for (var i = 0; i < monitors.length; i++)
+ {
+ if (parseInt(monitors[i].Monitor.Id) == idnum)
+ {
+ // console.log ("Matched, exiting getMonitorname");
+ return monitors[i].Monitor.baseURL;
+ }
+
+ }
+ return "(Unknown)";
+ },
+
+ };
+ }
+]);