summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.xml2
-rw-r--r--www/css/style.css4
-rw-r--r--www/external/origjs/packery.pkgd.js4
-rw-r--r--www/external/packery.pkgd.min.js2
-rw-r--r--www/js/DataModel.js9
-rw-r--r--www/js/MontageCtrl.js232
-rw-r--r--www/lang/locale-en.json4
-rw-r--r--www/templates/montage.html45
8 files changed, 262 insertions, 40 deletions
diff --git a/config.xml b/config.xml
index c44bea3c..f3e8568b 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.31" 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.32" 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/css/style.css b/www/css/style.css
index 1ff3ce8c..2b3fda9b 100644
--- a/www/css/style.css
+++ b/www/css/style.css
@@ -26,6 +26,10 @@
background-size:contain;
}
+.icon-save:before {
+ font-family: "fontawesome";
+ content: "\f0c7";
+}
diff --git a/www/external/origjs/packery.pkgd.js b/www/external/origjs/packery.pkgd.js
index 7576576b..fa4e8308 100644
--- a/www/external/origjs/packery.pkgd.js
+++ b/www/external/origjs/packery.pkgd.js
@@ -3088,10 +3088,10 @@ proto._bindFitEvents = function( item ) {
// -------------------------- resize -------------------------- //
// debounced, layout on resize
-proto.resize = function() {
+proto.resize = function(force) {
// don't trigger if size did not change
// or if resize was unbound. See #285, outlayer#9
- if ( !this.isResizeBound || !this.needsResizeLayout() ) {
+ if ( !force && (!this.isResizeBound || !this.needsResizeLayout()) ) {
return;
}
diff --git a/www/external/packery.pkgd.min.js b/www/external/packery.pkgd.min.js
index f1ba5e03..0a072330 100644
--- a/www/external/packery.pkgd.min.js
+++ b/www/external/packery.pkgd.min.js
@@ -227,7 +227,7 @@ else{var o=this._getElementOffset(t)
e=new i({x:this._getOption("originLeft")?o.left:o.right,y:this._getOption("originTop")?o.top:o.bottom})}this._setRectSize(t,e),this.packer.placed(e),this._setMaxXY(e)},u.sortItemsByPosition=function(){var t=this._getOption("horizontal")?r:s
this.items.sort(t)},u.fit=function(t,e,i){var n=this.getItem(t)
n&&(this.stamp(n.element),n.enablePlacing(),this.updateShiftTargets(n),e=void 0===e?n.rect.x:e,i=void 0===i?n.rect.y:i,this.shift(n,e,i),this._bindFitEvents(n),n.moveTo(n.rect.x,n.rect.y),this.shiftLayout(),this.unstamp(n.element),this.sortItemsByPosition(),n.disablePlacing())},u._bindFitEvents=function(t){function e(){n++,2==n&&i.dispatchEvent("fitComplete",null,[t])}var i=this,n=0
-t.once("layout",e),this.once("layoutComplete",e)},u.resize=function(){this.isResizeBound&&this.needsResizeLayout()&&(this.options.shiftPercentResize?this.resizeShiftPercentLayout():this.layout())},u.needsResizeLayout=function(){var e=t(this.element),i=this._getOption("horizontal")?"innerHeight":"innerWidth"
+t.once("layout",e),this.once("layoutComplete",e)},u.resize=function(t){(t||this.isResizeBound&&this.needsResizeLayout())&&(this.options.shiftPercentResize?this.resizeShiftPercentLayout():this.layout())},u.needsResizeLayout=function(){var e=t(this.element),i=this._getOption("horizontal")?"innerHeight":"innerWidth"
return e[i]!=this.size[i]},u.resizeShiftPercentLayout=function(){var e=this._getItemsForLayout(this.items),i=this._getOption("horizontal"),n=i?"y":"x",o=i?"height":"width",s=i?"rowHeight":"columnWidth",r=i?"innerHeight":"innerWidth",a=this[s]
if(a=a&&a+this.gutter){this._getMeasurements()
var h=this[s]+this.gutter
diff --git a/www/js/DataModel.js b/www/js/DataModel.js
index 5a837039..4c8be1a7 100644
--- a/www/js/DataModel.js
+++ b/www/js/DataModel.js
@@ -121,6 +121,7 @@ angular.module('zmApp.controllers')
'enableBlog': true,
'use24hr': false,
'packeryPositions': '',
+ 'packeryPositionsArray': {},
'EHpackeryPositions': '',
'packerySizes': '',
'timelineModalGraphType': 'all',
@@ -755,6 +756,14 @@ angular.module('zmApp.controllers')
}
+ 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");
diff --git a/www/js/MontageCtrl.js b/www/js/MontageCtrl.js
index 294073f5..ffb265cf 100644
--- a/www/js/MontageCtrl.js
+++ b/www/js/MontageCtrl.js
@@ -4,7 +4,7 @@
/* global cordova,StatusBar,angular,console,ionic,Packery, Draggabilly, imagesLoaded, ConnectSDK, moment */
angular.module('zmApp.controllers')
- .controller('zmApp.MontageCtrl', ['$scope', '$rootScope', 'NVRDataModel', 'message', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$ionicPopup', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$ionicPlatform', 'zm', '$ionicPopover', '$controller', 'imageLoadingDataShare', '$window', '$localstorage', '$translate', function($scope, $rootScope, NVRDataModel, message, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $ionicPopup, $stateParams, $ionicHistory, $ionicScrollDelegate, $ionicPlatform, zm, $ionicPopover, $controller, imageLoadingDataShare, $window, $localstorage, $translate)
+ .controller('zmApp.MontageCtrl', ['$scope', '$rootScope', 'NVRDataModel', 'message', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$ionicPopup', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$ionicPlatform', 'zm', '$ionicPopover', '$controller', 'imageLoadingDataShare', '$window', '$localstorage', '$translate', 'SecuredPopups', function($scope, $rootScope, NVRDataModel, message, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $ionicPopup, $stateParams, $ionicHistory, $ionicScrollDelegate, $ionicPlatform, zm, $ionicPopover, $controller, imageLoadingDataShare, $window, $localstorage, $translate, SecuredPopups)
{
//---------------------------------------------------------------------
@@ -175,7 +175,7 @@ angular.module('zmApp.controllers')
columnWidth: '.grid-sizer',
gutter: 0,
initLayout: layouttype,
- shiftPercentResize:true
+ shiftPercentResize: true
});
@@ -186,9 +186,12 @@ angular.module('zmApp.controllers')
NVRDataModel.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) pckry.layout();},100);
-
+
+ $timeout(function()
+ {
+ if (layouttype) pckry.layout();
+ }, 100);
+
progressCalled = true;
// if (layouttype) $timeout (function(){layout(pckry);},100);
@@ -207,7 +210,7 @@ angular.module('zmApp.controllers')
if (!progressCalled)
{
NVRDataModel.log("*** PROGRESS WAS NOT CALLED");
- // pckry.reloadItems();
+ // pckry.reloadItems();
}
$timeout(function()
@@ -250,14 +253,15 @@ angular.module('zmApp.controllers')
{
//NVRDataModel.log("Force calling resize");
///pckry.reloadItems();
- pckry.initShiftLayout(positions,"data-item-id");
+ pckry.initShiftLayout(positions, "data-item-id");
// now do a jiggle
$timeout(function()
{
- pckry.shiftLayout();
+ NVRDataModel.debug("doing the jiggle and dance...");
+ pckry.resize(true);
}, 300);
- }, 20);
+ }, 100);
//pckry.onresize();
@@ -919,28 +923,189 @@ angular.module('zmApp.controllers')
function orientationChanged()
{
- /*NVRDataModel.debug("Detected orientation change, redoing packery resize");
-
- $timeout(function () {
- var positions = pckry.getShiftPositions('data-item-id');
- pckry.initShiftLayout(positions,'data-item-id');
- pckry.shiftLayout();
- }, zm.packeryTimer);*/
-
- /* var positions = pckry.getShiftPositions('data-item-id');
- $timeout(function () {
- NVRDataModel.log("init shift layout");
- pckry.initShiftLayout(positions,"data-item-id");
- $ionicScrollDelegate.$getByHandle("montage-delegate").scrollTop();
- }, 20);*/
-
- //console.log ("POSITIONS MAP " + JSON.stringify(positions));
- // var ld = NVRDataModel.getLogin();
- // ld.packeryPositions = JSON.stringify(positions);
- //console.log ("Saving " + ld.packeryPositions);
- // NVRDataModel.setLogin(ld);
+
}
+
+ // remove a saved montage profile
+ $scope.deleteMontageProfile = function()
+ {
+ var posArray;
+
+ try
+ {
+ posArray = NVRDataModel.getLogin().packeryPositionsArray;
+ //console.log ("PA="+JSON.stringify(posArray));
+
+ }
+ catch (e)
+ {
+ NVRDataModel.debug("error parsing packery array positions");
+ posArray = {};
+ }
+
+ //console.log ("posArray="+JSON.stringify(posArray));
+
+ $scope.listdata = [];
+ for (var key in posArray)
+ {
+ if (posArray.hasOwnProperty(key))
+ {
+ $scope.listdata.push(key);
+ }
+ }
+
+ if (!$scope.listdata.length)
+ {
+
+ $rootScope.zmPopup = $ionicPopup.alert(
+ {
+ title: $translate.instant('kError'),
+ template: $translate.instant('kMontageNoSavedProfiles')
+ });
+ return;
+ }
+
+ $scope.data = {
+ 'selectedVal': ''
+ };
+
+ $rootScope.zmPopup = SecuredPopups.show('confirm',
+ {
+ template: '<ion-list> ' +
+ ' <ion-radio-fix ng-repeat="item in listdata" ng-value="item" ng-model="data.selectedVal"> ' +
+ ' {{item}} ' +
+ ' </ion-item> ' +
+ '</ion-list> ',
+
+ title: $translate.instant('kSelect'),
+ scope: $scope,
+
+ }).then(function(res)
+ {
+ NVRDataModel.debug("Deleting profile: " + $scope.data.selectedVal);
+ delete posArray[$scope.data.selectedVal];
+ var ld = NVRDataModel.getLogin();
+ ld.packeryPositionsArray = posArray;
+ NVRDataModel.setLogin(ld);
+
+ });
+
+ };
+
+ // switch to another montage profile
+ $scope.switchMontageProfile = function()
+ {
+ var posArray;
+
+ try
+ {
+ posArray = NVRDataModel.getLogin().packeryPositionsArray;
+ //console.log ("PA="+JSON.stringify(posArray));
+
+ }
+ catch (e)
+ {
+ NVRDataModel.debug("error parsing packery array positions");
+ posArray = {};
+ }
+
+ //console.log ("posArray="+JSON.stringify(posArray));
+
+ $scope.listdata = [];
+ for (var key in posArray)
+ {
+ if (posArray.hasOwnProperty(key))
+ {
+ $scope.listdata.push(key);
+ }
+ }
+
+ if (!$scope.listdata.length)
+ {
+
+ $rootScope.zmPopup = $ionicPopup.alert(
+ {
+ title: $translate.instant('kError'),
+ template: $translate.instant('kMontageNoSavedProfiles')
+ });
+ return;
+ }
+
+ $scope.data = {
+ 'selectedVal': ''
+ };
+
+ $rootScope.zmPopup = SecuredPopups.show('confirm',
+ {
+ template: '<ion-list> ' +
+ ' <ion-radio-fix ng-repeat="item in listdata" ng-value="item" ng-model="data.selectedVal"> ' +
+ ' {{item}} ' +
+ ' </ion-item> ' +
+ '</ion-list> ',
+
+ title: $translate.instant('kSelect'),
+ scope: $scope,
+
+ }).then(function(res)
+ {
+ if (res)
+ {
+ //console.log ("SELECTED " + $scope.data.selectedVal);
+ var ld = NVRDataModel.getLogin();
+ //console.log ("OLD POS="+ld.packeryPositions);
+ ld.packeryPositions = ld.packeryPositionsArray[$scope.data.selectedVal];
+ //console.log ("NEW POS="+ld.packeryPositions);
+ NVRDataModel.setLogin(ld);
+ NVRDataModel.reloadMonitorDisplayStatus();
+ draggies.forEach(function(drag)
+ {
+ drag.destroy();
+ });
+ draggies = [];
+ pckry.destroy();
+ initPackery();
+ //pckry.reloadItems();
+ }
+
+ });
+
+ };
+
+ // save current configuration into a profile
+ $scope.saveMontageProfile = function()
+ {
+ $scope.data = {
+ montageName: ""
+ };
+ $rootScope.zmPopup = SecuredPopups.show('confirm',
+ {
+ title: $translate.instant('kMontageSave'),
+ template: "<input autocapitalize='none' autocomplete='off' autocorrect='off' type='text' ng-model='data.montageName'>",
+ subTitle: $translate.instant('kMontageSaveSubtitle'),
+ scope: $scope,
+
+ }).then(function(res)
+ {
+ console.log(res);
+ if (res) // ok
+ {
+
+ var ld = NVRDataModel.getLogin();
+
+ if ($scope.data.montageName != '')
+ {
+ var pos = JSON.stringify(pckry.getShiftPositions('data-item-id'));
+ ld.packeryPositionsArray[$scope.data.montageName] = pos;
+ NVRDataModel.debug("Saving " + $scope.data.montageName + " with:" + pos);
+ NVRDataModel.setLogin(ld);
+ }
+
+ }
+ });
+
+ };
+
$scope.toggleSizeButtons = function()
{
@@ -1196,10 +1361,15 @@ angular.module('zmApp.controllers')
ld.packeryPositions = JSON.stringify(positions);
//console.log ("Saving " + ld.packeryPositions);
NVRDataModel.setLogin(ld);
+
+ $timeout(function()
+ {
+ NVRDataModel.debug("doing the jiggle and dance...");
+ pckry.resize(true);
+ }, 300);
+
// $scope.slider.monsize = 2;
});
- //layout(pckry);
-
pckry.layout();
}, 20);
diff --git a/www/lang/locale-en.json b/www/lang/locale-en.json
index 951edb38..33ecc8a8 100644
--- a/www/lang/locale-en.json
+++ b/www/lang/locale-en.json
@@ -203,6 +203,9 @@
"kMonitors" :"Monitors",
"kMontage" :"Montage",
"kMontageImageScale" :"Montage image scale",
+ "kMontageNoSavedProfiles" :"No saved montage profiles",
+ "kMontageSave" :"Save Montage Profile",
+ "kMontageSaveSubtitle" :"please enter a profile name to save current settings",
"kMonth" :"Month",
"kMore" :"more",
"kNeedToKnow" :"I need to know your ZoneMinder login and path details to get started",
@@ -284,6 +287,7 @@
"kSearch" :"search",
"kSearchCancelled" :"search cancelled",
"kSec" :"sec",
+ "kSelect" :"Please Select",
"kSelectFallback" :"Select fallback",
"kSelectLanguage" :"Select Language",
"kSelectRunState" :"Select run state",
diff --git a/www/templates/montage.html b/www/templates/montage.html
index ceab34ae..3bb62104 100644
--- a/www/templates/montage.html
+++ b/www/templates/montage.html
@@ -3,7 +3,7 @@
<button class="button button-icon button-clear ion-navicon" ng-click="openMenu()"></button>
<button class="button button-icon button-clear ion-eye" ng-click="hideUnhide();">&nbsp;
</button>
- <button class="button button-icon button-clear ion-arrow-resize" ng-click="toggleSizeButtons();">&nbsp;
+ <button class="button button-icon button-clear ion-android-more-vertical" ng-click="toggleSizeButtons();">&nbsp;
</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>
@@ -23,14 +23,49 @@
</button>
</ion-nav-buttons>
<ion-content scroll-sista ng-cloak has-bouncing="false" style="background-color:#444444" delegate-handle="montage-delegate" overflow-scroll="false">
- <span ng-if="!minimal && showSizeButtons">
+ <div ng-if="!minimal && showSizeButtons" >
<!-- this is header -->
+ <br/>
+ <div id="flyoutmenu" style="float:left">
+ <ul>
+ <li>
+ <a href="" ng-click="sliderChanged(1)"> <i class="ion-plus-circled"></i></a>
+ </li>
+ <li>
+ <a href="" ng-click="sliderChanged(-1)"> <i class="ion-minus-circled"></i></a>
+ </li>
+ </ul>
+ </div>
+
+ <div id="flyoutmenu" style="float:right">
+ <ul>
+ <li>
+ <a href="" ng-click="switchMontageProfile()"> <i class="ion-navicon-round"></i></a>
+ </li>
+
+ <li>
+ <a href="" ng-click="saveMontageProfile()"> <i class="ion-heart"></i></a>
+ </li>
+
+ <li>
+ <a href="" ng-click="deleteMontageProfile()"> <i class="ion-trash-a"></i></a>
+ </li>
+
+
+ </ul>
+ </div>
+ <div style="clear: both;"></div>
+
+ <!-- <span ng-click="sliderChanged(1)" style="float:right;margin-top:0px;padding-top:8px;background-color:#5c6767;color:#fff;font-size:25px;opacity:1;width:40px;height:36px;border-radius: 0px 0px 0px 5px;">&nbsp;<i class="ion-plus-circled">&nbsp;</i></span>
+
- <span ng-click="sliderChanged(1)" style="float:right;margin-top:0px;padding-top:8px;background-color:#5c6767;color:#fff;font-size:25px;opacity:1;width:40px;height:36px;border-radius: 0px 0px 0px 5px;">&nbsp;<i class="ion-plus-circled">&nbsp;</i></span>
<span ng-click="sliderChanged(-1)" style="float:left;margin-top:0px;padding-top:8px;background-color:#5c6767;color:#fff;font-size:22px;opacity:1;width:40px;height:36px;border-radius: 0px 0px 5px 0px;">&nbsp;<i class="ion-minus-circled">&nbsp;</i></span>
+
+ <span ng-click="" style="left:50%;margin-top:0px;padding-top:8px;background-color:#5c6767;color:#fff;font-size:11px;opacity:1;width:40px;height:36px;border-radius: 0px 0px 0px 5px;">&nbsp;<i class="ion-plus-circled">&nbsp;hello</i></span>
+ -->
+
<br/>
- <br/>
- </span>
+ </div>
<!-- now lets draw the montage windows -->
<div class="grid" id="mygrid">
<div class="grid-sizer grid-item-10"></div>