/* jshint -W041 */ /* jslint browser: true*/ /* global cordova,StatusBar,angular,console, URI */ // 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('ZMDataModel', ['$http', '$q', '$ionicLoading', '$ionicBackdrop', '$fileLogger', 'zm','$rootScope','$ionicContentBanner', '$timeout','$cordovaPinDialog', '$ionicPopup', '$localstorage', '$state', '$ionicNativeTransitions', function ($http, $q, $ionicLoading, $ionicBackdrop,$fileLogger, zm, $rootScope,$ionicContentBanner, $timeout, $cordovaPinDialog, $ionicPopup, $localstorage, $state, $ionicNativeTransitions) { var zmAppVersion="unknown"; var isBackground = false; var justResumed = false; var monitorsLoaded = 0; //var montageSize = 3; var monitors = []; var multiservers = []; var oldevents = []; var serverGroupList={}; 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", // single streaming quality in % '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 'enableLogs':true, 'enableDebug':false, // if enabled with log messages with "debug" 'usePin':false, 'pinCode':'', 'canSwipeMonitors':true, 'persistMontageOrder':false, 'onTapScreen':'events', 'enableh264':true, 'gapless':false, 'montageOrder':'', 'montageHiddenOrder':'', 'montageArraySize':'0', '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':'', 'packerySizes':'', 'timelineModalGraphType':'all', 'resumeDelay':300, }; var defaultLoginData = angular.copy(loginData); var configParams = { 'ZM_EVENT_IMAGE_DIGITS':'-1', 'ZM_PATH_ZMS':'' }; // 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; } //-------------------------------------------------------------------------- // uses fileLogger to write logs to file for later investigation //-------------------------------------------------------------------------- function zmLog(val,logtype) { if (loginData.enableLogs) $fileLogger.log(logtype, val); } function setLogin(newLogin) { loginData = angular.copy(newLogin); serverGroupList[loginData.serverName]=angular.copy(loginData); $localstorage.setObject("serverGroupList", serverGroupList); $localstorage.set("defaultServerName",loginData.serverName); // console.log ("SAVING " + loginData.serverName); // console.log ("DATA IS " + JSON.stringify(loginData)); } // separate out a debug so we don't do this if comparison for normal logs function zmDebug(val) { if (loginData.enableDebug && loginData.enableLogs) $fileLogger.debug(val); } //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 //------------------------------------------------------------- zmLog: function (val,type) { var logtype = 'info'; if (type != undefined) logtype = type ; zmLog(val,logtype); }, zmDebug: function (val) { zmDebug(val); }, // 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=="") { zmLog("Reachable: No server name configured, likely first use?"); d.reject ("No servers"); return d.promise; } var chainURLs = []; var savedLoginData = angular.copy(loginData); //zmLog ("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 { zmLog ("Adding to chain stack: " + tLd.serverName + ">"+tLd.url); chainURLs.push ({url:tLd.url+"/index.php", server:tLd.serverName}); zmLog ("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 zmLog ("Looks like a server object was deleted, but is still in fallback"); keepBuilding = false; } } else { zmLog ("reached end of chain loop"); } } else { zmLog ("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); zmLog ("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: 'trying ' + urls[0].server}); zmLog ("Reachability test.." + urls[0].url); return $http.get(urls[0].url).then(function () { zmLog ("Success: reachability on "+ urls[0].url); $ionicLoading.hide(); return urls[0]; }, function(err) { zmLog ("Failed reachability on "+ urls[0].url+ " with error " + JSON.stringify(err)); return findFirstReachableUrl(urls.slice(1)); }); } else { $ionicLoading.hide(); return $q.reject("No reachable URL"); } } return d.promise; }, init: function () { // console.log("****** DATAMODEL INIT SERVICE CALLED ********"); zmLog("ZMData init: checking for stored variables & setting up log file"); serverGroupList = $localstorage.getObject("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