summaryrefslogtreecommitdiff
path: root/www
diff options
context:
space:
mode:
authorBoskSpb <bosk@mail.ru>2016-12-15 01:05:35 +0300
committerGitHub <noreply@github.com>2016-12-15 01:05:35 +0300
commitebf2e82f9687110b0237245169f2ae20f18382c0 (patch)
treeffd2f53566b2351e8c345124c47fe5a7f4509908 /www
parente2f15230ac9c4c90b483914398886c70f7d6dd04 (diff)
parent1d9625d30bec5929164fb53b237fa4995f6991f5 (diff)
Merge pull request #1 from pliablepixels/master
updating
Diffstat (limited to 'www')
-rw-r--r--www/css/style.css325
-rw-r--r--www/external/GCMIntentService.java543
-rw-r--r--www/external/NeuQuant.min.js55
-rw-r--r--www/external/angular-carousel.min.js124
-rw-r--r--www/external/angular-circular-navigation.min.js5
-rw-r--r--www/external/canvas-toBlob.min.js17
-rw-r--r--www/external/draggabilly.pkgd.min.js96
-rw-r--r--www/external/gifwriter.min.js64
-rw-r--r--www/external/imagesloaded.pkgd.min.js42
-rw-r--r--www/external/ion-pullup.min.js10
-rw-r--r--www/external/ionRadio.min.js12
-rw-r--r--www/external/ionic.content.banner.min.js12
-rw-r--r--www/external/ionic.scroll.sista.min.js28
-rw-r--r--www/external/ng-websocket.min.js31
-rw-r--r--www/external/origjs/NeuQuant.js389
-rw-r--r--www/external/origjs/README.TXT3
-rw-r--r--www/external/origjs/angular-carousel.js (renamed from www/external/angular-carousel.js)26
-rw-r--r--www/external/origjs/angular-circular-navigation.js (renamed from www/external/angular-circular-navigation.js)0
-rw-r--r--www/external/origjs/angular-ios9-uiwebview.patch.js73
-rw-r--r--www/external/origjs/canvas-toBlob.js (renamed from www/external/canvas-toBlob.js)0
-rw-r--r--www/external/origjs/draggabilly.pkgd.js (renamed from www/external/draggabilly.pkgd.js)0
-rw-r--r--www/external/origjs/gifwriter.js406
-rw-r--r--www/external/origjs/imagesloaded.pkgd.js (renamed from www/external/imagesloaded.pkgd.js)0
-rwxr-xr-xwww/external/origjs/ion-pullup.js (renamed from www/external/ion-pullup.js)0
-rw-r--r--www/external/origjs/ionRadio.js (renamed from www/external/ionRadio.js)0
-rw-r--r--www/external/origjs/ionic.content.banner.js (renamed from www/external/ionic.content.banner.js)0
-rw-r--r--www/external/origjs/ionic.scroll.sista.js (renamed from www/external/ionic.scroll.sista.js)0
-rw-r--r--www/external/origjs/ng-websocket.js (renamed from www/external/ng-websocket.js)0
-rw-r--r--www/external/origjs/packery.pkgd.js (renamed from www/external/packery.pkgd.js)4
-rw-r--r--www/external/packery.pkgd.min.js270
-rw-r--r--www/external/polyfill.min.js4
-rw-r--r--www/external/screwdriver.js151
-rw-r--r--www/img/gif-anim.pngbin0 -> 25359 bytes
-rw-r--r--www/img/gif.svg10
-rw-r--r--www/img/mp4.svg7
-rw-r--r--www/index.html270
-rw-r--r--www/js/DataModel.js963
-rw-r--r--www/js/DevOptionsCtrl.js76
-rw-r--r--www/js/EventCtrl.js1742
-rw-r--r--www/js/EventDateTimeFilterCtrl.js79
-rw-r--r--www/js/EventModalCtrl.js897
-rw-r--r--www/js/EventServer.js327
-rw-r--r--www/js/EventServerSettingsCtrl.js207
-rw-r--r--www/js/EventsGraphsCtrl.js158
-rw-r--r--www/js/EventsModalGraphCtrl.js189
-rw-r--r--www/js/FirstUseCtrl.js67
-rw-r--r--www/js/HelpCtrl.js50
-rw-r--r--www/js/ImportantMessageCtrl.js20
-rw-r--r--www/js/LogCtrl.js128
-rw-r--r--www/js/LoginCtrl.js434
-rw-r--r--www/js/LowVersionCtrl.js16
-rw-r--r--www/js/MenuController.js72
-rw-r--r--www/js/MonitorCtrl.js446
-rw-r--r--www/js/MonitorModalCtrl.js617
-rw-r--r--www/js/MontageCtrl.js2375
-rw-r--r--www/js/MontageHistoryCtrl.js723
-rw-r--r--www/js/NewsCtrl.js88
-rw-r--r--www/js/PortalLoginCtrl.js300
-rw-r--r--www/js/StateCtrl.js201
-rw-r--r--www/js/TimelineCtrl.js816
-rw-r--r--www/js/TimelineModalCtrl.js202
-rw-r--r--www/js/WizardCtrl.js348
-rw-r--r--www/js/app.js1207
-rw-r--r--www/js/ionicUtils.js39
-rw-r--r--www/lang/README.md17
-rwxr-xr-xwww/lang/checklang.py53
-rwxr-xr-xwww/lang/checklang.py3121
-rw-r--r--www/lang/help/help-es.html73
-rw-r--r--www/lang/help/help-pl.html71
-rw-r--r--www/lang/locale-en.json21
-rw-r--r--www/lang/locale-es.json383
-rw-r--r--www/lang/locale-pl.json389
-rw-r--r--www/lang/locale-pt.json39
-rw-r--r--www/lib/videogular-cuepoints/.bower.json33
-rw-r--r--www/lib/videogular-cuepoints/LICENSE21
-rw-r--r--www/lib/videogular-cuepoints/bower.json21
-rw-r--r--www/lib/videogular-cuepoints/cuepoints.css18
-rw-r--r--www/lib/videogular-cuepoints/cuepoints.js52
-rw-r--r--www/lib/videogular/videogular.js9
-rw-r--r--www/templates/devoptions.html43
-rw-r--r--www/templates/events-graphs.html58
-rw-r--r--www/templates/events-modal.html61
-rw-r--r--www/templates/events-modalgraph.html9
-rw-r--r--www/templates/events-popover.html15
-rw-r--r--www/templates/events.html341
-rw-r--r--www/templates/eventserversettings.html82
-rw-r--r--www/templates/first-use.html16
-rw-r--r--www/templates/help.html17
-rw-r--r--www/templates/important_message.html9
-rw-r--r--www/templates/log.html19
-rw-r--r--www/templates/login.html59
-rw-r--r--www/templates/lowversion.html8
-rw-r--r--www/templates/monitors-modal.html116
-rw-r--r--www/templates/monitors.html104
-rw-r--r--www/templates/montage-history.html197
-rw-r--r--www/templates/montage.html126
-rw-r--r--www/templates/news.html17
-rw-r--r--www/templates/reorder-modal.html43
-rw-r--r--www/templates/state.html22
-rw-r--r--www/templates/timeline-modal.html52
-rw-r--r--www/templates/timeline-popover.html24
-rw-r--r--www/templates/timeline.html3
-rw-r--r--www/templates/wizard.html182
-rw-r--r--www/templates/zm-portal-login.html14
104 files changed, 11805 insertions, 6917 deletions
diff --git a/www/css/style.css b/www/css/style.css
index 950a9e0d..18a34314 100644
--- a/www/css/style.css
+++ b/www/css/style.css
@@ -5,22 +5,54 @@
font-weight: normal;
font-style: normal;
}
+
+
+.icon.gif-icon {
+ background-repeat: no-repeat;
+ background-position: 50%;
+ height: 100%;
+ /*background-image: url('../img/gif.svg');*/
+ background-image: url('../img/gif-anim.png');
+ background-size:contain;
+}
+
+
+.icon.mp4-icon {
+ background-repeat: no-repeat;
+ background-position: 50%;
+ height: 100%;
+ /*background-image: url('../img/gif.svg');*/
+ background-image: url('../img/mp4.svg');
+ background-size:contain;
+}
+
+.icon-save:before {
+ font-family: "fontawesome";
+ content: "\f0c7";
+}
+
+
+
.icon-super-speed:before {
font-family: "fontawesome";
content: "\f135";
}
+
.icon-normal-speed:before {
font-family: "fontawesome";
content: "\f1b9";
}
+
.icon-faster:before {
font-family: "fontawesome";
content: "\f101";
}
+
.icon-slower:before {
font-family: "fontawesome";
content: "\f100";
}
+
.icon-server:before {
font-family: "fontawesome";
content: "\f233";
@@ -31,18 +63,22 @@
width: 100%;
height: 100%;
}
+
image-loader {
position: absolute;
top: 50%;
left: 50%;
z-index: 999;
}
+
.vis-time-axis .grid.vis-odd {
background: #f5f5f5;
}
+
.my-vis-font {
font-size: 10px;
}
+
.my-vis-buttons {
position: absolute;
top: 0;
@@ -50,20 +86,25 @@ image-loader {
margin: 10px;
z-index: 9999;
}
+
ion-popover-view.fit {
height: auto;
}
+
ion-popover-view.fit ion-content {
position: relative;
}
+
.platform-android ion-popover-view.fit {
margin-top: 10px;
}
+
.platform-ios ion-popover-view.fit {
padding-top: 10px;
padding-bottom: 10px;
}
+
/* Using this for full screen modals for event and monitor mode */
.modal {
@@ -74,6 +115,7 @@ ion-popover-view.fit ion-content {
width: 100%
}
+
/* I am using flexboxes to dynamicall adapt content
in montage view.
Credit: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
@@ -91,10 +133,12 @@ Credit: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
text-align: left;
flex-wrap: wrap;
}
+
.wrapper > * {
/*padding: 5px;*/
/*flex: 1 100%; -- I commented this out - the wrapper was taking up full width in Chrome*/
}
+
.header {
background: #333333;
color: #cccccc;
@@ -103,14 +147,16 @@ Credit: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
border-width: 1px;
opacity: 80%;
}
+
.header-event-id {
- background:rgba(52, 152, 219,0.2);
+ background: rgba(52, 152, 219, 0.2);
color: #FFF;
bottom: 0;
left: 0;
right: 0;
opacity: 1;
}
+
.header-paused {
background: #ba3e3e;
color: #cccccc;
@@ -120,6 +166,7 @@ Credit: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
opacity: 80%;
transform: translate(-50%, -50%);
}
+
.alarmed-header {
background: #ba3e3e;
color: #ffffff;
@@ -128,6 +175,7 @@ Credit: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
border-width: 1px;
opacity: 80%;
}
+
.minimized-alarmed-header {
background: #ba3e3e;
color: #ffffff;
@@ -140,12 +188,15 @@ Credit: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
left: 0px;
width: 100%;
}
+
#slowpulse {
-webkit-animation-duration: 1500ms;
}
+
.alarmed-body {
border-left: thick solid #ba3e3e;
}
+
.alarmed-footer {
background: #ba3e3e;
border-color: #ba3e3e;
@@ -153,6 +204,7 @@ Credit: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
border-width: 1px;
/*padding:2px;*/
}
+
.main {
text-align: center;
/*background: #5a5a5a;*/
@@ -161,25 +213,31 @@ Credit: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
padding: 0px;
/*padding:2px;*/
}
+
.aside-1 {
background: gold;
}
+
.aside-2 {
background: hotpink;
}
+
figure {
position: relative;
}
+
figure img {
display: block;
width: 100%;
height: auto;
}
+
figcaption {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
+
.normal-figcaption {
background: rgba(0, 0, 0, 0.2);
color: #FFF;
@@ -189,6 +247,7 @@ figcaption {
right: 0;
opacity: 1;
}
+
.alarmed-figcaption {
background: #ba3e3e;
color: #ffffff;
@@ -199,12 +258,14 @@ figcaption {
opacity: 0.7;
}
+
/* modified from:
http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
.notification-badge {
position: relative;
}
+
.notification-badge[data-badge]:after {
content: attr(data-badge);
position: absolute;
@@ -221,6 +282,7 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
font-weight: bold;
}
+
/*
.notification-badge{
@@ -240,6 +302,7 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
right: 95%;
transform: translate(-50%, -50%);
}
+
.ptzcentered {
position: fixed;
/* or absolute */
@@ -247,6 +310,7 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
left: 50%;
transform: translate(-50%, -50%);
}
+
.timeline_text {
text-align: center;
color: white;
@@ -258,6 +322,7 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
font-size: 80%;
}
+
/* // not using for now, may come in handy later
.ptzcenteredtopbutton
{
@@ -273,12 +338,14 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
left: 50%;
transform: translate(-50%, -50%);
}
+
.ptzpresetbuttons {
position: absolute;
top: 5%;
left: 10%;
width: 80%;
}
+
.timelinebuttons {
position: fixed;
/* or absolute */
@@ -287,6 +354,7 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
transform: translate(-50%, -50%);
z-index: 99999;
}
+
.eventprogress {
position: absolute;
-webkit-transform: rotate(-90deg) translateY(-1000%);
@@ -299,6 +367,7 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
z-index: 10;
width: 100px;
}
+
.camera-icon {
position: absolute;
bottom: 20px;
@@ -306,6 +375,7 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
z-index: 10;
opacity: 0.7;
}
+
.desktop-zoom-icon {
position: absolute;
bottom: 20px;
@@ -313,6 +383,7 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
z-index: 10;
opacity: 0.7;
}
+
.modal-alarm-badge {
position: absolute;
bottom: 20px;
@@ -320,6 +391,7 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
z-index: 10;
opacity: 0.7;
}
+
.event-modal-alarm-badge {
position: absolute;
left: 10px;
@@ -327,6 +399,7 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
z-index: 10;
opacity: 1;
}
+
.events-modal-gapless-icon {
position: absolute;
bottom: 120px;
@@ -334,15 +407,19 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
z-index: 10;
opacity: 0.5;
}
+
.popup80 .popup {
width: 80% !important;
}
+
.popup90 .popup {
width: 90% !important;
}
+
.popup95 .popup {
width: 95% !important;
}
+
.events-modal-camera-icon {
position: absolute;
bottom: 120px;
@@ -350,6 +427,7 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
z-index: 10;
opacity: 0.5;
}
+
.events-modal-onlyalarms-icon {
position: absolute;
bottom: 120px;
@@ -357,6 +435,7 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
z-index: 10;
opacity: 0.5;
}
+
.events-range {
position: absolute;
bottom: 20px;
@@ -365,6 +444,7 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
opacity: 0.7;
width: 70%;
}
+
.events-range-modal {
position: absolute;
bottom: 30px;
@@ -373,6 +453,7 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
opacity: 1;
width: 70%;
}
+
.events-range-modal-text {
position: absolute;
top: 20px;
@@ -388,6 +469,7 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
padding-left: 3px;
padding-right: 3px;
}
+
.monitor-modal-text {
position: absolute;
bottom: 30px;
@@ -401,6 +483,7 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
padding-left: 3px;
padding-right: 3px;
}
+
.events-alarm-line {
position: relative;
bottom: 1px;
@@ -408,7 +491,26 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
width: 100%;
border-top: 1px solid black;
}
+
.bwmode {
+ writing-mode: vertical-lr;
+ -webkit-writing-mode: vertical-lr;
+ position: absolute;
+ top: 30%;
+ transform: translateY(-50%: );
+ right: 0;
+ background-color: #f1c40f;
+ color: #000;
+ font-size: 11px;
+ border-radius: 5px 0px 0px 5px;
+ z-index: 99999;
+ padding-top: 3px;
+ padding-bottom: 3px;
+ opacity: 0.7;
+}
+
+
+/*.bwmode {
position: absolute;
top: 30%;
right: -28px;
@@ -425,27 +527,27 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
-o-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg);
-}
-
+}*/
.events-float-filter {
+ writing-mode: vertical-lr;
+ -webkit-writing-mode: vertical-lr;
position: absolute;
top: 50%;
- right: -12px;
- color: #fff;
+ transform: translateY(-50%: );
+ right: 0;
background-color: #b47226;
- border-radius: 0px 0px 5px 5px;
+ color: #fff;
font-size: 11px;
- padding-left: 3px;
- padding-right: 3px;
- opacity: 0.7;
+ border-radius: 5px 0px 0px 5px;
z-index: 99999;
- -webkit-transform: rotate(90deg);
- -moz-transform: rotate(90deg);
- -o-transform: rotate(90deg);
- -ms-transform: rotate(90deg);
- transform: rotate(90deg);
+ padding-top: 3px;
+ padding-bottom: 3px;
+ opacity: 0.7;
}
+
+
+/*
.events-filter-on {
position: absolute;
left: 50%;
@@ -458,80 +560,104 @@ http://www.cssportal.com/tryit/index.php?file=blog/css-notification-badge */
padding-right: 6px;
color: #fff;
font-size: 11px;
-}
+}*/
+
/* Styling of progress bar
http://www.useragentman.com/blog/2012/01/03/cross-browser-html5-progress-bars-in-depth/
*/
+
progress,
+
/* All HTML5 progress enabled browsers */
-progress[role]/* polyfill */
+
+progress[role]
+/* polyfill */
+
{
/* Turns off styling - not usually needed, but good to know. */
-
appearance: none;
-moz-appearance: none;
-webkit-appearance: none;
/* gets rid of default border in Firefox and Opera. */
-
border: none;
/* Needs to be in here for Safari polyfill so background images work as expected. */
-
background-size: auto;
height: 10px;
}
+
/* Polyfill */
+
progress[role]:after {
background-image: none;
/* removes default background from polyfill */
}
+
/* Ensure fallback text doesn't appear in polyfill */
+
progress[role] strong {
display: none;
}
+
/* ----------------- progress background start ------------------*/
+
progress,
+
/* Firefox */
+
progress[role][aria-valuenow] {
/* Polyfill */
-
background: #aaaaaa !important;
/* !important is needed by the polyfill */
}
+
/* Chrome */
+
progress::-webkit-progress-bar {
background: #aaaaaa;
}
+
/* ----------------- progress background start ------------------*/
+
/* ----------------- progress bar color start ------------------*/
+
/* IE10 */
+
progress {
color: #f1a165;
}
+
/* Firefox */
+
progress::-moz-progress-bar {
background: #f1a165;
}
+
/* Chrome */
+
progress::-webkit-progress-value {
background: #f1a165;
}
+
/* Polyfill */
+
progress[aria-valuenow]:before {
background: #f1a165;
}
+
/* ----------------- progress bar color end ------------------*/
+
.rotate-button {
-webkit-transform: rotate(90deg);
-moz-transform: rotate(90deg);
@@ -539,55 +665,68 @@ progress[aria-valuenow]:before {
-ms-transform: rotate(90deg);
transform: rotate(90deg);
}
+
.zm-image-fit {
max-width: 100%;
max-height: 100%;
}
+
.zm-image-crop {
width: 100%;
}
+
/*
.object-fit_cover { object-fit: cover; width:100%; height:auto;}
.object-fit_contain { object-fit: contain; max-width:100%; height:auto; }
*/
+
.object-fit_cover {
object-fit: cover;
width: 100%;
height: auto;
}
+
.object-fit_contain {
object-fit: contain;
max-width: 100%;
max-height: 100%;
}
+
.list .item.item-accordion {
line-height: 200px;
padding-top: 0;
padding-bottom: 0;
transition: 3s all linear;
}
+
.list .item.item-accordion.ng-hide {
line-height: 0px;
}
+
.list .item.item-accordion.ng-hide-add,
.list .item.item-accordion.ng-hide-remove {
display: block !important;
}
+
ul[rn-carousel] > li {
position: relative;
margin-left: -100%;
}
+
ul[rn-carousel] > li:first-child {
margin-left: 0;
}
+
ul[rn-carousel] img {
max-width: 100%;
}
+
.content-banner .content-banner-close {
margin-top: -5px;
}
+
.mySliderClass.jslider.sliderCSS div.jslider-pointer {
left: 50%;
width: 30px;
@@ -598,12 +737,14 @@ ul[rn-carousel] img {
margin-left: -3px;
margin-top: -8px;
}
+
.mySliderClass div.jslider-scale ins {
color: red;
/*background-color:red;
border-radius:5%;
line-height:5px;*/
}
+
.mySliderClass div.jslider-value {
position: absolute;
top: -29px;
@@ -618,14 +759,18 @@ ul[rn-carousel] img {
-o-border-radius: 2px;
border-radius: 2px
}
+
input[type=range]::-webkit-slider-thumb {
background: #2980b9;
}
+
/* this is for input = password and dialog = text */
+
.pinCode input[type=number] {
-webkit-text-security: disc;
}
+
.pinCode input[type=number] {
height: 50px;
width: 200px;
@@ -634,80 +779,116 @@ input[type=range]::-webkit-slider-thumb {
border: 1px solid #ddd;
-webkit-appearance: none;
}
+
.pin-background.scroll-content {
/*background: url('../img/background.png') no-repeat center center fixed;*/
-
background-size: cover;
background-color: #555555;
/*background-color:#16a085;*/
}
+
#responsive-image {
width: 65%;
margin: 0 auto;
}
+
#responsive-image img {
width: 100%
}
+
/* to avoid padding on delete in event list */
+
.item-content {
padding-right: 16px !important;
}
+
.eventDeleteSpeed {
-webkit-animation-duration: 300ms;
}
+
.content-banner.alarm {
background-color: forestgreen;
color: white;
}
+
.content-banner.net {
background-color: #9b59b6;
color: white;
}
+
/* This accordion list is used for event server monitor filter
* http://docs.angularjs.org/api/ng/directive/ngShow#usage_animations
*/
+
.list .item.item-accordion {
line-height: 45px;
padding-top: 0;
padding-bottom: 0;
transition: 0.09s all linear;
}
+
.list .item.item-accordion.ng-hide {
line-height: 0px;
}
+
.list .item.item-accordion.ng-hide-add,
.list .item.item-accordion.ng-hide-remove {
display: block !important;
}
+
.custom-list i {
float: right;
}
+
/* This is for quick scrub for H264 */
+
.videogular-container {
height: 260px;
margin: auto;
overflow: hidden;
float: left;
}
-.videogular-full-container {
- height: 80%;
- z-index: 100;
- float: center;
+
+/* don't need these, responsive is on
+and parent div is hard pixels
+
+ .videogular-container-modal-width {
+ height: auto;
+ width: 100%;
margin: auto;
overflow: hidden;
}
+
+.videogular-container-modal-height {
+ height: 100%;
+ width:auto;
+ margin: auto;
+
+}*/
+
+videogular div.event-time {
+ position: absolute;
+ display: block;
+ z-index: 9999;
+ bottom: 10%;
+ background: rgba(255, 0, 0, 0.5);
+}
+
#full-screen-event {
-webkit-animation-duration: 200ms;
}
+
#monitorimage {
-webkit-animation-duration: 200ms;
}
+
#firstuse {
-webkit-animation-delay: 1s;
}
+
#transition-delay {
-webkit-animation-delay: 1s;
}
@@ -716,93 +897,114 @@ input[type=range]::-webkit-slider-thumb {
-webkit-animation-delay: 4s;
}
+
/* packery stuff */
+
* {
box-sizing: border-box;
}
-body {
- font-family: sans-serif;
-}
-* {
- font-family: -apple-system, "Helvetica Neue", sans-serif;
-}
+
+
/* ---- grid ---- */
+
.dragborder {
border: 2px dotted #3498db;
}
+
.dragborder-selected {
border: 4px solid #e74c3c;
}
+
.grid-sizer {
width: 10%;
}
+
.grid-item {
width: 20%;
}
+
.grid-item-10 {
width: 10%;
}
+
.grid-item-20 {
width: 20%;
}
+
.grid-item-30 {
width: 30%;
}
+
.grid-item-40 {
width: 40%;
}
+
.grid-item-50 {
width: 50%;
}
+
.grid-item-60 {
width: 60%;
}
+
.grid-item-70 {
width: 70%;
}
+
.grid-item-80 {
width: 80%;
}
+
.grid-item-90 {
width: 90%;
}
+
.grid-item-100 {
width: 100%;
}
+
/* clear fix */
+
.grid:after {
content: '';
display: block;
clear: both;
}
+
.grid-item img {
display: block;
width: 100%;
height: auto;
}
+
/* ---- .grid-item ---- */
+
.grid-item.is-dragging,
.grid-item.is-positioning-post-drag {
background: #34495e;
z-index: 2;
}
+
.packery-drop-placeholder {
outline: 3px dashed hsla(0, 0%, 0%, 0.5);
outline-offset: -6px;
-webkit-transition: -webkit-transform 0.2s;
transition: transform 0.2s;
}
+
.wide-as-needed {
overflow: scroll;
white-space: nowrap;
}
+
.visred {
fill: #FF0000;
}
+
.wizardtip {
margin-left: 5px;
margin-right: 5px;
@@ -816,16 +1018,20 @@ body {
border-color: #8b8888;
border-style: solid;
}
+
.wiz-list {
list-style-type: disc;
list-style-position: inside;
}
+
.zmPullup {
background-color: #E3BE1C;
}
+
#flyoutmenu {
z-index: 99;
}
+
#flyoutmenu ul {
list-style: none;
margin: 0;
@@ -836,11 +1042,13 @@ body {
font-family: sans-serif;
text-transform: uppercase;
}
+
#flyoutmenu ul li a i {
font-size: 2.5em;
font-family: sans-serif;
text-transform: uppercase;
}
+
#flyoutmenu li {
display: inline-block;
margin-bottom: .2em;
@@ -848,43 +1056,52 @@ body {
margin-right: 4px;
line-height: 100%;
}
+
#flyoutmenu li:first-child {
/*background: rgba(192, 57, 43, 0.7);*/
-
background: rgba(108, 122, 137, 0.7);
-webkit-border-radius: 5px 0 0 5px;
}
+
#flyoutmenu li:only-child {
/*background: rgba(192, 57, 43, 0.7);*/
-
background: rgba(108, 122, 137, 0.7);
-webkit-border-radius: 5px 5px 5px 5px;
}
+
#flyoutmenu li:last-child {
-webkit-border-radius: 0 5px 5px 0;
}
+
/* make sure this is after last-child */
+
+
+
+
#flyoutmenu li:only-child {
- /*background: rgba(192, 57, 43, 0.7);*/
background: rgba(108, 122, 137, 0.7);
-webkit-border-radius: 5px 5px 5px 5px;
}
+
#flyoutmenu li:nth-child(n+2) {
background: rgba(108, 122, 137, 0.7);
z-index: -1;
}
#flyoutmenu li:active {
- background:#3498db;
+ background: #3498db;
}
+
#flyoutmenu a {
text-decoration: none;
color: white;
}
+
/* Overlay graph in event footage */
+
#event_canvas {
position: absolute;
width: 80%;
@@ -894,6 +1111,7 @@ body {
opacity: 1;
z-index: 99;
}
+
#event_canvas_video {
position: absolute;
width: 100%;
@@ -907,15 +1125,12 @@ body {
#history_canvas_video {
position: absolute;
width: 95%;
- top:10%;
+ top: 10%;
height: 20px;
-
opacity: 1;
z-index: 998;
}
-
-
#event_slider {
position: absolute;
width: 80%;
@@ -925,6 +1140,7 @@ body {
opacity: 1;
z-index: 9999;
}
+
#event_rate_text {
position: absolute;
width: 80%;
@@ -934,6 +1150,7 @@ body {
color: #fff;
z-index: 9998;
}
+
.smallnote {
font-size: 9px;
text-align: center;
@@ -942,19 +1159,39 @@ body {
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
+
.white-button-text {
color: #fff !important;
}
+.hiddengifcanvas {
+ width: 0 !important;
+ height: 0 !important;
+ position: absolute;
+ left: -99999px;
+ top: -99999px;
+}
+
+
+
@media (min-width:600px) {
.montage-time {
- display:block !important;
- line-height:250%;
- }
+ display: block !important;
+ line-height: 250%;
+ }
}
@media (max-width:599px) {
.montage-time {
- display:none !important;
- }
+ display: none !important;
+ }
}
+
+body {
+ font-family: sans-serif;
+ height:100%;
+}
+
+* {
+ font-family: -apple-system, "Helvetica Neue", sans-serif;
+} \ No newline at end of file
diff --git a/www/external/GCMIntentService.java b/www/external/GCMIntentService.java
deleted file mode 100644
index 9ffe5836..00000000
--- a/www/external/GCMIntentService.java
+++ /dev/null
@@ -1,543 +0,0 @@
-package com.adobe.phonegap.push;
-
-import android.annotation.SuppressLint;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.AssetManager;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Color;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.v4.app.NotificationCompat;
-import android.text.Html;
-import android.util.Log;
-
-import com.google.android.gcm.GCMBaseIntentService;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Random;
-
-@SuppressLint("NewApi")
-public class GCMIntentService extends GCMBaseIntentService implements PushConstants {
-
- private static final String LOG_TAG = "PushPlugin_GCMIntentService";
- private static HashMap<Integer, ArrayList<String>> messageMap = new HashMap<Integer, ArrayList<String>>();
-
- public void setNotification(int notId, String message){
- ArrayList<String> messageList = messageMap.get(notId);
- if(messageList == null) {
- messageList = new ArrayList<String>();
- messageMap.put(notId, messageList);
- }
-
- if(message.isEmpty()){
- messageList.clear();
- }else{
- messageList.add(message);
- }
- }
-
- public GCMIntentService() {
- super("GCMIntentService");
- }
-
- @Override
- public void onRegistered(Context context, String regId) {
-
- Log.v(LOG_TAG, "onRegistered: " + regId);
-
- try {
- JSONObject json = new JSONObject().put(REGISTRATION_ID, regId);
-
- Log.v(LOG_TAG, "onRegistered: " + json.toString());
-
- PushPlugin.sendEvent( json );
- }
- catch(JSONException e) {
- // No message to the user is sent, JSON failed
- Log.e(LOG_TAG, "onRegistered: JSON exception");
- }
- }
-
- @Override
- public void onUnregistered(Context context, String regId) {
- Log.d(LOG_TAG, "onUnregistered - regId: " + regId);
- }
-
- @Override
- protected void onMessage(Context context, Intent intent) {
- Log.d(LOG_TAG, "onMessage - context: " + context);
-
- // Extract the payload from the message
- Bundle extras = intent.getExtras();
- if (extras != null) {
- // if we are in the foreground, just surface the payload, else post it to the statusbar
- if (PushPlugin.isInForeground()) {
- extras.putBoolean(FOREGROUND, true);
- PushPlugin.sendExtras(extras);
- }
- else {
- extras.putBoolean(FOREGROUND, false);
-
- // Send a notification if there is a message
- String message = this.getMessageText(extras);
- String title = getString(extras, TITLE, "");
- if ((message != null && message.length() != 0) ||
- (title != null && title.length() != 0)) {
- createNotification(context, extras);
- }
- }
- }
- }
-
- public void createNotification(Context context, Bundle extras) {
- NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- String appName = getAppName(this);
- String packageName = context.getPackageName();
- Resources resources = context.getResources();
-
- int notId = parseInt(NOT_ID, extras);
- Intent notificationIntent = new Intent(this, PushHandlerActivity.class);
- notificationIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- notificationIntent.putExtra(PUSH_BUNDLE, extras);
- notificationIntent.putExtra(NOT_ID, notId);
-
- int requestCode = new Random().nextInt();
- PendingIntent contentIntent = PendingIntent.getActivity(this, requestCode, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-
- NotificationCompat.Builder mBuilder =
- new NotificationCompat.Builder(context)
- .setWhen(System.currentTimeMillis())
- .setContentTitle(getString(extras, TITLE))
- .setTicker(getString(extras, TITLE))
- .setContentIntent(contentIntent)
- .setAutoCancel(true);
-
- SharedPreferences prefs = context.getSharedPreferences(PushPlugin.COM_ADOBE_PHONEGAP_PUSH, Context.MODE_PRIVATE);
- String localIcon = prefs.getString(ICON, null);
- String localIconColor = prefs.getString(ICON_COLOR, null);
- boolean soundOption = prefs.getBoolean(SOUND, true);
- //PP - make vibrate false
- //boolean vibrateOption = prefs.getBoolean(VIBRATE, true);
- boolean vibrateOption = false;
- Log.d(LOG_TAG, "stored icon=" + localIcon);
- Log.d(LOG_TAG, "stored iconColor=" + localIconColor);
- Log.d(LOG_TAG, "stored sound=" + soundOption);
- Log.d(LOG_TAG, "stored vibrate=" + vibrateOption);
-
- /*
- * Notification Vibration
- */
-
- setNotificationVibration(extras, vibrateOption, mBuilder);
-
- /*
- * Notification Icon Color
- *
- * Sets the small-icon background color of the notification.
- * To use, add the `iconColor` key to plugin android options
- *
- */
- setNotificationIconColor(getString(extras,"color"), mBuilder, localIconColor);
-
- /*
- * Notification Icon
- *
- * Sets the small-icon of the notification.
- *
- * - checks the plugin options for `icon` key
- * - if none, uses the application icon
- *
- * The icon value must be a string that maps to a drawable resource.
- * If no resource is found, falls
- *
- */
- setNotificationSmallIcon(context, extras, packageName, resources, mBuilder, localIcon);
-
- /*
- * Notification Large-Icon
- *
- * Sets the large-icon of the notification
- *
- * - checks the gcm data for the `image` key
- * - checks to see if remote image, loads it.
- * - checks to see if assets image, Loads It.
- * - checks to see if resource image, LOADS IT!
- * - if none, we don't set the large icon
- *
- */
- setNotificationLargeIcon(extras, packageName, resources, mBuilder);
-
- /*
- * Notification Sound
- */
- if (soundOption) {
- /* PP Enable Sound option */
- setNotificationSound(context, extras, mBuilder);
- }
-
- /*
- * LED Notification
- */
- setNotificationLedColor(extras, mBuilder);
-
- /*
- * Priority Notification
- */
- setNotificationPriority(extras, mBuilder);
-
- /*
- * Notification message
- */
- setNotificationMessage(notId, extras, mBuilder);
-
- /*
- * Notification count
- */
- setNotificationCount(extras, mBuilder);
-
- /*
- * Notication add actions
- */
- createActions(extras, mBuilder, resources, packageName);
-
- // PP add lights
- mBuilder.setLights(0xFFFF0000, 500, 500);
-
- //PP - http://stackoverflow.com/questions/29343974/custom-sound-in-android-push-notifications-gcm
- // mNotificationManager.notify(appName, notId, mBuilder.build());
- Notification notification = mBuilder.build();
- //notification.sound = Uri.parse("android.resource://" + context.getPackageName() + "/"+ R.raw.blop);
- //PP http://stackoverflow.com/questions/15449945/notification-sound-from-uri-parse-does-not-work
- //notification.sound = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + getPackageName() + "/raw/blop");
- //notification.defaults |= Notification.DEFAULT_VIBRATE;
- mNotificationManager.notify((String) appName, notId, notification);
- }
-
- private void createActions(Bundle extras, NotificationCompat.Builder mBuilder, Resources resources, String packageName) {
- Log.d(LOG_TAG, "create actions");
- String actions = getString(extras, ACTIONS);
- if (actions != null) {
- try {
- JSONArray actionsArray = new JSONArray(actions);
- for (int i=0; i < actionsArray.length(); i++) {
- Log.d(LOG_TAG, "adding action");
- JSONObject action = actionsArray.getJSONObject(i);
- Log.d(LOG_TAG, "adding callback = " + action.getString(CALLBACK));
- Intent intent = new Intent(this, PushHandlerActivity.class);
- intent.putExtra(CALLBACK, action.getString(CALLBACK));
- intent.putExtra(PUSH_BUNDLE, extras);
- PendingIntent pIntent = PendingIntent.getActivity(this, i, intent, PendingIntent.FLAG_UPDATE_CURRENT);
-
- mBuilder.addAction(resources.getIdentifier(action.getString(ICON), DRAWABLE, packageName),
- action.getString(TITLE), pIntent);
- }
- } catch(JSONException e) {
- // nope
- }
- }
- }
-
- private void setNotificationCount(Bundle extras, NotificationCompat.Builder mBuilder) {
- String msgcnt = getString(extras, MSGCNT);
- if (msgcnt == null) {
- msgcnt = getString(extras, BADGE);
- }
- if (msgcnt != null) {
- mBuilder.setNumber(Integer.parseInt(msgcnt));
- }
- }
-
- private void setNotificationVibration(Bundle extras, Boolean vibrateOption, NotificationCompat.Builder mBuilder) {
- String vibrationPattern = getString(extras, VIBRATION_PATTERN);
- if (vibrationPattern != null) {
- String[] items = vibrationPattern.replaceAll("\\[", "").replaceAll("\\]", "").split(",");
- long[] results = new long[items.length];
- for (int i = 0; i < items.length; i++) {
- try {
- results[i] = Long.parseLong(items[i]);
- } catch (NumberFormatException nfe) {}
- }
- mBuilder.setVibrate(results);
- } else {
- if (vibrateOption) {
- mBuilder.setDefaults(Notification.DEFAULT_VIBRATE);
- }
- }
- }
-
- private void setNotificationMessage(int notId, Bundle extras, NotificationCompat.Builder mBuilder) {
- String message = getMessageText(extras);
-
- String style = getString(extras, STYLE, STYLE_TEXT);
- if(STYLE_INBOX.equals(style)) {
- setNotification(notId, message);
-
- mBuilder.setContentText(message);
-
- ArrayList<String> messageList = messageMap.get(notId);
- Integer sizeList = messageList.size();
- if (sizeList > 1) {
- String sizeListMessage = sizeList.toString();
- String stacking = sizeList + " more";
- if (getString(extras, SUMMARY_TEXT) != null) {
- stacking = getString(extras, SUMMARY_TEXT);
- stacking = stacking.replace("%n%", sizeListMessage);
- }
- NotificationCompat.InboxStyle notificationInbox = new NotificationCompat.InboxStyle()
- .setBigContentTitle(getString(extras, TITLE))
- .setSummaryText(stacking);
-
- for (int i = messageList.size() - 1; i >= 0; i--) {
- notificationInbox.addLine(Html.fromHtml(messageList.get(i)));
- }
-
- mBuilder.setStyle(notificationInbox);
- } else {
- NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle();
- if (message != null) {
- bigText.bigText(message);
- bigText.setBigContentTitle(getString(extras, TITLE));
- mBuilder.setStyle(bigText);
- }
- }
- } else if (STYLE_PICTURE.equals(style)) {
- setNotification(notId, "");
-
- NotificationCompat.BigPictureStyle bigPicture = new NotificationCompat.BigPictureStyle();
- bigPicture.bigPicture(getBitmapFromURL(getString(extras, PICTURE)));
- bigPicture.setBigContentTitle(getString(extras, TITLE));
- bigPicture.setSummaryText(getString(extras, SUMMARY_TEXT));
-
- mBuilder.setContentTitle(getString(extras, TITLE));
- mBuilder.setContentText(message);
-
- mBuilder.setStyle(bigPicture);
- } else {
- setNotification(notId, "");
-
- NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle();
-
- if (message != null) {
- mBuilder.setContentText(Html.fromHtml(message));
-
- bigText.bigText(message);
- bigText.setBigContentTitle(getString(extras, TITLE));
-
- String summaryText = getString(extras, SUMMARY_TEXT);
- if (summaryText != null) {
- bigText.setSummaryText(summaryText);
- }
-
- mBuilder.setStyle(bigText);
- }
- /*
- else {
- mBuilder.setContentText("<missing message content>");
- }
- */
- }
- }
-
- private String getString(Bundle extras,String key) {
- String message = extras.getString(key);
- if (message == null) {
- message = extras.getString(GCM_NOTIFICATION+"."+key);
- }
- return message;
- }
-
- private String getString(Bundle extras,String key, String defaultString) {
- String message = extras.getString(key);
- if (message == null) {
- message = extras.getString(GCM_NOTIFICATION+"."+key, defaultString);
- }
- return message;
- }
-
- private String getMessageText(Bundle extras) {
- String message = getString(extras, MESSAGE);
- if (message == null) {
- message = getString(extras, BODY);
- }
- return message;
- }
-
- private void setNotificationSound(Context context, Bundle extras, NotificationCompat.Builder mBuilder) {
- String soundname = getString(extras, SOUNDNAME);
- if (soundname == null) {
- soundname = getString(extras, SOUND);
- }
- if (soundname != null) {
- Uri sound = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE
- + "://" + context.getPackageName() + "/raw/" + soundname);
- Log.d(LOG_TAG, sound.toString());
- mBuilder.setSound(sound);
- } else {
- mBuilder.setSound(android.provider.Settings.System.DEFAULT_NOTIFICATION_URI);
- }
- }
-
- private void setNotificationLedColor(Bundle extras, NotificationCompat.Builder mBuilder) {
- String ledColor = getString(extras, LED_COLOR);
- if (ledColor != null) {
- // Converts parse Int Array from ledColor
- String[] items = ledColor.replaceAll("\\[", "").replaceAll("\\]", "").split(",");
- int[] results = new int[items.length];
- for (int i = 0; i < items.length; i++) {
- try {
- results[i] = Integer.parseInt(items[i]);
- } catch (NumberFormatException nfe) {}
- }
- if (results.length == 4) {
- mBuilder.setLights(Color.argb(results[0], results[1], results[2], results[3]), 500, 500);
- } else {
- Log.e(LOG_TAG, "ledColor parameter must be an array of length == 4 (ARGB)");
- }
- }
- }
-
- private void setNotificationPriority(Bundle extras, NotificationCompat.Builder mBuilder) {
- String priorityStr = getString(extras, PRIORITY);
- if (priorityStr != null) {
- try {
- Integer priority = Integer.parseInt(priorityStr);
- if (priority >= NotificationCompat.PRIORITY_MIN && priority <= NotificationCompat.PRIORITY_MAX) {
- mBuilder.setPriority(priority);
- } else {
- Log.e(LOG_TAG, "Priority parameter must be between -2 and 2");
- }
- } catch (NumberFormatException e) {
- e.printStackTrace();
- }
- }
- }
-
- private void setNotificationLargeIcon(Bundle extras, String packageName, Resources resources, NotificationCompat.Builder mBuilder) {
- String gcmLargeIcon = getString(extras, IMAGE); // from gcm
- if (gcmLargeIcon != null) {
- if (gcmLargeIcon.startsWith("http://") || gcmLargeIcon.startsWith("https://")) {
- mBuilder.setLargeIcon(getBitmapFromURL(gcmLargeIcon));
- Log.d(LOG_TAG, "using remote large-icon from gcm");
- } else {
- AssetManager assetManager = getAssets();
- InputStream istr;
- try {
- istr = assetManager.open(gcmLargeIcon);
- Bitmap bitmap = BitmapFactory.decodeStream(istr);
- mBuilder.setLargeIcon(bitmap);
- Log.d(LOG_TAG, "using assets large-icon from gcm");
- } catch (IOException e) {
- int largeIconId = 0;
- largeIconId = resources.getIdentifier(gcmLargeIcon, DRAWABLE, packageName);
- if (largeIconId != 0) {
- Bitmap largeIconBitmap = BitmapFactory.decodeResource(resources, largeIconId);
- mBuilder.setLargeIcon(largeIconBitmap);
- Log.d(LOG_TAG, "using resources large-icon from gcm");
- } else {
- Log.d(LOG_TAG, "Not setting large icon");
- }
- }
- }
- }
- }
-
- private void setNotificationSmallIcon(Context context, Bundle extras, String packageName, Resources resources, NotificationCompat.Builder mBuilder, String localIcon) {
- int iconId = 0;
- String icon = getString(extras, ICON);
- if (icon != null) {
- iconId = resources.getIdentifier(icon, DRAWABLE, packageName);
- Log.d(LOG_TAG, "using icon from plugin options");
- }
- else if (localIcon != null) {
- iconId = resources.getIdentifier(localIcon, DRAWABLE, packageName);
- Log.d(LOG_TAG, "using icon from plugin options");
- }
- if (iconId == 0) {
- Log.d(LOG_TAG, "no icon resource found - using application icon");
- iconId = context.getApplicationInfo().icon;
- }
- mBuilder.setSmallIcon(iconId);
- }
-
- private void setNotificationIconColor(String color, NotificationCompat.Builder mBuilder, String localIconColor) {
- int iconColor = 0;
- if (color != null) {
- try {
- iconColor = Color.parseColor(color);
- } catch (IllegalArgumentException e) {
- Log.e(LOG_TAG, "couldn't parse color from android options");
- }
- }
- else if (localIconColor != null) {
- try {
- iconColor = Color.parseColor(localIconColor);
- } catch (IllegalArgumentException e) {
- Log.e(LOG_TAG, "couldn't parse color from android options");
- }
- }
- if (iconColor != 0) {
- mBuilder.setColor(iconColor);
- }
- }
-
- public Bitmap getBitmapFromURL(String strURL) {
- try {
- URL url = new URL(strURL);
- HttpURLConnection connection = (HttpURLConnection) url.openConnection();
- connection.setDoInput(true);
- connection.connect();
- InputStream input = connection.getInputStream();
- return BitmapFactory.decodeStream(input);
- } catch (IOException e) {
- e.printStackTrace();
- return null;
- }
- }
-
- private static String getAppName(Context context) {
- CharSequence appName = context.getPackageManager().getApplicationLabel(context.getApplicationInfo());
- return (String)appName;
- }
-
- @Override
- public void onError(Context context, String errorId) {
- Log.e(LOG_TAG, "onError - errorId: " + errorId);
- // if we are in the foreground, just send the error
- if (PushPlugin.isInForeground()) {
- PushPlugin.sendError(errorId);
- }
- }
-
- private int parseInt(String value, Bundle extras) {
- int retval = 0;
-
- try {
- retval = Integer.parseInt(getString(extras, value));
- }
- catch(NumberFormatException e) {
- Log.e(LOG_TAG, "Number format exception - Error parsing " + value + ": " + e.getMessage());
- }
- catch(Exception e) {
- Log.e(LOG_TAG, "Number format exception - Error parsing " + value + ": " + e.getMessage());
- }
-
- return retval;
- }
-}
diff --git a/www/external/NeuQuant.min.js b/www/external/NeuQuant.min.js
new file mode 100644
index 00000000..12d6bb8d
--- /dev/null
+++ b/www/external/NeuQuant.min.js
@@ -0,0 +1,55 @@
+// NeuQuant.js
+// ===========
+
+/*
+ * NeuQuant Neural-Net Quantization Algorithm
+ * ------------------------------------------
+ *
+ * Copyright (c) 1994 Anthony Dekker
+ *
+ * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See
+ * "Kohonen neural networks for optimal colour quantization" in "Network:
+ * Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of
+ * the algorithm.
+ *
+ * Any party obtaining a copy of these files from the author, directly or
+ * indirectly, is granted, free of charge, a full and unrestricted irrevocable,
+ * world-wide, paid up, royalty-free, nonexclusive right and license to deal in
+ * this software and documentation files (the "Software"), including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons who
+ * receive copies from any such party to do so, with the only requirement being
+ * that this copyright notice remain intact.
+ */
+
+/*
+ * This class handles Neural-Net quantization algorithm
+ * @author Kevin Weiner (original Java version - kweiner@fmsware.com)
+ * @author Thibault Imbert (AS3 version - bytearray.org)
+ * @version 0.1 AS3 implementation
+ * @version 0.2 JS->AS3 "translation" by antimatter15
+ * @version 0.3 JS clean up + using modern JS idioms by sole - http://soledadpenades.com
+ * Also implement fix in color conversion described at http://stackoverflow.com/questions/16371712/neuquant-js-javascript-color-quantization-hidden-bug-in-js-conversion
+ */
+
+NeuQuant=function(){function r(){function r(r,n,f){var o,t
+for(y=r,p=n,h=f,s=new Array(w),o=0;o<w;o++)s[o]=new Array(4),t=s[o],t[0]=t[1]=t[2]=(o<<d+8)/w|0,R[o]=k/w|0,P[o]=0}function n(){for(var r=[],n=new Array(w),f=0;f<w;f++)n[s[f][3]]=f
+for(var o=0,t=0;t<w;t++){var a=n[t]
+r[o++]=s[a][0],r[o++]=s[a][1],r[o++]=s[a][2]}return r}function f(){var r,n,f,o,t,a,u,c
+for(u=0,c=0,r=0;r<w;r++){for(t=s[r],f=r,o=t[1],n=r+1;n<w;n++)a=s[n],a[1]<o&&(f=n,o=a[1])
+if(a=s[f],r!=f&&(n=a[0],a[0]=t[0],t[0]=n,n=a[1],a[1]=t[1],t[1]=n,n=a[2],a[2]=t[2],t[2]=n,n=a[3],a[3]=t[3],t[3]=n),o!=u){for(O[u]=c+r>>1,n=u+1;n<o;n++)O[n]=r
+u=o,c=r}}for(O[u]=c+b>>1,n=u+1;n<256;n++)O[n]=b}function o(){var r,n,f,o,t,a,u,s,w,b,j,k,q,x
+for(p<Q&&(h=1),e=30+(h-1)/3,k=y,q=0,x=p,j=p/(3*h),b=j/g|0,s=I,a=F,u=a>>D,u<=1&&(u=0),r=0;r<u;r++)S[r]=s*((u*u-r*r)*K/(u*u))
+for(w=p<Q?3:p%A!==0?3*A:p%l!==0?3*l:p%m!==0?3*m:3*N,r=0;r<j;)if(f=(255&k[q+0])<<d,o=(255&k[q+1])<<d,t=(255&k[q+2])<<d,n=v(f,o,t),i(s,n,f,o,t),0!==u&&c(u,n,f,o,t),q+=w,q>=x&&(q-=p),r++,0===b&&(b=1),r%b===0)for(s-=s/e,a-=a/G,u=a>>D,u<=1&&(u=0),n=0;n<u;n++)S[n]=s*((u*u-n*n)*K/(u*u))}function t(r,n,f){var o,t,a,u,c,i,v
+for(c=1e3,v=-1,o=O[n],t=o-1;o<w||t>=0;)o<w&&(i=s[o],a=i[1]-n,a>=c?o=w:(o++,a<0&&(a=-a),u=i[0]-r,u<0&&(u=-u),a+=u,a<c&&(u=i[2]-f,u<0&&(u=-u),a+=u,a<c&&(c=a,v=i[3])))),t>=0&&(i=s[t],a=n-i[1],a>=c?t=-1:(t--,a<0&&(a=-a),u=i[0]-r,u<0&&(u=-u),a+=u,a<c&&(u=i[2]-f,u<0&&(u=-u),a+=u,a<c&&(c=a,v=i[3]))))
+return v}function a(){return o(),u(),f(),n()}function u(){var r
+for(r=0;r<w;r++)s[r][0]>>=d,s[r][1]>>=d,s[r][2]>>=d,s[r][3]=r}function c(r,n,f,o,t){var a,u,c,i,v,e,y
+for(c=n-r,c<-1&&(c=-1),i=n+r,i>w&&(i=w),a=n+1,u=n-1,e=1;a<i||u>c;){if(v=S[e++],a<i){y=s[a++]
+try{y[0]-=v*(y[0]-f)/M|0,y[1]-=v*(y[1]-o)/M|0,y[2]-=v*(y[2]-t)/M|0}catch(r){}}if(u>c){y=s[u--]
+try{y[0]-=v*(y[0]-f)/M|0,y[1]-=v*(y[1]-o)/M|0,y[2]-=v*(y[2]-t)/M|0}catch(r){}}}}function i(r,n,f,o,t){var a=s[n],u=r/I
+a[0]-=u*(a[0]-f)|0,a[1]-=u*(a[1]-o)|0,a[2]-=u*(a[2]-t)|0}function v(r,n,f){var o,t,a,u,c,i,v,e,y,p
+for(e=~(1<<31),y=e,i=-1,v=i,o=0;o<w;o++)p=s[o],t=p[0]-r,t<0&&(t=-t),a=p[1]-n,a<0&&(a=-a),t+=a,a=p[2]-f,a<0&&(a=-a),t+=a,t<e&&(e=t,i=o),u=t-(P[o]>>j-d),u<y&&(y=u,v=o),c=R[o]>>x,R[o]-=c,P[o]+=c<<q
+return R[i]+=z,P[i]-=B,v}var e,y,p,h,s,w=256,A=499,l=491,m=487,N=503,Q=3*N,b=w-1,d=4,g=100,j=16,k=1<<j,q=10,x=10,z=k>>x,B=k<<q-x,C=w>>3,D=6,E=1<<D,F=C*E,G=30,H=10,I=1<<H,J=8,K=1<<J,L=H+J,M=1<<L,O=[],P=[],R=[],S=[]
+r.apply(this,arguments)
+var T={}
+return T.map=t,T.process=a,T}return r}()
diff --git a/www/external/angular-carousel.min.js b/www/external/angular-carousel.min.js
new file mode 100644
index 00000000..34327d30
--- /dev/null
+++ b/www/external/angular-carousel.min.js
@@ -0,0 +1,124 @@
+//Angular Carousel - Mobile friendly touch carousel for AngularJS
+//@version v0.3.12 - 2015-06-11
+//@link http://revolunet.github.com/angular-carousel
+//@author Julien Bouquillon <julien@revolunet.com>
+//@license MIT License, http://www.opensource.org/licenses/MIT
+//Angular touch carousel with CSS GPU accel and slide buffering
+//http://github.com/revolunet/angular-carousel
+
+// Modified by PP for mobile friendly touch, start without auto slide but enabled it if tapped
+// and logic to wait for an image to load before it slides to the next one
+//
+/* jshint ignore:start */
+/*global angular */
+
+angular.module("angular-carousel",["ngTouch","angular-carousel.shifty"]),angular.module("angular-carousel").directive("rnCarouselAutoSlide",["$interval","$timeout","imageLoadingDataShare","carouselUtils",function(t,e,n,r){return{restrict:"A",link:function(t,e,n){var i=function(){t.autoSlider&&(t.autoSlider=null)},o=function(){0==r.isStopped()&&t.autoSlide()},a=function(){r.setStop(!r.getStop()),r.isStopped()&&r.setIndex(t.carouselIndex),t.autoSlider?(r.setStop(!0),r.setIndex(t.carouselIndex),i()):(r.setStop(!1),o())}
+r.setStop(!1),t.$watch("carouselIndex",o),n.hasOwnProperty("rnCarouselPauseOnHover")&&"false"!==n.rnCarouselPauseOnHover&&(n.hasOwnProperty("rnPlatform")&&"desktop"==n.rnPlatform?(e.off("click",a),e.on("click",a)):(e.off("touchend",a),e.on("touchend",a))),t.$on("$destroy",function(){i(),e.off("touchend",a),e.off("click",a)})}}}]),angular.module("angular-carousel").directive("rnCarouselIndicators",["$parse",function(t){return{restrict:"A",scope:{slides:"=",index:"=rnCarouselIndex"},templateUrl:"carousel-indicators.html",link:function(e,n,r){var i=t(r.rnCarouselIndex)
+e.goToSlide=function(t){i.assign(e.$parent.$parent,t)}}}}]),angular.module("angular-carousel").run(["$templateCache",function(t){t.put("carousel-indicators.html",'<div class="rn-carousel-indicator">\n<span ng-repeat="slide in slides" ng-class="{active: $index==index}" ng-click="goToSlide($index)">●</span></div>')}]),function(){"use strict"
+angular.module("angular-carousel").service("DeviceCapabilities",function(){function t(){var t="transform",e="webkitTransform"
+return"undefined"!=typeof document.body.style[t]?["webkit","moz","o","ms"].every(function(e){var n="-"+e+"-transform"
+return"undefined"==typeof document.body.style[n]||(t=n,!1)}):t="undefined"!=typeof document.body.style[e]?"-webkit-transform":void 0,t}function e(){var t,e=document.createElement("p"),n={webkitTransform:"-webkit-transform",msTransform:"-ms-transform",transform:"transform"}
+document.body.insertBefore(e,null)
+for(var r in n)void 0!==e.style[r]&&(e.style[r]="translate3d(1px,1px,1px)",t=window.getComputedStyle(e).getPropertyValue(n[r]))
+return document.body.removeChild(e),void 0!==t&&t.length>0&&"none"!==t}return{has3d:e(),transformProperty:t()}}).service("computeCarouselSlideStyle",["DeviceCapabilities",function(t){return function(e,n,r){var i,o={display:"inline-block"},a=100*e+n,u=t.has3d?"translate3d("+a+"%, 0, 0)":"translate3d("+a+"%, 0)",s=(100-Math.abs(a))/100
+if(t.transformProperty)if("fadeAndSlide"==r)o[t.transformProperty]=u,i=0,Math.abs(a)<100&&(i=.3+.7*s),o.opacity=i
+else if("hexagon"==r){var c=100,l=0,f=60*(s-1)
+c=n<e*-100?100:0,l=n<e*-100?f:-f,o[t.transformProperty]=u+" rotateY("+l+"deg)",o[t.transformProperty+"-origin"]=c+"% 50%"}else if("zoom"==r){o[t.transformProperty]=u
+var d=1
+Math.abs(a)<100&&(d=1+2*(1-s)),o[t.transformProperty]+=" scale("+d+")",o[t.transformProperty+"-origin"]="50% 50%",i=0,Math.abs(a)<100&&(i=.3+.7*s),o.opacity=i}else o[t.transformProperty]=u
+else o["margin-left"]=a+"%"
+return o}}]).factory("carouselUtils",function(){var t=!1,e=0,n=0
+return{setDuration:function(t){e=t},getDuration:function(){return e},setStop:function(e){t=e,t&&console.log("Paused at "+n)},setIndex:function(t){n=t},getIndex:function(){return n},getStop:function(){return t},isStopped:function(){return t},hello:function(){}}}).service("createStyleString",function(){return function(t){var e=[]
+return angular.forEach(t,function(t,n){e.push(n+":"+t)}),e.join(";")}}).directive("rnCarousel",["$swipe","$window","$document","$parse","$compile","$timeout","$interval","computeCarouselSlideStyle","createStyleString","Tweenable","imageLoadingDataShare","carouselUtils",function(t,e,n,r,i,o,a,u,s,c,l,f){function d(t,e,n){var r=n
+return t.every(function(t,n){return!angular.equals(t,e)||(r=n,!1)}),r}var h=0
+e.requestAnimationFrame||e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame
+return{restrict:"A",scope:!0,compile:function(a,p){var g,m,v=a[0].querySelector("li"),w=v?v.attributes:[],y=!1,S=!1
+return["ng-repeat","data-ng-repeat","ng:repeat","x-ng-repeat"].every(function(t){var e=w[t]
+if(angular.isDefined(e)){var n=e.value.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),r=n[3]
+if(g=n[1],m=n[2],g)return angular.isDefined(p.rnCarouselBuffered)&&(S=!0,e.value=g+" in "+m+"|carouselSlice:carouselBufferIndex:carouselBufferSize",r&&(e.value+=" track by "+r)),y=!0,!1}return!0}),function(a,p,g,v){function w(){return p[0].querySelectorAll("ul[rn-carousel] > li")}function _(t){N=!0,F({x:t.clientX,y:t.clientY},t)}function I(t){var e=100*a.carouselBufferIndex+t
+angular.forEach(w(),function(t,n){t.style.cssText=s(u(n,e,z.transitionType))})}function x(t,e){if(void 0===t&&(t=a.carouselIndex),e=e||{},e.animate===!1||"none"===z.transitionType)return U=!1,R=t*-100,a.carouselIndex=t,void O()
+U=!0
+var n=new c
+n.tween({from:{x:R},to:{x:t*-100},duration:z.transitionDuration,easing:z.transitionEasing,step:function(t){I(t.x)},finish:function(){a.$apply(function(){a.carouselIndex=t,R=t*-100,O(),o(function(){U=!1},0,!1)})}})}function C(){var t=p[0].getBoundingClientRect()
+return t.width?t.width:t.right-t.left}function b(){Q=C()}function k(){j||(j=!0,n.bind("mouseup",_))}function M(){j&&(j=!1,n.unbind("mouseup",_))}function T(t,e){if(!(U||H.length<=1))return b(),L=p[0].querySelector("li").getBoundingClientRect().left,D=!0,A=t.x,!1}function P(t,e){var n,r
+if(k(),D&&(n=t.x,r=A-n,r>2||r<-2)){N=!0
+var i=R+100*-r/Q
+I(i)}return!1}function F(t,e,n){if((!e||N)&&(M(),D=!1,N=!1,B=A-t.x,0!==B&&!U))if(R+=100*-B/Q,z.isSequential){var i=z.moveTreshold*Q,o=-B,u=-Math[o>=0?"ceil":"floor"](o/Q),s=Math.abs(o)>i
+H&&u+a.carouselIndex>=H.length&&(u=H.length-1-a.carouselIndex),u+a.carouselIndex<0&&(u=-a.carouselIndex)
+var c=s?u:0
+B=a.carouselIndex+c,x(B),void 0!==g.rnCarouselOnInfiniteScrollRight&&0===u&&0!==a.carouselIndex&&(r(g.rnCarouselOnInfiniteScrollRight)(a),x(0)),void 0!==g.rnCarouselOnInfiniteScrollLeft&&0===u&&0===a.carouselIndex&&0===c&&(r(g.rnCarouselOnInfiniteScrollLeft)(a),x(H.length))}else a.$apply(function(){a.carouselIndex=parseInt(-R/100,10),O()})}function O(){var t=0,e=(a.carouselBufferSize-1)/2
+S?(t=a.carouselIndex<=e?0:H&&H.length<a.carouselBufferSize?0:H&&a.carouselIndex>H.length-a.carouselBufferSize?H.length-a.carouselBufferSize:a.carouselIndex-e,a.carouselBufferIndex=t,o(function(){I(R)},0,!1)):o(function(){I(R)},0,!1)}function $(){b(),x()}h++
+var D,A,B,q={transitionType:g.rnCarouselTransition||"slide",transitionEasing:g.rnCarouselEasing||"easeTo",transitionDuration:parseFloat(g.rnCarouselDuration,10)||300,isSequential:!0,autoSlideDuration:3,bufferSize:9,moveTreshold:.1,defaultIndex:0},z=angular.extend({},q),E=!1,R=0,N=!1,H=[],Q=null,L=null,j=!1,U=!1
+"true"!==g.rnSwipeDisabled&&t.bind(p,{start:T,move:P,end:F,cancel:function(t){F({},t)}}),a.nextSlide=function(t){if(1!=f.isStopped()){var e=a.carouselIndex+1
+e>H.length-1&&e--,1!=l.get()?x(e,t):a.autoSlide()}},a.prevSlide=function(t){var e=a.carouselIndex-1
+e<0&&(e=H.length-1),x(e,t)}
+var W=!0
+if(a.carouselIndex=0,y||(H=[],angular.forEach(w(),function(t,e){H.push({id:e})})),void 0!==g.rnCarouselControls){var Y=y?m.replace("::","")+".length - 1":H.length-1,G='<div class="rn-carousel-controls">\n <span class="rn-carousel-control rn-carousel-control-prev" ng-click="prevSlide()" ng-if="carouselIndex > 0"></span>\n <span class="rn-carousel-control rn-carousel-control-next" ng-click="nextSlide()" ng-if="carouselIndex < '+Y+'"></span>\n</div>'
+p.parent().append(i(angular.element(G))(a))}if(void 0!==g.rnCarouselAutoSlide){var V=parseFloat(g.rnCarouselAutoSlide,10)||z.autoSlideDuration
+f.setDuration(V),console.log("Setting duration - should only happen once"),a.autoSlide=function(){if(a.autoSlider&&(a.autoSlider=null),0==f.isStopped()){var t=f.getDuration(),e=1e3*t
+e<5&&(console.log("duration is too small at "+e+" making it to 5"),e=5),a.autoSlider=o(function(){a.nextSlide()},e)}else console.log("We are stopped, doing nothing")}}if(g.rnCarouselDefaultIndex){var X=r(g.rnCarouselDefaultIndex)
+z.defaultIndex=X(a.$parent)||0}var J=null
+if(g.rnCarouselIndex){var K=function(t){t<0||Z.assign(a.$parent,t)},Z=r(g.rnCarouselIndex)
+angular.isFunction(Z.assign)?(a.$watch("carouselIndex",function(t){K(t)}),a.$parent.$watch(function(){return Z(a.$parent)},function(t,e){J=t,void 0!==t&&null!==t&&(H&&H.length>0&&t>=H.length?(t=H.length-1,K(t)):H&&t<0&&(t=0,K(t)),U||x(t,{animate:!W}),W=!1)}),E=!0,z.defaultIndex&&x(z.defaultIndex,{animate:!W})):isNaN(g.rnCarouselIndex)||x(parseInt(g.rnCarouselIndex,10),{animate:!1})}else x(z.defaultIndex,{animate:!W}),W=!1
+if(g.rnCarouselLocked&&a.$watch(g.rnCarouselLocked,function(t,e){U=t===!0}),y){var tt=void 0!==g.rnCarouselDeepWatch
+a[tt?"$watch":"$watchCollection"](m,function(t,e){if(H=t,J&&(a.carouselIndex=J,J=null),tt&&angular.isArray(t)){var n=e[a.carouselIndex],r=d(t,n,a.carouselIndex)
+x(r,{animate:!1})}else x(a.carouselIndex,{animate:!1})},!0)}a.$on("$destroy",function(){M()}),a.carouselBufferIndex=0,a.carouselBufferSize=z.bufferSize
+var et=angular.element(e)
+et.bind("orientationchange",$),et.bind("resize",$),a.$on("$destroy",function(){M(),et.unbind("orientationchange",$),et.unbind("resize",$)})}}}}])}(),angular.module("angular-carousel.shifty",[]).factory("Tweenable",function(){return function(t){var e=function(){"use strict"
+function e(){}function n(t,e){var n
+for(n in t)Object.hasOwnProperty.call(t,n)&&e(n)}function r(t,e){return n(e,function(n){t[n]=e[n]}),t}function i(t,e){n(e,function(n){"undefined"==typeof t[n]&&(t[n]=e[n])})}function o(t,e,n,r,i,o,u){var s,c=(t-o)/i
+for(s in e)e.hasOwnProperty(s)&&(e[s]=a(n[s],r[s],f[u[s]],c))
+return e}function a(t,e,n,r){return t+(e-t)*n(r)}function u(t,e){var r=l.prototype.filter,i=t._filterArgs
+n(r,function(n){"undefined"!=typeof r[n][e]&&r[n][e].apply(t,i)})}function s(t,e,n,r,i,a,s,c,l){w=e+n,y=Math.min(v(),w),S=y>=w,_=n-(w-y),t.isPlaying()&&!S?(t._scheduleId=l(t._timeoutHandler,g),u(t,"beforeTween"),o(y,r,i,a,n,e,s),u(t,"afterTween"),c(r,t._attachment,_)):S&&(c(a,t._attachment,_),t.stop(!0))}function c(t,e){var r={}
+return"string"==typeof e?n(t,function(t){r[t]=e}):n(t,function(t){r[t]||(r[t]=e[t]||h)}),r}function l(t,e){this._currentState=t||{},this._configured=!1,this._scheduleFunction=d,"undefined"!=typeof e&&this.setConfig(e)}var f,d,h="linear",p=500,g=1e3/60,m=Date.now?Date.now:function(){return+new Date},v="undefined"!=typeof SHIFTY_DEBUG_NOW?SHIFTY_DEBUG_NOW:m
+d="undefined"!=typeof window?window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||window.mozCancelRequestAnimationFrame&&window.mozRequestAnimationFrame||setTimeout:setTimeout
+var w,y,S,_
+return l.prototype.tween=function(t){return this._isTweening?this:(void 0===t&&this._configured||this.setConfig(t),this._timestamp=v(),this._start(this.get(),this._attachment),this.resume())},l.prototype.setConfig=function(t){t=t||{},this._configured=!0,this._attachment=t.attachment,this._pausedAtTime=null,this._scheduleId=null,this._start=t.start||e,this._step=t.step||e,this._finish=t.finish||e,this._duration=t.duration||p,this._currentState=t.from||this.get(),this._originalState=this.get(),this._targetState=t.to||this.get()
+var n=this._currentState,r=this._targetState
+return i(r,n),this._easing=c(n,t.easing||h),this._filterArgs=[n,this._originalState,r,this._easing],u(this,"tweenCreated"),this},l.prototype.get=function(){return r({},this._currentState)},l.prototype.set=function(t){this._currentState=t},l.prototype.pause=function(){return this._pausedAtTime=v(),this._isPaused=!0,this},l.prototype.resume=function(){this._isPaused&&(this._timestamp+=v()-this._pausedAtTime),this._isPaused=!1,this._isTweening=!0
+var t=this
+return this._timeoutHandler=function(){s(t,t._timestamp,t._duration,t._currentState,t._originalState,t._targetState,t._easing,t._step,t._scheduleFunction)},this._timeoutHandler(),this},l.prototype.seek=function(t){return this._timestamp=v()-t,this.isPlaying()||(this._isTweening=!0,this._isPaused=!1,s(this,this._timestamp,this._duration,this._currentState,this._originalState,this._targetState,this._easing,this._step,this._scheduleFunction),this._timeoutHandler(),this.pause()),this},l.prototype.stop=function(n){return this._isTweening=!1,this._isPaused=!1,this._timeoutHandler=e,(t.cancelAnimationFrame||t.webkitCancelAnimationFrame||t.oCancelAnimationFrame||t.msCancelAnimationFrame||t.mozCancelRequestAnimationFrame||t.clearTimeout)(this._scheduleId),n&&(r(this._currentState,this._targetState),u(this,"afterTweenEnd"),this._finish.call(this,this._currentState,this._attachment)),this},l.prototype.isPlaying=function(){return this._isTweening&&!this._isPaused},l.prototype.setScheduleFunction=function(t){this._scheduleFunction=t},l.prototype.dispose=function(){var t
+for(t in this)this.hasOwnProperty(t)&&delete this[t]},l.prototype.filter={},l.prototype.formula={linear:function(t){return t}},f=l.prototype.formula,r(l,{now:v,each:n,tweenProps:o,tweenProp:a,applyFilter:u,shallowCopy:r,defaults:i,composeEasingObject:c}),t.Tweenable=l,l}()
+!function(){e.shallowCopy(e.prototype.formula,{easeInQuad:function(t){return Math.pow(t,2)},easeOutQuad:function(t){return-(Math.pow(t-1,2)-1)},easeInOutQuad:function(t){return(t/=.5)<1?.5*Math.pow(t,2):-.5*((t-=2)*t-2)},easeInCubic:function(t){return Math.pow(t,3)},easeOutCubic:function(t){return Math.pow(t-1,3)+1},easeInOutCubic:function(t){return(t/=.5)<1?.5*Math.pow(t,3):.5*(Math.pow(t-2,3)+2)},easeInQuart:function(t){return Math.pow(t,4)},easeOutQuart:function(t){return-(Math.pow(t-1,4)-1)},easeInOutQuart:function(t){return(t/=.5)<1?.5*Math.pow(t,4):-.5*((t-=2)*Math.pow(t,3)-2)},easeInQuint:function(t){return Math.pow(t,5)},easeOutQuint:function(t){return Math.pow(t-1,5)+1},easeInOutQuint:function(t){return(t/=.5)<1?.5*Math.pow(t,5):.5*(Math.pow(t-2,5)+2)},easeInSine:function(t){return-Math.cos(t*(Math.PI/2))+1},easeOutSine:function(t){return Math.sin(t*(Math.PI/2))},easeInOutSine:function(t){return-.5*(Math.cos(Math.PI*t)-1)},easeInExpo:function(t){return 0===t?0:Math.pow(2,10*(t-1))},easeOutExpo:function(t){return 1===t?1:-Math.pow(2,-10*t)+1},easeInOutExpo:function(t){return 0===t?0:1===t?1:(t/=.5)<1?.5*Math.pow(2,10*(t-1)):.5*(-Math.pow(2,-10*--t)+2)},easeInCirc:function(t){return-(Math.sqrt(1-t*t)-1)},easeOutCirc:function(t){return Math.sqrt(1-Math.pow(t-1,2))},easeInOutCirc:function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeOutBounce:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInBack:function(t){var e=1.70158
+return t*t*((e+1)*t-e)},easeOutBack:function(t){var e=1.70158
+return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack:function(t){var e=1.70158
+return(t/=.5)<1?.5*(t*t*(((e*=1.525)+1)*t-e)):.5*((t-=2)*t*(((e*=1.525)+1)*t+e)+2)},elastic:function(t){return-1*Math.pow(4,-8*t)*Math.sin((6*t-1)*(2*Math.PI)/2)+1},swingFromTo:function(t){var e=1.70158
+return(t/=.5)<1?.5*(t*t*(((e*=1.525)+1)*t-e)):.5*((t-=2)*t*(((e*=1.525)+1)*t+e)+2)},swingFrom:function(t){var e=1.70158
+return t*t*((e+1)*t-e)},swingTo:function(t){var e=1.70158
+return(t-=1)*t*((e+1)*t+e)+1},bounce:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},bouncePast:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?2-(7.5625*(t-=1.5/2.75)*t+.75):t<2.5/2.75?2-(7.5625*(t-=2.25/2.75)*t+.9375):2-(7.5625*(t-=2.625/2.75)*t+.984375)},easeFromTo:function(t){return(t/=.5)<1?.5*Math.pow(t,4):-.5*((t-=2)*Math.pow(t,3)-2)},easeFrom:function(t){return Math.pow(t,4)},easeTo:function(t){return Math.pow(t,.25)}})}(),function(){function t(t,e,n,r,i,o){function a(t){return((h*t+p)*t+g)*t}function u(t){return((m*t+v)*t+w)*t}function s(t){return(3*h*t+2*p)*t+g}function c(t){return 1/(200*t)}function l(t,e){return u(d(t,e))}function f(t){return t>=0?t:0-t}function d(t,e){var n,r,i,o,u,c
+for(i=t,c=0;c<8;c++){if(o=a(i)-t,f(o)<e)return i
+if(u=s(i),f(u)<1e-6)break
+i-=o/u}if(n=0,r=1,i=t,i<n)return n
+if(i>r)return r
+for(;n<r;){if(o=a(i),f(o-t)<e)return i
+t>o?n=i:r=i,i=.5*(r-n)+n}return i}var h=0,p=0,g=0,m=0,v=0,w=0
+return g=3*e,p=3*(r-e)-g,h=1-g-p,w=3*n,v=3*(i-n)-w,m=1-w-v,l(t,c(o))}function n(e,n,r,i){return function(o){return t(o,e,n,r,i,1)}}e.setBezierFunction=function(t,r,i,o,a){var u=n(r,i,o,a)
+return u.x1=r,u.y1=i,u.x2=o,u.y2=a,e.prototype.formula[t]=u},e.unsetBezierFunction=function(t){delete e.prototype.formula[t]}}(),function(){function t(t,n,r,i,o){return e.tweenProps(i,n,t,r,1,0,o)}var n=new e
+n._filterArgs=[],e.interpolate=function(r,i,o,a){var u=e.shallowCopy({},r),s=e.composeEasingObject(r,a||"linear")
+n.set({})
+var c=n._filterArgs
+c.length=0,c[0]=u,c[1]=r,c[2]=i,c[3]=s,e.applyFilter(n,"tweenCreated"),e.applyFilter(n,"beforeTween")
+var l=t(r,u,i,o,s)
+return e.applyFilter(n,"afterTween"),l}}(),function(t){function e(t,e){M.length=0
+var n,r=t.length
+for(n=0;n<r;n++)M.push("_"+e+"_"+n)
+return M}function n(t){var e=t.match(_)
+return e?(1===e.length||t[0].match(S))&&e.unshift(""):e=["",""],e.join(k)}function r(e){t.each(e,function(t){var n=e[t]
+"string"==typeof n&&n.match(b)&&(e[t]=i(n))})}function i(t){return s(b,t,o)}function o(t){var e=a(t)
+return"rgb("+e[0]+","+e[1]+","+e[2]+")"}function a(t){return t=t.replace(/#/,""),3===t.length&&(t=t.split(""),t=t[0]+t[0]+t[1]+t[1]+t[2]+t[2]),T[0]=u(t.substr(0,2)),T[1]=u(t.substr(2,2)),T[2]=u(t.substr(4,2)),T}function u(t){return parseInt(t,16)}function s(t,e,n){var r=e.match(t),i=e.replace(t,k)
+if(r)for(var o,a=r.length,u=0;u<a;u++)o=r.shift(),i=i.replace(k,n(o))
+return i}function c(t){return s(x,t,l)}function l(t){for(var e=t.match(I),n=e.length,r=t.match(C)[0],i=0;i<n;i++)r+=parseInt(e[i],10)+","
+return r=r.slice(0,-1)+")"}function f(r){var i={}
+return t.each(r,function(t){var o=r[t]
+if("string"==typeof o){var a=v(o)
+i[t]={formatString:n(o),chunkNames:e(a,t)}}}),i}function d(e,n){t.each(n,function(t){for(var r=e[t],i=v(r),o=i.length,a=0;a<o;a++)e[n[t].chunkNames[a]]=+i[a]
+delete e[t]})}function h(e,n){t.each(n,function(t){var r=e[t],i=p(e,n[t].chunkNames),o=g(i,n[t].chunkNames)
+r=m(n[t].formatString,o),e[t]=c(r)})}function p(t,e){for(var n,r={},i=e.length,o=0;o<i;o++)n=e[o],r[n]=t[n],delete t[n]
+return r}function g(t,e){P.length=0
+for(var n=e.length,r=0;r<n;r++)P.push(t[e[r]])
+return P}function m(t,e){for(var n=t,r=e.length,i=0;i<r;i++)n=n.replace(k,+e[i].toFixed(4))
+return n}function v(t){return t.match(I)}function w(e,n){t.each(n,function(t){for(var r=n[t],i=r.chunkNames,o=i.length,a=e[t].split(" "),u=a[a.length-1],s=0;s<o;s++)e[i[s]]=a[s]||u
+delete e[t]})}function y(e,n){t.each(n,function(t){for(var r=n[t],i=r.chunkNames,o=i.length,a="",u=0;u<o;u++)a+=" "+e[i[u]],delete e[i[u]]
+e[t]=a.substr(1)})}var S=/(\d|\-|\.)/,_=/([^\-0-9\.]+)/g,I=/[0-9.\-]+/g,x=new RegExp("rgb\\("+I.source+/,\s*/.source+I.source+/,\s*/.source+I.source+"\\)","g"),C=/^.*\(/,b=/#([0-9]|[a-f]){3,6}/gi,k="VAL",M=[],T=[],P=[]
+t.prototype.filter.token={tweenCreated:function(t,e,n,i){r(t),r(e),r(n),this._tokenData=f(t)},beforeTween:function(t,e,n,r){w(r,this._tokenData),d(t,this._tokenData),d(e,this._tokenData),d(n,this._tokenData)},afterTween:function(t,e,n,r){h(t,this._tokenData),h(e,this._tokenData),h(n,this._tokenData),y(r,this._tokenData)}}}(e)}(window),window.Tweenable}),function(){"use strict"
+angular.module("angular-carousel").filter("carouselSlice",function(){return function(t,e,n){return angular.isArray(t)?t.slice(e,e+n):angular.isObject(t)?t:void 0}})}()
diff --git a/www/external/angular-circular-navigation.min.js b/www/external/angular-circular-navigation.min.js
new file mode 100644
index 00000000..3b466c2f
--- /dev/null
+++ b/www/external/angular-circular-navigation.min.js
@@ -0,0 +1,5 @@
+// https://github.com/maxklenk/angular-circular-navigation/blob/master/LICENSE
+// PP - Modified to show at right angles
+!function(){"use strict"
+var o=window.angular?window.angular:"undefined"!=typeof require?require("angular"):void 0,n=o.module("angularCircularNavigation",[]).directive("circular",["$compile",function(o){return{restrict:"EA",scope:{options:"="},template:'<button ng-click="toggleMenu()" class="cn-button {{options.button.size}}" ng-class="options.button.cssClass" style="background: {{options.button.background ? options.button.background : options.background}}; color: {{options.button.color ? options.button.color :options.color}};">{{options.content}}</button><div class="cn-wrapper {{options.size}} items-{{options.items.length}}" ng-class="{\'opened-nav\':options.isOpen}"><ul><li ng-repeat="item in options.items"><a ng-hide="item.empty" ng-click="perform(options, item)" ng-class="{\'is-active\': item.isActive}" class="{{item.cssClass}}" style="background: {{item.background ? item.background : options.background}}; color: {{item.color ? item.color : options.color}};"><span>{{item.content}}</span></a></li></ul></div>',controller:["$scope","$element","$attrs",function(o,n,t){o.toggleMenu=function(){"function"==typeof o.options.button.onclick?o.options.button.onclick():o.options.isOpen=!o.options.isOpen},o.perform=function(n,t){"function"==typeof t.onclick&&t.onclick(n,t),o.options.toggleOnClick&&o.toggleMenu()}}]}}])
+"function"==typeof define&&define.amd?define("circular",["angular"],n):"undefined"!=typeof exports&&"undefined"!=typeof module&&(module.exports=n)}()
diff --git a/www/external/canvas-toBlob.min.js b/www/external/canvas-toBlob.min.js
new file mode 100644
index 00000000..6651861b
--- /dev/null
+++ b/www/external/canvas-toBlob.min.js
@@ -0,0 +1,17 @@
+/* canvas-toBlob.js
+ * A canvas.toBlob() implementation.
+ * 2013-12-27
+ *
+ * By Eli Grey, http://eligrey.com and Devin Samarin, https://github.com/eboyjr
+ * License: MIT
+ * See https://github.com/eligrey/canvas-toBlob.js/blob/master/LICENSE.md
+ */
+!function(t){"use strict"
+var o,e=t.Uint8Array,n=t.HTMLCanvasElement,i=n&&n.prototype,s=/\s*;\s*base64\s*(?:;|$)/i,a="toDataURL",l=function(t){for(var n,i,s,a=t.length,l=new e(a/4*3|0),r=0,b=0,d=[0,0],f=0,c=0;a--;)i=t.charCodeAt(r++),n=o[i-43],255!==n&&n!==s&&(d[1]=d[0],d[0]=i,c=c<<6|n,f++,4===f&&(l[b++]=c>>>16,61!==d[1]&&(l[b++]=c>>>8),61!==d[0]&&(l[b++]=c),f=0))
+return l}
+e&&(o=new e([62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,0,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51])),n&&!i.toBlob&&(i.toBlob=function(t,o){if(o||(o="image/png"),this.mozGetAsFile)return void t(this.mozGetAsFile("canvas",o))
+if(this.msToBlob&&/^\s*image\/png\s*(?:$|;)/i.test(o))return void t(this.msToBlob())
+var n,i=Array.prototype.slice.call(arguments,1),r=this[a].apply(this,i),b=r.indexOf(","),d=r.substring(b+1),f=s.test(r.substring(0,b))
+Blob.fake?(n=new Blob,f?n.encoding="base64":n.encoding="URI",n.data=d,n.size=d.length):e&&(n=f?new Blob([l(d)],{type:o}):new Blob([decodeURIComponent(d)],{type:o})),t(n)},i.toDataURLHD?i.toBlobHD=function(){a="toDataURLHD"
+var t=this.toBlob()
+return a="toDataURL",t}:i.toBlobHD=i.toBlob)}("undefined"!=typeof self&&self||"undefined"!=typeof window&&window||this.content||this)
diff --git a/www/external/draggabilly.pkgd.min.js b/www/external/draggabilly.pkgd.min.js
new file mode 100644
index 00000000..93e9bdcb
--- /dev/null
+++ b/www/external/draggabilly.pkgd.min.js
@@ -0,0 +1,96 @@
+/*!
+ * Draggabilly PACKAGED v2.1.0
+ * Make that shiz draggable
+ * http://draggabilly.desandro.com
+ * MIT license
+ */
+!function(t,i){"function"==typeof define&&define.amd?define("jquery-bridget/jquery-bridget",["jquery"],function(e){i(t,e)}):"object"==typeof module&&module.exports?module.exports=i(t,require("jquery")):t.jQueryBridget=i(t,t.jQuery)}(window,function(t,i){function e(e,r,a){function h(t,i,n){var o,r="$()."+e+'("'+i+'")'
+return t.each(function(t,h){var d=a.data(h,e)
+if(!d)return void s(e+" not initialized. Cannot call methods, i.e. "+r)
+var u=d[i]
+if(!u||"_"==i.charAt(0))return void s(r+" is not a valid method")
+var p=u.apply(d,n)
+o=void 0===o?p:o}),void 0!==o?o:t}function d(t,i){t.each(function(t,n){var o=a.data(n,e)
+o?(o.option(i),o._init()):(o=new r(n,i),a.data(n,e,o))})}a=a||i||t.jQuery,a&&(r.prototype.option||(r.prototype.option=function(t){a.isPlainObject(t)&&(this.options=a.extend(!0,this.options,t))}),a.fn[e]=function(t){if("string"==typeof t){var i=o.call(arguments,1)
+return h(this,t,i)}return d(this,t),this},n(a))}function n(t){!t||t&&t.bridget||(t.bridget=e)}var o=Array.prototype.slice,r=t.console,s="undefined"==typeof r?function(){}:function(t){r.error(t)}
+return n(i||t.jQuery),e}),function(t,i){"function"==typeof define&&define.amd?define("get-size/get-size",[],function(){return i()}):"object"==typeof module&&module.exports?module.exports=i():t.getSize=i()}(window,function(){function t(t){var i=parseFloat(t),e=t.indexOf("%")==-1&&!isNaN(i)
+return e&&i}function i(){}function e(){for(var t={width:0,height:0,innerWidth:0,innerHeight:0,outerWidth:0,outerHeight:0},i=0;i<d;i++){var e=h[i]
+t[e]=0}return t}function n(t){var i=getComputedStyle(t)
+return i||a("Style returned "+i+". Are you running this code in a hidden iframe on Firefox? See http://bit.ly/getsizebug1"),i}function o(){if(!u){u=!0
+var i=document.createElement("div")
+i.style.width="200px",i.style.padding="1px 2px 3px 4px",i.style.borderStyle="solid",i.style.borderWidth="1px 2px 3px 4px",i.style.boxSizing="border-box"
+var e=document.body||document.documentElement
+e.appendChild(i)
+var o=n(i)
+r.isBoxSizeOuter=s=200==t(o.width),e.removeChild(i)}}function r(i){if(o(),"string"==typeof i&&(i=document.querySelector(i)),i&&"object"==typeof i&&i.nodeType){var r=n(i)
+if("none"==r.display)return e()
+var a={}
+a.width=i.offsetWidth,a.height=i.offsetHeight
+for(var u=a.isBorderBox="border-box"==r.boxSizing,p=0;p<d;p++){var c=h[p],f=r[c],g=parseFloat(f)
+a[c]=isNaN(g)?0:g}var l=a.paddingLeft+a.paddingRight,v=a.paddingTop+a.paddingBottom,m=a.marginLeft+a.marginRight,y=a.marginTop+a.marginBottom,b=a.borderLeftWidth+a.borderRightWidth,P=a.borderTopWidth+a.borderBottomWidth,E=u&&s,_=t(r.width)
+_!==!1&&(a.width=_+(E?0:l+b))
+var x=t(r.height)
+return x!==!1&&(a.height=x+(E?0:v+P)),a.innerWidth=a.width-(l+b),a.innerHeight=a.height-(v+P),a.outerWidth=a.width+m,a.outerHeight=a.height+y,a}}var s,a="undefined"==typeof console?i:function(t){console.error(t)},h=["paddingLeft","paddingRight","paddingTop","paddingBottom","marginLeft","marginRight","marginTop","marginBottom","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth"],d=h.length,u=!1
+return r}),function(t,i){"function"==typeof define&&define.amd?define("ev-emitter/ev-emitter",i):"object"==typeof module&&module.exports?module.exports=i():t.EvEmitter=i()}(this,function(){function t(){}var i=t.prototype
+return i.on=function(t,i){if(t&&i){var e=this._events=this._events||{},n=e[t]=e[t]||[]
+return n.indexOf(i)==-1&&n.push(i),this}},i.once=function(t,i){if(t&&i){this.on(t,i)
+var e=this._onceEvents=this._onceEvents||{},n=e[t]=e[t]||[]
+return n[i]=!0,this}},i.off=function(t,i){var e=this._events&&this._events[t]
+if(e&&e.length){var n=e.indexOf(i)
+return n!=-1&&e.splice(n,1),this}},i.emitEvent=function(t,i){var e=this._events&&this._events[t]
+if(e&&e.length){var n=0,o=e[n]
+i=i||[]
+for(var r=this._onceEvents&&this._onceEvents[t];o;){var s=r&&r[o]
+s&&(this.off(t,o),delete r[o]),o.apply(this,i),n+=s?0:1,o=e[n]}return this}},t}),function(t,i){"function"==typeof define&&define.amd?define("unipointer/unipointer",["ev-emitter/ev-emitter"],function(e){return i(t,e)}):"object"==typeof module&&module.exports?module.exports=i(t,require("ev-emitter")):t.Unipointer=i(t,t.EvEmitter)}(window,function(t,i){function e(){}function n(){}var o=n.prototype=Object.create(i.prototype)
+o.bindStartEvent=function(t){this._bindStartEvent(t,!0)},o.unbindStartEvent=function(t){this._bindStartEvent(t,!1)},o._bindStartEvent=function(i,e){e=void 0===e||!!e
+var n=e?"addEventListener":"removeEventListener"
+t.navigator.pointerEnabled?i[n]("pointerdown",this):t.navigator.msPointerEnabled?i[n]("MSPointerDown",this):(i[n]("mousedown",this),i[n]("touchstart",this))},o.handleEvent=function(t){var i="on"+t.type
+this[i]&&this[i](t)},o.getTouch=function(t){for(var i=0;i<t.length;i++){var e=t[i]
+if(e.identifier==this.pointerIdentifier)return e}},o.onmousedown=function(t){var i=t.button
+i&&0!==i&&1!==i||this._pointerDown(t,t)},o.ontouchstart=function(t){this._pointerDown(t,t.changedTouches[0])},o.onMSPointerDown=o.onpointerdown=function(t){this._pointerDown(t,t)},o._pointerDown=function(t,i){this.isPointerDown||(this.isPointerDown=!0,this.pointerIdentifier=void 0!==i.pointerId?i.pointerId:i.identifier,this.pointerDown(t,i))},o.pointerDown=function(t,i){this._bindPostStartEvents(t),this.emitEvent("pointerDown",[t,i])}
+var r={mousedown:["mousemove","mouseup"],touchstart:["touchmove","touchend","touchcancel"],pointerdown:["pointermove","pointerup","pointercancel"],MSPointerDown:["MSPointerMove","MSPointerUp","MSPointerCancel"]}
+return o._bindPostStartEvents=function(i){if(i){var e=r[i.type]
+e.forEach(function(i){t.addEventListener(i,this)},this),this._boundPointerEvents=e}},o._unbindPostStartEvents=function(){this._boundPointerEvents&&(this._boundPointerEvents.forEach(function(i){t.removeEventListener(i,this)},this),delete this._boundPointerEvents)},o.onmousemove=function(t){this._pointerMove(t,t)},o.onMSPointerMove=o.onpointermove=function(t){t.pointerId==this.pointerIdentifier&&this._pointerMove(t,t)},o.ontouchmove=function(t){var i=this.getTouch(t.changedTouches)
+i&&this._pointerMove(t,i)},o._pointerMove=function(t,i){this.pointerMove(t,i)},o.pointerMove=function(t,i){this.emitEvent("pointerMove",[t,i])},o.onmouseup=function(t){this._pointerUp(t,t)},o.onMSPointerUp=o.onpointerup=function(t){t.pointerId==this.pointerIdentifier&&this._pointerUp(t,t)},o.ontouchend=function(t){var i=this.getTouch(t.changedTouches)
+i&&this._pointerUp(t,i)},o._pointerUp=function(t,i){this._pointerDone(),this.pointerUp(t,i)},o.pointerUp=function(t,i){this.emitEvent("pointerUp",[t,i])},o._pointerDone=function(){this.isPointerDown=!1,delete this.pointerIdentifier,this._unbindPostStartEvents(),this.pointerDone()},o.pointerDone=e,o.onMSPointerCancel=o.onpointercancel=function(t){t.pointerId==this.pointerIdentifier&&this._pointerCancel(t,t)},o.ontouchcancel=function(t){var i=this.getTouch(t.changedTouches)
+i&&this._pointerCancel(t,i)},o._pointerCancel=function(t,i){this._pointerDone(),this.pointerCancel(t,i)},o.pointerCancel=function(t,i){this.emitEvent("pointerCancel",[t,i])},n.getPointerPoint=function(t){return{x:t.pageX,y:t.pageY}},n}),function(t,i){"function"==typeof define&&define.amd?define("unidragger/unidragger",["unipointer/unipointer"],function(e){return i(t,e)}):"object"==typeof module&&module.exports?module.exports=i(t,require("unipointer")):t.Unidragger=i(t,t.Unipointer)}(window,function(t,i){function e(){}function n(){}var o=n.prototype=Object.create(i.prototype)
+o.bindHandles=function(){this._bindHandles(!0)},o.unbindHandles=function(){this._bindHandles(!1)}
+var r=t.navigator
+return o._bindHandles=function(t){t=void 0===t||!!t
+var i
+i=r.pointerEnabled?function(i){i.style.touchAction=t?"none":""}:r.msPointerEnabled?function(i){i.style.msTouchAction=t?"none":""}:e
+for(var n=t?"addEventListener":"removeEventListener",o=0;o<this.handles.length;o++){var s=this.handles[o]
+this._bindStartEvent(s,t),i(s),s[n]("click",this)}},o.pointerDown=function(t,i){if("INPUT"==t.target.nodeName&&"range"==t.target.type)return this.isPointerDown=!1,void delete this.pointerIdentifier
+this._dragPointerDown(t,i)
+var e=document.activeElement
+e&&e.blur&&e.blur(),this._bindPostStartEvents(t),this.emitEvent("pointerDown",[t,i])},o._dragPointerDown=function(t,e){this.pointerDownPoint=i.getPointerPoint(e)
+var n=this.canPreventDefaultOnPointerDown(t,e)
+n&&t.preventDefault()},o.canPreventDefaultOnPointerDown=function(t){return"SELECT"!=t.target.nodeName},o.pointerMove=function(t,i){var e=this._dragPointerMove(t,i)
+this.emitEvent("pointerMove",[t,i,e]),this._dragMove(t,i,e)},o._dragPointerMove=function(t,e){var n=i.getPointerPoint(e),o={x:n.x-this.pointerDownPoint.x,y:n.y-this.pointerDownPoint.y}
+return!this.isDragging&&this.hasDragStarted(o)&&this._dragStart(t,e),o},o.hasDragStarted=function(t){return Math.abs(t.x)>3||Math.abs(t.y)>3},o.pointerUp=function(t,i){this.emitEvent("pointerUp",[t,i]),this._dragPointerUp(t,i)},o._dragPointerUp=function(t,i){this.isDragging?this._dragEnd(t,i):this._staticClick(t,i)},o._dragStart=function(t,e){this.isDragging=!0,this.dragStartPoint=i.getPointerPoint(e),this.isPreventingClicks=!0,this.dragStart(t,e)},o.dragStart=function(t,i){this.emitEvent("dragStart",[t,i])},o._dragMove=function(t,i,e){this.isDragging&&this.dragMove(t,i,e)},o.dragMove=function(t,i,e){t.preventDefault(),this.emitEvent("dragMove",[t,i,e])},o._dragEnd=function(t,i){this.isDragging=!1,setTimeout(function(){delete this.isPreventingClicks}.bind(this)),this.dragEnd(t,i)},o.dragEnd=function(t,i){this.emitEvent("dragEnd",[t,i])},o.onclick=function(t){this.isPreventingClicks&&t.preventDefault()},o._staticClick=function(t,i){if(!this.isIgnoringMouseUp||"mouseup"!=t.type){var e=t.target.nodeName
+"INPUT"!=e&&"TEXTAREA"!=e||t.target.focus(),this.staticClick(t,i),"mouseup"!=t.type&&(this.isIgnoringMouseUp=!0,setTimeout(function(){delete this.isIgnoringMouseUp}.bind(this),400))}},o.staticClick=function(t,i){this.emitEvent("staticClick",[t,i])},n.getPointerPoint=i.getPointerPoint,n}),function(t,i){"function"==typeof define&&define.amd?define(["get-size/get-size","unidragger/unidragger"],function(e,n){return i(t,e,n)}):"object"==typeof module&&module.exports?module.exports=i(t,require("get-size"),require("unidragger")):t.Draggabilly=i(t,t.getSize,t.Unidragger)}(window,function(t,i,e){function n(){}function o(t,i){for(var e in i)t[e]=i[e]
+return t}function r(t){return t instanceof HTMLElement}function s(t,i){this.element="string"==typeof t?h.querySelector(t):t,f&&(this.$element=f(this.element)),this.options=o({},this.constructor.defaults),this.option(i),this._create()}function a(t,i,e){return e=e||"round",i?Math[e](t/i)*i:t}var h=t.document,d=t.requestAnimationFrame||t.webkitRequestAnimationFrame||t.mozRequestAnimationFrame,u=0
+d||(d=function(t){var i=(new Date).getTime(),e=Math.max(0,16-(i-u)),n=setTimeout(t,e)
+return u=i+e,n})
+var p=h.documentElement,c="string"==typeof p.style.transform?"transform":"WebkitTransform",f=t.jQuery,g=s.prototype=Object.create(e.prototype)
+return s.defaults={},g.option=function(t){o(this.options,t)},g._create=function(){this.position={},this._getPosition(),this.startPoint={x:0,y:0},this.dragPoint={x:0,y:0},this.startPosition=o({},this.position)
+var t=getComputedStyle(this.element)
+"relative"!=t.position&&"absolute"!=t.position&&(this.element.style.position="relative"),this.enable(),this.setHandles()},g.setHandles=function(){this.handles=this.options.handle?this.element.querySelectorAll(this.options.handle):[this.element],this.bindHandles()},g.dispatchEvent=function(i,e,n){var o=[e].concat(n)
+this.emitEvent(i,o)
+var r=t.jQuery
+if(r&&this.$element)if(e){var s=r.Event(e)
+s.type=i,this.$element.trigger(s,n)}else this.$element.trigger(i,n)},s.prototype._getPosition=function(){var t=getComputedStyle(this.element),i=this._getPositionCoord(t.left,"width"),e=this._getPositionCoord(t.top,"height")
+this.position.x=isNaN(i)?0:i,this.position.y=isNaN(e)?0:e,this._addTransformPosition(t)},s.prototype._getPositionCoord=function(t,e){if(t.indexOf("%")!=-1){var n=i(this.element.parentNode)
+return parseFloat(t)/100*n[e]}return parseInt(t,10)},g._addTransformPosition=function(t){var i=t[c]
+if(0===i.indexOf("matrix")){var e=i.split(","),n=0===i.indexOf("matrix3d")?12:4,o=parseInt(e[n],10),r=parseInt(e[n+1],10)
+this.position.x+=o,this.position.y+=r}},g.pointerDown=function(t,i){this._dragPointerDown(t,i)
+var e=h.activeElement
+e&&e.blur&&e!=h.body&&e.blur(),this._bindPostStartEvents(t),this.element.classList.add("is-pointer-down"),this.dispatchEvent("pointerDown",t,[i])},g.pointerMove=function(t,i){var e=this._dragPointerMove(t,i)
+this.dispatchEvent("pointerMove",t,[i,e]),this._dragMove(t,i,e)},g.dragStart=function(t,i){this.isEnabled&&(this._getPosition(),this.measureContainment(),this.startPosition.x=this.position.x,this.startPosition.y=this.position.y,this.setLeftTop(),this.dragPoint.x=0,this.dragPoint.y=0,this.element.classList.add("is-dragging"),this.dispatchEvent("dragStart",t,[i]),this.animate())},g.measureContainment=function(){var t=this.options.containment
+if(t){var e=r(t)?t:"string"==typeof t?h.querySelector(t):this.element.parentNode,n=i(this.element),o=i(e),s=this.element.getBoundingClientRect(),a=e.getBoundingClientRect(),d=o.borderLeftWidth+o.borderRightWidth,u=o.borderTopWidth+o.borderBottomWidth,p=this.relativeStartPosition={x:s.left-(a.left+o.borderLeftWidth),y:s.top-(a.top+o.borderTopWidth)}
+this.containSize={width:o.width-d-p.x-n.width,height:o.height-u-p.y-n.height}}},g.dragMove=function(t,i,e){if(this.isEnabled){var n=e.x,o=e.y,r=this.options.grid,s=r&&r[0],h=r&&r[1]
+n=a(n,s),o=a(o,h),n=this.containDrag("x",n,s),o=this.containDrag("y",o,h),n="y"==this.options.axis?0:n,o="x"==this.options.axis?0:o,this.position.x=this.startPosition.x+n,this.position.y=this.startPosition.y+o,this.dragPoint.x=n,this.dragPoint.y=o,this.dispatchEvent("dragMove",t,[i,e])}},g.containDrag=function(t,i,e){if(!this.options.containment)return i
+var n="x"==t?"width":"height",o=this.relativeStartPosition[t],r=a(-o,e,"ceil"),s=this.containSize[n]
+return s=a(s,e,"floor"),Math.min(s,Math.max(r,i))},g.pointerUp=function(t,i){this.element.classList.remove("is-pointer-down"),this.dispatchEvent("pointerUp",t,[i]),this._dragPointerUp(t,i)},g.dragEnd=function(t,i){this.isEnabled&&(c&&(this.element.style[c]="",this.setLeftTop()),this.element.classList.remove("is-dragging"),this.dispatchEvent("dragEnd",t,[i]))},g.animate=function(){if(this.isDragging){this.positionDrag()
+var t=this
+d(function(){t.animate()})}},g.setLeftTop=function(){this.element.style.left=this.position.x+"px",this.element.style.top=this.position.y+"px"},g.positionDrag=function(){this.element.style[c]="translate3d( "+this.dragPoint.x+"px, "+this.dragPoint.y+"px, 0)"},g.staticClick=function(t,i){this.dispatchEvent("staticClick",t,[i])},g.enable=function(){this.isEnabled=!0},g.disable=function(){this.isEnabled=!1,this.isDragging&&this.dragEnd()},g.destroy=function(){this.disable(),this.element.style[c]="",this.element.style.left="",this.element.style.top="",this.element.style.position="",this.unbindHandles(),this.$element&&this.$element.removeData("draggabilly")},g._init=n,f&&f.bridget&&f.bridget("draggabilly",s),s})
diff --git a/www/external/gifwriter.min.js b/www/external/gifwriter.min.js
new file mode 100644
index 00000000..427fd50f
--- /dev/null
+++ b/www/external/gifwriter.min.js
@@ -0,0 +1,64 @@
+// (c) Dean McNamee <dean@gmail.com>, 2013.
+// //
+// // https://github.com/deanm/omggif
+// //
+// // Permission is hereby granted, free of charge, to any person obtaining a copy
+// // of this software and associated documentation files (the "Software"), to
+// // deal in the Software without restriction, including without limitation the
+// // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// // sell copies of the Software, and to permit persons to whom the Software is
+// // furnished to do so, subject to the following conditions:
+// //
+// // The above copyright notice and this permission notice shall be included in
+// // all copies or substantial portions of the Software.
+// //
+// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// // IN THE SOFTWARE.
+// //
+// // omggif is a JavaScript implementation of a GIF 89a encoder and decoder,
+// // including animation and compression. It does not rely on any specific
+// // underlying system, so should run in the browser, Node, or Plask.
+// Main compression routine, palette indexes -> LZW code stream.
+// |index_stream| must have at least one entry.
+"use strict"
+function _classCallCheck(r,e){if(!(r instanceof e))throw new TypeError("Cannot call a class as a function")}function check_palette_and_num_colors(r){var e=r.length
+if(e<2||e>256||e&e-1)throw new Error("Invalid code/color length, must be power of 2 and 2 .. 256.")
+return e}function GifWriterOutputLZWCodeStream(r,e,t,a){function n(t){for(;s>=t;)r[e++]=255&v,v>>=8,s-=8,e===i+256&&(r[i]=255,i=e++)}function o(r){v|=r<<s,s+=c,n(8)}r[e++]=t
+var i=e++,l=1<<t,u=l-1,f=l+1,d=f+1,c=t+1,s=0,v=0,h=a[0]&u,p={}
+o(l)
+for(var w=1,g=a.length;w<g;++w){var _=a[w]&u,y=h<<8|_,m=p[y]
+if(void 0===m){for(v|=h<<s,s+=c;s>=8;)r[e++]=255&v,v>>=8,s-=8,e===i+256&&(r[i]=255,i=e++)
+4096===d?(o(l),d=f+1,c=t+1,p={}):(d>=1<<c&&++c,p[y]=d++),h=_}else h=m}return o(h),o(f),n(1),i+1===e?r[i]=0:(r[i]=e-i-1,r[e++]=0),e}var GifWriter=function(){function r(e,t,a){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{}
+_classCallCheck(this,r)
+var o=n.loop,i=n.palette,l=0,u=[],f=i
+if(t<=0||a<=0||t>65535||a>65535)throw new Error("Width/Height invalid.")
+u[l++]=71,u[l++]=73,u[l++]=70,u[l++]=56,u[l++]=57,u[l++]=97
+var d=0,c=0
+if(f){for(var s=check_palette_and_num_colors(f);s>>=1;)++d
+if(s=1<<d,d--,void 0!==n.background){if(c=n.background,c>=s)throw new Error("Background index out of range.")
+if(0===c)throw new Error("Background index explicitly passed as 0.")}}if(u[l++]=255&t,u[l++]=t>>8&255,u[l++]=255&a,u[l++]=a>>8&255,u[l++]=(f?128:0)|d,u[l++]=c,u[l++]=0,f)for(var v=f,h=Array.isArray(v),p=0,v=h?v:v[Symbol.iterator]();;){var w
+if(h){if(p>=v.length)break
+w=v[p++]}else{if(p=v.next(),p.done)break
+w=p.value}var g=w
+u[l++]=g>>16&255,u[l++]=g>>8&255,u[l++]=255&g}if(Number.isInteger(o)){if(o<0||o>65535)throw"Loop count invalid."
+u[l++]=33,u[l++]=255,u[l++]=11,u[l++]=78,u[l++]=69,u[l++]=84,u[l++]=83,u[l++]=67,u[l++]=65,u[l++]=80,u[l++]=69,u[l++]=50,u[l++]=46,u[l++]=48,u[l++]=3,u[l++]=1,u[l++]=255&o,u[l++]=o>>8&255,u[l++]=0}var _=this,y=e.getReader()
+return new ReadableStream({start:function(r){r.enqueue(new Uint8Array(u))},pull:function(r){return y.read().then(function(e){var t=e.done,a=e.value
+return t?(r.enqueue(new Uint8Array([59])),void r.close()):void _.addFrame.apply(_,[r].concat(a))})}})}return r.prototype.addFrame=function(r,e,t,a,n,o){var i=arguments.length>6&&void 0!==arguments[6]?arguments[6]:{},l=0,u=[]
+if(e<0||t<0||e>65535||t>65535)throw new Error("x/y invalid.")
+if(a<=0||n<=0||a>65535||n>65535)throw"Width/Height invalid."
+if(o.length<a*n)throw"Not enough pixels for the frame size."
+var f=!0,d=i.palette
+if(void 0!==d&&null!==d||(f=!1,d=global_palette),void 0===d||null===d)throw"Must supply either a local or global palette."
+for(var c=check_palette_and_num_colors(d),s=0;c>>=1;)++s
+c=1<<s
+var v=void 0===i.delay?0:i.delay,h=void 0===i.disposal?0:i.disposal
+if(h<0||h>3)throw"Disposal out of range."
+var p=!1,w=0
+if(void 0!==i.transparent&&null!==i.transparent&&(p=!0,w=i.transparent,w<0||w>=c))throw"Transparent color index."
+if((0!==h||p||0!==v)&&(u[l++]=33,u[l++]=249,u[l++]=4,u[l++]=h<<2|(p===!0?1:0),u[l++]=255&v,u[l++]=v>>8&255,u[l++]=w,u[l++]=0),u[l++]=44,u[l++]=255&e,u[l++]=e>>8&255,u[l++]=255&t,u[l++]=t>>8&255,u[l++]=255&a,u[l++]=a>>8&255,u[l++]=255&n,u[l++]=n>>8&255,u[l++]=f===!0?128|s-1:0,f===!0)for(var g=0,_=d.length;g<_;++g){var y=d[g]
+u[l++]=y>>16&255,u[l++]=y>>8&255,u[l++]=255&y}GifWriterOutputLZWCodeStream(u,l,s<2?2:s,o),r.enqueue(new Uint8Array(u))},r}()
diff --git a/www/external/imagesloaded.pkgd.min.js b/www/external/imagesloaded.pkgd.min.js
new file mode 100644
index 00000000..aa190be5
--- /dev/null
+++ b/www/external/imagesloaded.pkgd.min.js
@@ -0,0 +1,42 @@
+/*!
+ * imagesLoaded PACKAGED v4.1.0
+ * JavaScript is all like "You images are done yet or what?"
+ * MIT License
+ */
+!function(t,e){"function"==typeof define&&define.amd?define("ev-emitter/ev-emitter",e):"object"==typeof module&&module.exports?module.exports=e():t.EvEmitter=e()}(this,function(){function t(){}var e=t.prototype
+return e.on=function(t,e){if(t&&e){var i=this._events=this._events||{},n=i[t]=i[t]||[]
+return n.indexOf(e)==-1&&n.push(e),this}},e.once=function(t,e){if(t&&e){this.on(t,e)
+var i=this._onceEvents=this._onceEvents||{},n=i[t]=i[t]||[]
+return n[e]=!0,this}},e.off=function(t,e){var i=this._events&&this._events[t]
+if(i&&i.length){var n=i.indexOf(e)
+return n!=-1&&i.splice(n,1),this}},e.emitEvent=function(t,e){var i=this._events&&this._events[t]
+if(i&&i.length){var n=0,o=i[n]
+e=e||[]
+for(var r=this._onceEvents&&this._onceEvents[t];o;){var s=r&&r[o]
+s&&(this.off(t,o),delete r[o]),o.apply(this,e),n+=s?0:1,o=i[n]}return this}},t}),function(t,e){"use strict"
+"function"==typeof define&&define.amd?define(["ev-emitter/ev-emitter"],function(i){return e(t,i)}):"object"==typeof module&&module.exports?module.exports=e(t,require("ev-emitter")):t.imagesLoaded=e(t,t.EvEmitter)}(window,function(t,e){function i(t,e){for(var i in e)t[i]=e[i]
+return t}function n(t){var e=[]
+if(Array.isArray(t))e=t
+else if("number"==typeof t.length)for(var i=0;i<t.length;i++)e.push(t[i])
+else e.push(t)
+return e}function o(t,e,r){return this instanceof o?("string"==typeof t&&(t=document.querySelectorAll(t)),this.elements=n(t),this.options=i({},this.options),"function"==typeof e?r=e:i(this.options,e),r&&this.on("always",r),this.getImages(),h&&(this.jqDeferred=new h.Deferred),void setTimeout(function(){this.check()}.bind(this))):new o(t,e,r)}function r(t){this.img=t}function s(t,e){this.url=t,this.element=e,this.img=new Image}var h=t.jQuery,a=t.console
+o.prototype=Object.create(e.prototype),o.prototype.options={},o.prototype.getImages=function(){this.images=[],this.elements.forEach(this.addElementImages,this)},o.prototype.addElementImages=function(t){"IMG"==t.nodeName&&this.addImage(t),this.options.background===!0&&this.addElementBackgroundImages(t)
+var e=t.nodeType
+if(e&&d[e]){for(var i=t.querySelectorAll("img"),n=0;n<i.length;n++){var o=i[n]
+this.addImage(o)}if("string"==typeof this.options.background){var r=t.querySelectorAll(this.options.background)
+for(n=0;n<r.length;n++){var s=r[n]
+this.addElementBackgroundImages(s)}}}}
+var d={1:!0,9:!0,11:!0}
+return o.prototype.addElementBackgroundImages=function(t){var e=getComputedStyle(t)
+if(e)for(var i=/url\((['"])?(.*?)\1\)/gi,n=i.exec(e.backgroundImage);null!==n;){var o=n&&n[2]
+o&&this.addBackground(o,t),n=i.exec(e.backgroundImage)}},o.prototype.addImage=function(t){var e=new r(t)
+this.images.push(e)},o.prototype.addBackground=function(t,e){var i=new s(t,e)
+this.images.push(i)},o.prototype.check=function(){function t(t,i,n){setTimeout(function(){e.progress(t,i,n)})}var e=this
+return this.progressedCount=0,this.hasAnyBroken=!1,this.images.length?void this.images.forEach(function(e){e.once("progress",t),e.check()}):void this.complete()},o.prototype.progress=function(t,e,i){this.progressedCount++,this.hasAnyBroken=this.hasAnyBroken||!t.isLoaded,this.emitEvent("progress",[this,t,e]),this.jqDeferred&&this.jqDeferred.notify&&this.jqDeferred.notify(this,t),this.progressedCount==this.images.length&&this.complete(),this.options.debug&&a&&a.log("progress: "+i,t,e)},o.prototype.complete=function(){var t=this.hasAnyBroken?"fail":"done"
+if(this.isComplete=!0,this.emitEvent(t,[this]),this.emitEvent("always",[this]),this.jqDeferred){var e=this.hasAnyBroken?"reject":"resolve"
+this.jqDeferred[e](this)}},r.prototype=Object.create(e.prototype),r.prototype.check=function(){var t=this.getIsImageComplete()
+return t?void this.confirm(0!==this.img.naturalWidth,"naturalWidth"):(this.proxyImage=new Image,this.proxyImage.addEventListener("load",this),this.proxyImage.addEventListener("error",this),this.img.addEventListener("load",this),this.img.addEventListener("error",this),void(this.proxyImage.src=this.img.src))},r.prototype.getIsImageComplete=function(){return this.img.complete&&void 0!==this.img.naturalWidth},r.prototype.confirm=function(t,e){this.isLoaded=t,this.emitEvent("progress",[this,this.img,e])},r.prototype.handleEvent=function(t){var e="on"+t.type
+this[e]&&this[e](t)},r.prototype.onload=function(){this.confirm(!0,"onload"),this.unbindEvents()},r.prototype.onerror=function(){this.confirm(!1,"onerror"),this.unbindEvents()},r.prototype.unbindEvents=function(){this.proxyImage.removeEventListener("load",this),this.proxyImage.removeEventListener("error",this),this.img.removeEventListener("load",this),this.img.removeEventListener("error",this)},s.prototype=Object.create(r.prototype),s.prototype.check=function(){this.img.addEventListener("load",this),this.img.addEventListener("error",this),this.img.src=this.url
+var t=this.getIsImageComplete()
+t&&(this.confirm(0!==this.img.naturalWidth,"naturalWidth"),this.unbindEvents())},s.prototype.unbindEvents=function(){this.img.removeEventListener("load",this),this.img.removeEventListener("error",this)},s.prototype.confirm=function(t,e){this.isLoaded=t,this.emitEvent("progress",[this,this.element,e])},o.makeJQueryPlugin=function(e){e=e||t.jQuery,e&&(h=e,h.fn.imagesLoaded=function(t,e){var i=new o(this,t,e)
+return i.jqDeferred.promise(h(this))})},o.makeJQueryPlugin(),o})
diff --git a/www/external/ion-pullup.min.js b/www/external/ion-pullup.min.js
new file mode 100644
index 00000000..047f0837
--- /dev/null
+++ b/www/external/ion-pullup.min.js
@@ -0,0 +1,10 @@
+angular.module("ionic-pullup",[]).constant("ionPullUpFooterState",{COLLAPSED:"COLLAPSED",MINIMIZED:"MINIMIZED",EXPANDED:"EXPANDED"}).constant("ionPullUpFooterBehavior",{HIDE:"HIDE",EXPAND:"EXPAND"}).directive("ionPullUpFooter",["$timeout","$rootScope","$window","$ionicPlatform",function(t,e,n,i){return{restrict:"AE",scope:{onExpand:"&",onCollapse:"&",onMinimize:"&"},controller:["$scope","$element","$attrs","ionPullUpFooterState","ionPullUpFooterBehavior",function(o,a,r,s,l){function u(){c(),a.css({transition:"300ms ease-in-out",padding:0}),P&&E&&a.css("bottom",P.offsetHeight+"px")}function c(){P=document.querySelector(".tabs"),E=document.querySelector(".tabs-bottom"),D=document.querySelector(".bar-header"),H=P?P.offsetHeight:0,x=D?D.offsetHeight:0}function p(){I.height=I.maxHeight>0?I.maxHeight:n.innerHeight-x-v-H,"ios"==e.platformOS&&(I.height-=60),"android"==e.platformOS&&(I.height-=40),a.css({height:I.height+"px"}),I.initialState==s.MINIMIZED?m():f()}function h(){t(function(){p()},300),a.css({transition:"none",padding:0})}function d(){c(),I.height=I.maxHeight>0?I.maxHeight:n.innerHeight-x-v-H,"ios"==e.platformOS&&(I.height-=60),"android"==e.platformOS&&(I.height-=40)}function g(){d(),I.lastPosY=0,a.css({height:I.height+"px","-webkit-transform":"translate3d(0, 0, 0)",transform:"translate3d(0, 0, 0)"}),a.css({transition:"300ms ease-in-out",padding:0}),o.onExpand(),I.state=s.EXPANDED}function f(){I.lastPosY=P&&E?I.height-H:I.height-I.defaultHeight,a.css({"-webkit-transform":"translate3d(0, "+I.lastPosY+"px, 0)",transform:"translate3d(0, "+I.lastPosY+"px, 0)"}),o.onCollapse(),I.state=s.COLLAPSED}function m(){I.lastPosY=I.height,a.css({"-webkit-transform":"translate3d(0, "+I.lastPosY+"px, 0)",transform:"translate3d(0, "+I.lastPosY+"px, 0)"}),o.onMinimize(),I.state=s.MINIMIZED}var P,E,D,H,x,v=0,I={height:0,posY:0,lastPosY:0,state:s.COLLAPSED,defaultHeight:a[0].offsetHeight,maxHeight:parseInt(r.maxHeight,10)||0,initialState:r.initialState?r.initialState.toUpperCase():s.COLLAPSED,defaultBehavior:r.defaultBehavior?r.defaultBehavior.toUpperCase():l.EXPAND}
+this.setHandleHeight=function(t){v=t,p()},this.getHeight=function(){return a[0].offsetHeight},this.getBackground=function(){return n.getComputedStyle(a[0]).background},this.onTap=function(t){t.gesture.srcEvent.preventDefault(),t.gesture.preventDefault(),I.state==s.COLLAPSED?I.defaultBehavior==l.HIDE?m():g():I.state==s.MINIMIZED?I.defaultBehavior==l.HIDE?f():g():I.initialState==s.MINIMIZED?m():f(),e.$broadcast("ionPullUp:tap",I.state)},this.onDrag=function(t){switch(t.gesture.srcEvent.preventDefault(),t.gesture.preventDefault(),t.type){case"dragstart":a.css("transition","none")
+break
+case"drag":if(I.posY=Math.round(t.gesture.deltaY)+I.lastPosY,I.posY<0||I.posY>I.height)return
+a.css({"-webkit-transform":"translate3d(0, "+I.posY+"px, 0)",transform:"translate3d(0, "+I.posY+"px, 0)"})
+break
+case"dragend":a.css({transition:"300ms ease-in-out"}),I.lastPosY=I.posY}},u(),i.ready(function(){n.addEventListener("orientationchange",h),i.on("resume",h)})}],compile:function(t,e){e.defaultHeight&&t.css("height",parseInt(e.defaultHeight,10)+"px"),t.addClass("bar bar-footer")}}}]).directive("ionPullUpContent",[function(){return{restrict:"AE",require:"^ionPullUpFooter",link:function(t,e,n,i){var o=i.getHeight()
+e.css({display:"block","margin-top":o+"px",width:"100%"}),n.scroll&&"TRUE"==n.scroll.toUpperCase()&&e.css({"overflow-y":"scroll","overflow-x":"hidden"})}}}]).directive("ionPullUpBar",[function(){return{restrict:"AE",require:"^ionPullUpFooter",link:function(t,e,n,i){var o=i.getHeight()
+e.css({display:"flex",height:o+"px",position:"absolute",right:"0",left:"0"})}}}]).directive("ionPullUpTrigger",["$ionicGesture",function(t){return{restrict:"AE",require:"^ionPullUpFooter",link:function(e,n,i,o){t.on("tap",o.onTap,n),t.on("drag dragstart dragend",o.onDrag,n)}}}]).directive("ionPullUpHandle",["$ionicGesture","$ionicPlatform","$timeout","$window",function(t,e,n,i){return{restrict:"AE",require:"^ionPullUpFooter",link:function(o,a,r,s){function l(){n(function(){a.css("left",(i.innerWidth-c)/2+"px")},300)}var u=parseInt(r.height,10)||25,c=parseInt(r.width,10)||100,p=s.getBackground(),h=r.toggle
+s.setHandleHeight(u),a.css({display:"block",background:p,position:"absolute",top:1-u+"px",left:(i.innerWidth-c)/2+"px",height:u+"px",width:c+"px","text-align":"center"}),t.on("tap",s.onTap,a),t.on("drag dragstart dragend",s.onDrag,a),o.$on("ionPullUp:tap",function(){a.find("i").toggleClass(h)}),e.ready(function(){i.addEventListener("orientationchange",l),e.on("resume",l)})}}}])
diff --git a/www/external/ionRadio.min.js b/www/external/ionRadio.min.js
new file mode 100644
index 00000000..d652fdda
--- /dev/null
+++ b/www/external/ionRadio.min.js
@@ -0,0 +1,12 @@
+/**
+ * ionRadioFix - fixes a bug in iOS 9 UIWebView that breaks the tilde selector in CSS. To
+ * use this fix, include it after your Ionic bundle JS.
+ *
+ * Note: due to Angular directive override limitations, you'll need to change any reference
+ * to <ion-radio> to <ion-radio-fix> to apply this patched radio button.
+ *
+ * Also, make sure to add the new CSS from the second part of this gist.
+ */
+angular.module("ionic").directive("ionRadioFix",function(){return{restrict:"E",replace:!0,require:"?ngModel",transclude:!0,template:'<label class="item item-radio"><input type="radio" name="radio-group"><div class="radio-content"><div class="item-content disable-pointer-events" ng-transclude></div><i class="radio-icon disable-pointer-events icon ion-checkmark"></i></div></label>',compile:function(e,n){if(n.icon){var i=e.find("i")
+i.removeClass("ion-checkmark").addClass(n.icon)}var a=e.find("input")
+return angular.forEach({name:n.name,value:n.value,disabled:n.disabled,"ng-value":n.ngValue,"ng-model":n.ngModel,"ng-disabled":n.ngDisabled,"ng-change":n.ngChange,"ng-required":n.ngRequired,required:n.required},function(e,n){angular.isDefined(e)&&a.attr(n,e)}),function(e,n,i){e.getValue=function(){return e.ngValue||i.value}}}}})
diff --git a/www/external/ionic.content.banner.min.js b/www/external/ionic.content.banner.min.js
new file mode 100644
index 00000000..62b317ce
--- /dev/null
+++ b/www/external/ionic.content.banner.min.js
@@ -0,0 +1,12 @@
+angular.module("jett.ionic.content.banner",["ionic"]),function(n){"use strict"
+n.module("jett.ionic.content.banner").directive("ionContentBanner",["$interval",function(n){return{restrict:"E",scope:!0,link:function(e,t){var o
+e.currentIndex=0,e.text.length>1&&(o=n(function(){e.currentIndex=e.currentIndex<e.text.length-1?e.currentIndex+1:0},e.interval)),e.$on("$destroy",function(){t.remove(),o&&n.cancel(o)})},template:'<div class="content-banner-text-wrapper"><div ng-repeat="item in text track by $index" ng-class="{active: $index === currentIndex}" class="content-banner-text" ng-bind="item"></div></div><button class="content-banner-close button button-icon icon {{::icon}}" ng-click="close()"></button>'}}])}(angular),function(n,e){"use strict"
+n.module("jett.ionic.content.banner").factory("$ionicContentBanner",["$document","$rootScope","$compile","$timeout","$ionicPlatform",function(t,o,r,i,c){function a(n){for(;null!==n&&"BODY"!==n.nodeName;){var e=n.getAttribute("nav-view")
+if(null!==e&&"cached"===e)return!1
+n=n.parentNode}return!0}function u(n){var e=n.querySelector('ion-modal-view[class*="ng-enter-active"]')
+if(null!=e&&e.getAttribute("class").indexOf("ng-leave")==-1)return e
+var t=n.querySelectorAll('ion-view[nav-view="active"]')
+return 1===t.length?t[0]:Array.prototype.slice.call(t).filter(function(n){return a(n)})[0]}function l(a){var l=o.$new(!0)
+n.extend(l,{icon:"ion-ios-close-empty",transition:"vertical",interval:7e3,type:"info",$deregisterBackButton:n.noop,closeOnStateChange:!0,autoClose:null},a)
+var s="content-banner "+l.type+" content-banner-transition-"+l.transition,d=l.element=r('<ion-content-banner class="'+s+'"></ion-content-banner>')(l),v=t[0].body,f=l.closeOnStateChange?o.$on("$stateChangeSuccess",function(){l.close()}):n.noop
+return l.$deregisterBackButton=c.registerBackButtonAction(function(){i(l.close)},300),l.close=function(){l.removed||(l.removed=!0,e.requestAnimationFrame(function(){d.removeClass("content-banner-in"),i(function(){l.$destroy(),d.remove(),v=f=null},400)}),l.$deregisterBackButton(),f())},l.show=function(){l.removed||(void 0!==u(v)&&u(v).querySelector(".scroll-content").appendChild(d[0]),e.requestAnimationFrame(function(){i(function(){d.addClass("content-banner-in"),l.autoClose&&i(function(){l.close()},l.autoClose,!1)},20,!1)}))},i(function(){l.show()},10,!1),l.close.$scope=l,l.close}return{show:l}}])}(angular,ionic)
diff --git a/www/external/ionic.scroll.sista.min.js b/www/external/ionic.scroll.sista.min.js
new file mode 100644
index 00000000..a49a1a3d
--- /dev/null
+++ b/www/external/ionic.scroll.sista.min.js
@@ -0,0 +1,28 @@
+/* global angular,ionic */
+!function(e,t){"use strict"
+e.module("jett.ionic.scroll.sista",["ionic"]).directive("scrollSista",["$document","$timeout","$rootScope","$ionicScrollDelegate","$ionicPlatform",function(a,n,r,o,i){function c(e,t,a,n){var r
+for(n=n||10;e.parentNode&&n--;){if(r=e.parentNode.getAttribute(t),r&&r===a)return e.parentNode
+e=e.parentNode}return null}var l=400,s=2*l,u=!t.Platform.isAndroid()
+return{restrict:"A",link:function(r,l,f){function d(e,a,r){r&&!e.style[t.CSS.TRANSITION_DURATION]&&(e.style[t.CSS.TRANSITION_DURATION]=r,n(function(){e.style[t.CSS.TRANSITION_DURATION]=""},s,!1)),e.style[t.CSS.TRANSFORM]="translate3d(0, "+-a+"px, 0)"}function b(){v=0,N=0,$=0}function h(){var e
+if(y=q.querySelector('[nav-bar="cached"] .bar-header'),g=q.querySelector('[nav-bar="active"] .bar-header'))switch(T=g.offsetHeight,i.ready(function(){i.is("ios")&&(V=!0,T-=20)}),A=T,I=q.querySelectorAll(".tabs"),I=I[I.length-1],I&&(k=I.offsetHeight,I.parentNode.classList.contains("tabs-top")?(F=!0,A+=k):I.parentNode.classList.contains("tabs-bottom")&&(L=!0)),e=c(l[0],"nav-view","active"),M=e&&e.querySelector(".bar-subheader"),M&&(R=M.offsetHeight,A+=R),x=2*A,O=w=C=x,k=k||0,R=R||0,f.scrollSista){case"header":C=T,w=F?T:0
+break
+case"header-tabs":U=F?k:0,C=F?T+k:T
+break
+case"tabs-subheader":O=0,U=F?A-T:R,_=F?R:0
+break
+case"tabs":O=0,C=F?k:0
+break
+case"subheader":O=0,w=0
+break
+case"header-subheader":w=F?T:0
+break
+case"subheader-header":U=R,_=F?R:0,w=F?T:0
+break
+default:U=F?A-T:R,_=F?R:0}}function S(a,n){var r=Math.max(0,1-a/T)
+g&&(d(g,a,n),e.forEach(g.children,function(e){e.style.opacity=r,u&&(e.style[t.CSS.TRANSFORM]="scale("+r+","+r+")")})),y&&d(y,a,n)}function p(e,t){var a,n,r=l[0].style,o=e>U?e-U:0
+M&&(n=e>P?e-P:0,d(M,Math.min(C,n),t)),I&&(a=Math.min(w,e>_?e-_:0),L&&(a=-a,r.bottom=Math.max(0,k-e)+"px"),d(I,a,t)),S(Math.min(O,o),t)
+var i=Math.max(0,A-e)
+V&&(i+=20),r.top=i+"px"}function m(e,a){t.requestAnimationFrame(function(){p(e,a)})}var v,N,$,y,g,T,A,M,R,I,k,x,O,w,C,H=!0,q=a[0].body,D=f.delegateHandle?o.$getByHandle(f.delegateHandle):o,E=D.getScrollView(),V=!1,F=!1,L=!1,U=0,_=0,P=0
+r.$on("scroll.refreshComplete",function(){b()}),r.$parent.$on("$ionicView.beforeLeave",function(){H=!0,p(0),g=null,y=null}),r.$parent.$on("$ionicView.beforeEnter",function(){E&&E.scrollTo(0,0)}),r.$parent.$on("$ionicView.afterEnter",function(){b(),n(function(){h(),H=!1},20,!1)}),l.bind("scroll",function(e){if(!H){e=e.originalEvent||e
+var t=0,a=e.detail?e.detail.scrollTop:e.target.scrollTop
+v=a>=0?Math.min(x,Math.max(0,v+a-$)):0,V&&v>T&&(v=T),E.getScrollMax().top-a<=A,$=a,N!==v&&(N=v,m(v,t))}})}}}])}(angular,ionic)
diff --git a/www/external/ng-websocket.min.js b/www/external/ng-websocket.min.js
new file mode 100644
index 00000000..a988667b
--- /dev/null
+++ b/www/external/ng-websocket.min.js
@@ -0,0 +1,31 @@
+"use strict"
+!function(){function e(){var e=this
+e.$$config={lazy:!1,reconnect:!0,reconnectInterval:2e3,mock:!1,enqueue:!1,protocols:null},e.$setup=function(n){return n=n||{},e.$$config=angular.extend({},e.$$config,n),e},e.$get=["$http",function(t){return new n(e.$$config,t)}]}function n(e,n){var o=this
+o.$$websocketList={},o.$$config=e||{},o.$get=function(e){return o.$$websocketList[e]},o.$new=function(e){e=e||{},"string"==typeof e&&(e={url:e},arguments.length>1&&("string"==typeof arguments[1]&&arguments[1].length>0?e.protocols=[arguments[1]]:"object"==typeof arguments[1]&&arguments[1].length>0&&(e.protocols=arguments[1])))
+var r=o.$get(e.url)
+if("undefined"==typeof r){var $=angular.extend({},o.$$config,e)
+r=new t($,n),o.$$websocketList[$.url]=r}return r}}function t(e,n){var t=this
+if("undefined"==typeof e||"object"==typeof e&&"undefined"==typeof e.url)throw new Error("An url must be specified for WebSocket")
+return t.$$eventMap={},t.$$ws=void 0,t.$$reconnectTask=void 0,t.$$reconnectCopy=!0,t.$$queue=[],t.$$config={url:void 0,lazy:!1,reconnect:!0,reconnectInterval:2e3,enqueue:!1,mock:!1,protocols:null},t.$$fireEvent=function(){var e=[]
+Array.prototype.push.apply(e,arguments)
+var n=e.shift(),o=t.$$eventMap[n]
+if("undefined"!=typeof o)for(var r=0;r<o.length;r++)"function"==typeof o[r]&&o[r].apply(t,e)},t.$$init=function(e){return e.mock?t.$$ws=new o(e.mock,n):e.protocols?t.$$ws=new WebSocket(e.url,e.protocols):t.$$ws=new WebSocket(e.url),t.$$ws.onmessage=function(e){try{var n=JSON.parse(e.data)
+t.$$fireEvent(n.event,n.data),t.$$fireEvent("$message",n)}catch(n){t.$$fireEvent("$message",e.data)}},t.$$ws.onerror=function(e){t.$$fireEvent("$error",e)},t.$$ws.onopen=function(){if(t.$$reconnectTask&&(clearInterval(t.$$reconnectTask),delete t.$$reconnectTask),t.$$config.enqueue&&t.$$queue.length>0)for(;t.$$queue.length>0&&t.$ready();)t.$$send(t.$$queue.shift())
+t.$$fireEvent("$open")},t.$$ws.onclose=function(){t.$$config.reconnect&&!t.$$reconnectTask&&(t.$$reconnectTask=setInterval(function(){t.$status()===t.$CLOSED&&t.$open()},t.$$config.reconnectInterval)),t.$$fireEvent("$close")},t},t.$CONNECTING=0,t.$OPEN=1,t.$CLOSING=2,t.$CLOSED=3,t.$on=function(){var e=[]
+Array.prototype.push.apply(e,arguments)
+var n=e.shift()
+if("string"!=typeof n||0===e.length)throw new Error("$on accept two parameters at least: a String and a Function or an array of Functions")
+t.$$eventMap[n]=t.$$eventMap[n]||[]
+for(var o=0;o<e.length;o++)t.$$eventMap[n].push(e[o])
+return t},t.$un=function(e){if("string"!=typeof e)throw new Error("$un needs a String representing an event.")
+return"undefined"!=typeof t.$$eventMap[e]&&delete t.$$eventMap[e],t},t.$$send=function(e){t.$ready()?t.$$ws.send(JSON.stringify(e)):t.$$config.enqueue&&t.$$queue.push(e)},t.$emit=function(e,n){if("string"!=typeof e)throw new Error("$emit needs two parameter: a String and a Object or a String")
+var o={event:e,data:n}
+return t.$$send(o),t},t.$open=function(){return t.$$config.reconnect=t.$$reconnectCopy,t.$status()!==t.$OPEN&&t.$$init(t.$$config),t},t.$close=function(){return t.$status()!==t.$CLOSED&&t.$$ws.close(),t.$$reconnectTask&&(clearInterval(t.$$reconnectTask),delete t.$$reconnectTask),t.$$config.reconnect=!1,t},t.$status=function(){return"undefined"==typeof t.$$ws?t.$CLOSED:t.$$ws.readyState},t.$ready=function(){return t.$status()===t.$OPEN},t.$mockup=function(){return t.$$config.mock},t.$$config=angular.extend({},t.$$config,e),t.$$reconnectCopy=t.$$config.reconnect,t.$$config.lazy||t.$$init(t.$$config),t}function o(e,n){e=e||{}
+var t=this,o=e.openTimeout||500,r=e.closeTimeout||1e3,$=e.messageInterval||2e3,c=e.fixtures||{},a=[]
+t.CONNECTING=0,t.OPEN=1,t.CLOSING=2,t.CLOSED=3,t.readyState=t.CONNECTING,t.send=function(e){if(t.readyState===t.OPEN)return a.push(e),t
+throw new Error("WebSocket is already in CLOSING or CLOSED state.")},t.close=function(){return t.readyState===t.OPEN&&(t.readyState=t.CLOSING,setTimeout(function(){t.readyState=t.CLOSED,t.onclose()},r)),t},t.onmessage=function(){},t.onerror=function(){},t.onopen=function(){},t.onclose=function(){},setInterval(function(){if(a.length>0){var e=a.shift(),n=JSON.parse(e)
+switch(n.event){case"$close":t.close()
+break
+default:"undefined"!=typeof c[n.event]&&(n.data=c[n.event].data||n.data,n.event=c[n.event].event||n.event),e=JSON.stringify(n),t.onmessage({data:e})}}},$)
+var i=function(e){e=e||{},e=e instanceof Error?{}:e,c=e,setTimeout(function(){t.readyState=t.OPEN,t.onopen()},o)}
+return"string"==typeof c?n.get(c).success(i).error(i):i(c),t}angular.module("ngWebsocket",[]).provider("$websocket",e)}()
diff --git a/www/external/origjs/NeuQuant.js b/www/external/origjs/NeuQuant.js
new file mode 100644
index 00000000..f0c124ba
--- /dev/null
+++ b/www/external/origjs/NeuQuant.js
@@ -0,0 +1,389 @@
+NeuQuant = function () {
+ function NeuQuant() {
+ var netsize = 256;
+ var prime1 = 499;
+ var prime2 = 491;
+ var prime3 = 487;
+ var prime4 = 503;
+ var minpicturebytes = 3 * prime4;
+ var maxnetpos = netsize - 1;
+ var netbiasshift = 4;
+ var ncycles = 100;
+ var intbiasshift = 16;
+ var intbias = 1 << intbiasshift;
+ var gammashift = 10;
+ var gamma = 1 << gammashift;
+ var betashift = 10;
+ var beta = intbias >> betashift;
+ var betagamma = intbias << gammashift - betashift;
+ var initrad = netsize >> 3;
+ var radiusbiasshift = 6;
+ var radiusbias = 1 << radiusbiasshift;
+ var initradius = initrad * radiusbias;
+ var radiusdec = 30;
+ var alphabiasshift = 10;
+ var initalpha = 1 << alphabiasshift;
+ var alphadec;
+ var radbiasshift = 8;
+ var radbias = 1 << radbiasshift;
+ var alpharadbshift = alphabiasshift + radbiasshift;
+ var alpharadbias = 1 << alpharadbshift;
+ var thepicture;
+ var lengthcount;
+ var samplefac;
+ var network;
+ var netindex = [];
+ var bias = [];
+ var freq = [];
+ var radpower = [];
+ function NeuQuantConstructor(thepic, len, sample) {
+ var i;
+ var p;
+ thepicture = thepic;
+ lengthcount = len;
+ samplefac = sample;
+ network = new Array(netsize);
+ for (i = 0; i < netsize; i++) {
+ network[i] = new Array(4);
+ p = network[i];
+ p[0] = p[1] = p[2] = (i << netbiasshift + 8) / netsize | 0;
+ freq[i] = intbias / netsize | 0;
+ bias[i] = 0;
+ }
+ }
+ function colorMap() {
+ var map = [];
+ var index = new Array(netsize);
+ for (var i = 0; i < netsize; i++)
+ index[network[i][3]] = i;
+ var k = 0;
+ for (var l = 0; l < netsize; l++) {
+ var j = index[l];
+ map[k++] = network[j][0];
+ map[k++] = network[j][1];
+ map[k++] = network[j][2];
+ }
+ return map;
+ }
+ function inxbuild() {
+ var i;
+ var j;
+ var smallpos;
+ var smallval;
+ var p;
+ var q;
+ var previouscol;
+ var startpos;
+ previouscol = 0;
+ startpos = 0;
+ for (i = 0; i < netsize; i++) {
+ p = network[i];
+ smallpos = i;
+ smallval = p[1];
+ for (j = i + 1; j < netsize; j++) {
+ q = network[j];
+ if (q[1] < smallval) {
+ smallpos = j;
+ smallval = q[1];
+ }
+ }
+ q = network[smallpos];
+ if (i != smallpos) {
+ j = q[0];
+ q[0] = p[0];
+ p[0] = j;
+ j = q[1];
+ q[1] = p[1];
+ p[1] = j;
+ j = q[2];
+ q[2] = p[2];
+ p[2] = j;
+ j = q[3];
+ q[3] = p[3];
+ p[3] = j;
+ }
+ if (smallval != previouscol) {
+ netindex[previouscol] = startpos + i >> 1;
+ for (j = previouscol + 1; j < smallval; j++) {
+ netindex[j] = i;
+ }
+ previouscol = smallval;
+ startpos = i;
+ }
+ }
+ netindex[previouscol] = startpos + maxnetpos >> 1;
+ for (j = previouscol + 1; j < 256; j++) {
+ netindex[j] = maxnetpos;
+ }
+ }
+ function learn() {
+ var i;
+ var j;
+ var b;
+ var g;
+ var r;
+ var radius;
+ var rad;
+ var alpha;
+ var step;
+ var delta;
+ var samplepixels;
+ var p;
+ var pix;
+ var lim;
+ if (lengthcount < minpicturebytes) {
+ samplefac = 1;
+ }
+ alphadec = 30 + (samplefac - 1) / 3;
+ p = thepicture;
+ pix = 0;
+ lim = lengthcount;
+ samplepixels = lengthcount / (3 * samplefac);
+ delta = samplepixels / ncycles | 0;
+ alpha = initalpha;
+ radius = initradius;
+ rad = radius >> radiusbiasshift;
+ if (rad <= 1) {
+ rad = 0;
+ }
+ for (i = 0; i < rad; i++) {
+ radpower[i] = alpha * ((rad * rad - i * i) * radbias / (rad * rad));
+ }
+ if (lengthcount < minpicturebytes) {
+ step = 3;
+ } else if (lengthcount % prime1 !== 0) {
+ step = 3 * prime1;
+ } else {
+ if (lengthcount % prime2 !== 0) {
+ step = 3 * prime2;
+ } else {
+ if (lengthcount % prime3 !== 0) {
+ step = 3 * prime3;
+ } else {
+ step = 3 * prime4;
+ }
+ }
+ }
+ i = 0;
+ while (i < samplepixels) {
+ b = (p[pix + 0] & 255) << netbiasshift;
+ g = (p[pix + 1] & 255) << netbiasshift;
+ r = (p[pix + 2] & 255) << netbiasshift;
+ j = contest(b, g, r);
+ altersingle(alpha, j, b, g, r);
+ if (rad !== 0) {
+ alterneigh(rad, j, b, g, r);
+ }
+ pix += step;
+ if (pix >= lim) {
+ pix -= lengthcount;
+ }
+ i++;
+ if (delta === 0) {
+ delta = 1;
+ }
+ if (i % delta === 0) {
+ alpha -= alpha / alphadec;
+ radius -= radius / radiusdec;
+ rad = radius >> radiusbiasshift;
+ if (rad <= 1) {
+ rad = 0;
+ }
+ for (j = 0; j < rad; j++) {
+ radpower[j] = alpha * ((rad * rad - j * j) * radbias / (rad * rad));
+ }
+ }
+ }
+ }
+ function map(b, g, r) {
+ var i;
+ var j;
+ var dist;
+ var a;
+ var bestd;
+ var p;
+ var best;
+ bestd = 1000;
+ best = -1;
+ i = netindex[g];
+ j = i - 1;
+ while (i < netsize || j >= 0) {
+ if (i < netsize) {
+ p = network[i];
+ dist = p[1] - g;
+ if (dist >= bestd) {
+ i = netsize;
+ } else {
+ i++;
+ if (dist < 0) {
+ dist = -dist;
+ }
+ a = p[0] - b;
+ if (a < 0) {
+ a = -a;
+ }
+ dist += a;
+ if (dist < bestd) {
+ a = p[2] - r;
+ if (a < 0) {
+ a = -a;
+ }
+ dist += a;
+ if (dist < bestd) {
+ bestd = dist;
+ best = p[3];
+ }
+ }
+ }
+ }
+ if (j >= 0) {
+ p = network[j];
+ dist = g - p[1];
+ if (dist >= bestd) {
+ j = -1;
+ } else {
+ j--;
+ if (dist < 0) {
+ dist = -dist;
+ }
+ a = p[0] - b;
+ if (a < 0) {
+ a = -a;
+ }
+ dist += a;
+ if (dist < bestd) {
+ a = p[2] - r;
+ if (a < 0) {
+ a = -a;
+ }
+ dist += a;
+ if (dist < bestd) {
+ bestd = dist;
+ best = p[3];
+ }
+ }
+ }
+ }
+ }
+ return best;
+ }
+ function process() {
+ learn();
+ unbiasnet();
+ inxbuild();
+ return colorMap();
+ }
+ function unbiasnet() {
+ var i;
+ var j;
+ for (i = 0; i < netsize; i++) {
+ network[i][0] >>= netbiasshift;
+ network[i][1] >>= netbiasshift;
+ network[i][2] >>= netbiasshift;
+ network[i][3] = i;
+ }
+ }
+ function alterneigh(rad, i, b, g, r) {
+ var j;
+ var k;
+ var lo;
+ var hi;
+ var a;
+ var m;
+ var p;
+ lo = i - rad;
+ if (lo < -1) {
+ lo = -1;
+ }
+ hi = i + rad;
+ if (hi > netsize) {
+ hi = netsize;
+ }
+ j = i + 1;
+ k = i - 1;
+ m = 1;
+ while (j < hi || k > lo) {
+ a = radpower[m++];
+ if (j < hi) {
+ p = network[j++];
+ try {
+ p[0] -= a * (p[0] - b) / alpharadbias | 0;
+ p[1] -= a * (p[1] - g) / alpharadbias | 0;
+ p[2] -= a * (p[2] - r) / alpharadbias | 0;
+ } catch (e) {
+ }
+ }
+ if (k > lo) {
+ p = network[k--];
+ try {
+ p[0] -= a * (p[0] - b) / alpharadbias | 0;
+ p[1] -= a * (p[1] - g) / alpharadbias | 0;
+ p[2] -= a * (p[2] - r) / alpharadbias | 0;
+ } catch (e) {
+ }
+ }
+ }
+ }
+ function altersingle(alpha, i, b, g, r) {
+ var n = network[i];
+ var alphaMult = alpha / initalpha;
+ n[0] -= alphaMult * (n[0] - b) | 0;
+ n[1] -= alphaMult * (n[1] - g) | 0;
+ n[2] -= alphaMult * (n[2] - r) | 0;
+ }
+ function contest(b, g, r) {
+ var i;
+ var dist;
+ var a;
+ var biasdist;
+ var betafreq;
+ var bestpos;
+ var bestbiaspos;
+ var bestd;
+ var bestbiasd;
+ var n;
+ bestd = ~(1 << 31);
+ bestbiasd = bestd;
+ bestpos = -1;
+ bestbiaspos = bestpos;
+ for (i = 0; i < netsize; i++) {
+ n = network[i];
+ dist = n[0] - b;
+ if (dist < 0) {
+ dist = -dist;
+ }
+ a = n[1] - g;
+ if (a < 0) {
+ a = -a;
+ }
+ dist += a;
+ a = n[2] - r;
+ if (a < 0) {
+ a = -a;
+ }
+ dist += a;
+ if (dist < bestd) {
+ bestd = dist;
+ bestpos = i;
+ }
+ biasdist = dist - (bias[i] >> intbiasshift - netbiasshift);
+ if (biasdist < bestbiasd) {
+ bestbiasd = biasdist;
+ bestbiaspos = i;
+ }
+ betafreq = freq[i] >> betashift;
+ freq[i] -= betafreq;
+ bias[i] += betafreq << gammashift;
+ }
+ freq[bestpos] += beta;
+ bias[bestpos] -= betagamma;
+ return bestbiaspos;
+ }
+ NeuQuantConstructor.apply(this, arguments);
+ var exports = {};
+ exports.map = map;
+ exports.process = process;
+ return exports;
+ }
+ return NeuQuant;
+}(); \ No newline at end of file
diff --git a/www/external/origjs/README.TXT b/www/external/origjs/README.TXT
new file mode 100644
index 00000000..19a2b36d
--- /dev/null
+++ b/www/external/origjs/README.TXT
@@ -0,0 +1,3 @@
+These are original JS files.
+I minified then in externals/ using npm minify
+~pp
diff --git a/www/external/angular-carousel.js b/www/external/origjs/angular-carousel.js
index 21cc6729..48c94258 100644
--- a/www/external/angular-carousel.js
+++ b/www/external/origjs/angular-carousel.js
@@ -1,23 +1,21 @@
-/* jshint ignore:start */
-/**
-
- * Angular Carousel - Mobile friendly touch carousel for AngularJS
- * @version v0.3.12 - 2015-06-11
- * @link http://revolunet.github.com/angular-carousel
- * @author Julien Bouquillon <julien@revolunet.com>
- * @license MIT License, http://www.opensource.org/licenses/MIT
- */
-/*global angular */
-/*
-Angular touch carousel with CSS GPU accel and slide buffering
-http://github.com/revolunet/angular-carousel
+//Angular Carousel - Mobile friendly touch carousel for AngularJS
+//@version v0.3.12 - 2015-06-11
+//@link http://revolunet.github.com/angular-carousel
+//@author Julien Bouquillon <julien@revolunet.com>
+//@license MIT License, http://www.opensource.org/licenses/MIT
+
+
+
+//Angular touch carousel with CSS GPU accel and slide buffering
+//http://github.com/revolunet/angular-carousel
-*/
// Modified by PP for mobile friendly touch, start without auto slide but enabled it if tapped
// and logic to wait for an image to load before it slides to the next one
+/* jshint ignore:start */
+/*global angular */
angular.module('angular-carousel', [
'ngTouch',
'angular-carousel.shifty'
diff --git a/www/external/angular-circular-navigation.js b/www/external/origjs/angular-circular-navigation.js
index 17488768..17488768 100644
--- a/www/external/angular-circular-navigation.js
+++ b/www/external/origjs/angular-circular-navigation.js
diff --git a/www/external/origjs/angular-ios9-uiwebview.patch.js b/www/external/origjs/angular-ios9-uiwebview.patch.js
new file mode 100644
index 00000000..c52cad82
--- /dev/null
+++ b/www/external/origjs/angular-ios9-uiwebview.patch.js
@@ -0,0 +1,73 @@
+/**
+ * ================== angular-ios9-uiwebview.patch.js v1.1.0 ==================
+ *
+ * This patch works around iOS9 UIWebView regression that causes infinite digest
+ * errors in Angular.
+ *
+ * The patch can be applied to Angular 1.2.0 – 1.4.5. Newer versions of Angular
+ * have the workaround baked in.
+ *
+ * To apply this patch load/bundle this file with your application and add a
+ * dependency on the "ngIOS9Patch" module to your main app module.
+ *
+ * For example:
+ *
+ * ```
+ * angular.module('myApp', ['ngRoute'])`
+ * ```
+ *
+ * becomes
+ *
+ * ```
+ * angular.module('myApp', ['ngRoute', 'ngIOS9UIWebViewPatch'])
+ * ```
+ *
+ *
+ * More info:
+ * - https://openradar.appspot.com/22186109
+ * - https://github.com/angular/angular.js/issues/12241
+ * - https://github.com/driftyco/ionic/issues/4082
+ *
+ *
+ * @license AngularJS
+ * (c) 2010-2015 Google, Inc. http://angularjs.org
+ * License: MIT
+ */
+
+angular.module('ngIOS9UIWebViewPatch', ['ng']).config(function($provide) {
+ $provide.decorator('$browser', ['$delegate', '$window', function($delegate, $window) {
+
+ if (isIOS9UIWebView($window.navigator.userAgent)) {
+ return applyIOS9Shim($delegate);
+ }
+
+ return $delegate;
+
+ function isIOS9UIWebView(userAgent) {
+ return /(iPhone|iPad|iPod).* OS 9_\d/.test(userAgent) && !/Version\/9\./.test(userAgent);
+ }
+
+ function applyIOS9Shim(browser) {
+ var pendingLocationUrl = null;
+ var originalUrlFn= browser.url;
+
+ browser.url = function() {
+ if (arguments.length) {
+ pendingLocationUrl = arguments[0];
+ return originalUrlFn.apply(browser, arguments);
+ }
+
+ return pendingLocationUrl || originalUrlFn.apply(browser, arguments);
+ };
+
+ window.addEventListener('popstate', clearPendingLocationUrl, false);
+ window.addEventListener('hashchange', clearPendingLocationUrl, false);
+
+ function clearPendingLocationUrl() {
+ pendingLocationUrl = null;
+ }
+
+ return browser;
+ }
+ }]);
+});
diff --git a/www/external/canvas-toBlob.js b/www/external/origjs/canvas-toBlob.js
index 6d895a78..6d895a78 100644
--- a/www/external/canvas-toBlob.js
+++ b/www/external/origjs/canvas-toBlob.js
diff --git a/www/external/draggabilly.pkgd.js b/www/external/origjs/draggabilly.pkgd.js
index bef3db35..bef3db35 100644
--- a/www/external/draggabilly.pkgd.js
+++ b/www/external/origjs/draggabilly.pkgd.js
diff --git a/www/external/origjs/gifwriter.js b/www/external/origjs/gifwriter.js
new file mode 100644
index 00000000..67ae264e
--- /dev/null
+++ b/www/external/origjs/gifwriter.js
@@ -0,0 +1,406 @@
+// (c) Dean McNamee <dean@gmail.com>, 2013.
+// //
+// // https://github.com/deanm/omggif
+// //
+// // Permission is hereby granted, free of charge, to any person obtaining a copy
+// // of this software and associated documentation files (the "Software"), to
+// // deal in the Software without restriction, including without limitation the
+// // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// // sell copies of the Software, and to permit persons to whom the Software is
+// // furnished to do so, subject to the following conditions:
+// //
+// // The above copyright notice and this permission notice shall be included in
+// // all copies or substantial portions of the Software.
+// //
+// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// // IN THE SOFTWARE.
+// //
+// // omggif is a JavaScript implementation of a GIF 89a encoder and decoder,
+// // including animation and compression. It does not rely on any specific
+// // underlying system, so should run in the browser, Node, or Plask.
+'use strict';
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function check_palette_and_num_colors(palette) {
+ var num_colors = palette.length;
+
+ if (num_colors < 2 || num_colors > 256 || num_colors & num_colors - 1) {
+ throw new Error('Invalid code/color length, must be power of 2 and 2 .. 256.');
+ }
+
+ return num_colors;
+}
+
+var GifWriter = function () {
+ function GifWriter(rs, width, height) {
+ var gopts = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
+
+ _classCallCheck(this, GifWriter);
+
+ var loop = gopts.loop,
+ palette = gopts.palette;
+
+ var p = 0;
+ var buf = [];
+ var global_palette = palette;
+
+ if (width <= 0 || height <= 0 || width > 65535 || height > 65535) {
+ throw new Error('Width/Height invalid.');
+ }
+
+ // - Header.
+ buf[p++] = 0x47;buf[p++] = 0x49;buf[p++] = 0x46; // GIF
+ buf[p++] = 0x38;buf[p++] = 0x39;buf[p++] = 0x61; // 89a
+
+ // Handling of Global Color Table (palette) and background index.
+ var gp_num_colors_pow2 = 0;
+ var background = 0;
+ if (global_palette) {
+ var gp_num_colors = check_palette_and_num_colors(global_palette);
+ while (gp_num_colors >>= 1) {
+ ++gp_num_colors_pow2;
+ }gp_num_colors = 1 << gp_num_colors_pow2;
+ gp_num_colors_pow2--;
+ if (gopts.background !== undefined) {
+ background = gopts.background;
+ if (background >= gp_num_colors) {
+ throw new Error('Background index out of range.');
+ }
+ // The GIF spec states that a background index of 0 should be ignored, so
+ // this is probably a mistake and you really want to set it to another
+ // slot in the palette. But actually in the end most browsers, etc end
+ // up ignoring this almost completely (including for dispose background).
+ if (background === 0) throw new Error('Background index explicitly passed as 0.');
+ }
+ }
+
+ // - Logical Screen Descriptor.
+ // NOTE(deanm): w/h apparently ignored by implementations, but set anyway.
+ buf[p++] = width & 0xff;
+ buf[p++] = width >> 8 & 0xff;
+ buf[p++] = height & 0xff;
+ buf[p++] = height >> 8 & 0xff;
+
+ // NOTE: Indicates 0-bpp original color resolution (unused?).
+ buf[p++] = (global_palette ? 0x80 : 0) | // Global Color Table Flag.
+ gp_num_colors_pow2; // NOTE: No sort flag (unused?).
+ buf[p++] = background; // Background Color Index.
+ buf[p++] = 0; // Pixel aspect ratio (unused?).
+
+ // - Global Color Table
+ if (global_palette) {
+ for (var _iterator = global_palette, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
+ var _ref;
+
+ if (_isArray) {
+ if (_i >= _iterator.length) break;
+ _ref = _iterator[_i++];
+ } else {
+ _i = _iterator.next();
+ if (_i.done) break;
+ _ref = _i.value;
+ }
+
+ var rgb = _ref;
+
+ buf[p++] = rgb >> 16 & 0xff;
+ buf[p++] = rgb >> 8 & 0xff;
+ buf[p++] = rgb & 0xff;
+ }
+ }
+
+ if (Number.isInteger(loop)) {
+ // Netscape block for looping.
+ if (loop < 0 || loop > 65535) throw "Loop count invalid.";
+ // Extension code, label, and length.
+ buf[p++] = 0x21;buf[p++] = 0xff;buf[p++] = 0x0b;
+ // NETSCAPE2.0
+ buf[p++] = 0x4e;buf[p++] = 0x45;buf[p++] = 0x54;buf[p++] = 0x53;
+ buf[p++] = 0x43;buf[p++] = 0x41;buf[p++] = 0x50;buf[p++] = 0x45;
+ buf[p++] = 0x32;buf[p++] = 0x2e;buf[p++] = 0x30;
+ // Sub-block
+ buf[p++] = 0x03;buf[p++] = 0x01;
+ buf[p++] = loop & 0xff;buf[p++] = loop >> 8 & 0xff;
+ buf[p++] = 0x00; // Terminator.
+ }
+
+ var self = this;
+ var reader = rs.getReader();
+
+ return new ReadableStream({
+ start: function start(controller) {
+ controller.enqueue(new Uint8Array(buf));
+ },
+ pull: function pull(controller) {
+ return reader.read().then(function (_ref2) {
+ var done = _ref2.done,
+ value = _ref2.value;
+
+ if (done) {
+ controller.enqueue(new Uint8Array([0x3b]));
+ controller.close();
+ return;
+ }
+
+ self.addFrame.apply(self, [controller].concat(value));
+ });
+ }
+ });
+ }
+
+ GifWriter.prototype.addFrame = function addFrame(controller, x, y, w, h, indexed_pixels) {
+ var opts = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : {};
+
+ var p = 0;
+ var buf = [];
+
+ // TODO(deanm): Bounds check x, y. Do they need to be within the virtual
+ // canvas width/height, I imagine?
+ if (x < 0 || y < 0 || x > 65535 || y > 65535) {
+ throw new Error('x/y invalid.');
+ }
+
+ if (w <= 0 || h <= 0 || w > 65535 || h > 65535) throw "Width/Height invalid.";
+
+ if (indexed_pixels.length < w * h) throw "Not enough pixels for the frame size.";
+
+ var using_local_palette = true;
+ var palette = opts.palette;
+ if (palette === undefined || palette === null) {
+ using_local_palette = false;
+ palette = global_palette;
+ }
+
+ if (palette === undefined || palette === null) throw "Must supply either a local or global palette.";
+
+ var num_colors = check_palette_and_num_colors(palette);
+
+ // Compute the min_code_size (power of 2), destroying num_colors.
+ var min_code_size = 0;
+ while (num_colors >>= 1) {
+ ++min_code_size;
+ }num_colors = 1 << min_code_size; // Now we can easily get it back.
+
+ var delay = opts.delay === undefined ? 0 : opts.delay;
+
+ // From the spec:
+ // 0 - No disposal specified. The decoder is
+ // not required to take any action.
+ // 1 - Do not dispose. The graphic is to be left
+ // in place.
+ // 2 - Restore to background color. The area used by the
+ // graphic must be restored to the background color.
+ // 3 - Restore to previous. The decoder is required to
+ // restore the area overwritten by the graphic with
+ // what was there prior to rendering the graphic.
+ // 4-7 - To be defined.
+ // NOTE(deanm): Dispose background doesn't really work, apparently most
+ // browsers ignore the background palette index and clear to transparency.
+ var disposal = opts.disposal === undefined ? 0 : opts.disposal;
+ if (disposal < 0 || disposal > 3) // 4-7 is reserved.
+ throw "Disposal out of range.";
+
+ var use_transparency = false;
+ var transparent_index = 0;
+ if (opts.transparent !== undefined && opts.transparent !== null) {
+ use_transparency = true;
+ transparent_index = opts.transparent;
+ if (transparent_index < 0 || transparent_index >= num_colors) throw "Transparent color index.";
+ }
+
+ if (disposal !== 0 || use_transparency || delay !== 0) {
+ // - Graphics Control Extension
+ buf[p++] = 0x21;buf[p++] = 0xf9; // Extension / Label.
+ buf[p++] = 4; // Byte size.
+
+ buf[p++] = disposal << 2 | (use_transparency === true ? 1 : 0);
+ buf[p++] = delay & 0xff;buf[p++] = delay >> 8 & 0xff;
+ buf[p++] = transparent_index; // Transparent color index.
+ buf[p++] = 0; // Block Terminator.
+ }
+
+ // - Image Descriptor
+ buf[p++] = 0x2c; // Image Seperator.
+ buf[p++] = x & 0xff;buf[p++] = x >> 8 & 0xff; // Left.
+ buf[p++] = y & 0xff;buf[p++] = y >> 8 & 0xff; // Top.
+ buf[p++] = w & 0xff;buf[p++] = w >> 8 & 0xff;
+ buf[p++] = h & 0xff;buf[p++] = h >> 8 & 0xff;
+ // NOTE: No sort flag (unused?).
+ // TODO(deanm): Support interlace.
+ buf[p++] = using_local_palette === true ? 0x80 | min_code_size - 1 : 0;
+
+ // - Local Color Table
+ if (using_local_palette === true) {
+ for (var i = 0, il = palette.length; i < il; ++i) {
+ var rgb = palette[i];
+ buf[p++] = rgb >> 16 & 0xff;
+ buf[p++] = rgb >> 8 & 0xff;
+ buf[p++] = rgb & 0xff;
+ }
+ }
+
+ GifWriterOutputLZWCodeStream(buf, p, min_code_size < 2 ? 2 : min_code_size, indexed_pixels);
+
+ controller.enqueue(new Uint8Array(buf));
+ };
+
+ return GifWriter;
+}();
+
+// Main compression routine, palette indexes -> LZW code stream.
+// |index_stream| must have at least one entry.
+
+
+function GifWriterOutputLZWCodeStream(buf, p, min_code_size, index_stream) {
+ buf[p++] = min_code_size;
+ var cur_subblock = p++; // Pointing at the length field.
+
+ var clear_code = 1 << min_code_size;
+ var code_mask = clear_code - 1;
+ var eoi_code = clear_code + 1;
+ var next_code = eoi_code + 1;
+
+ var cur_code_size = min_code_size + 1; // Number of bits per code.
+ var cur_shift = 0;
+ // We have at most 12-bit codes, so we should have to hold a max of 19
+ // bits here (and then we would write out).
+ var cur = 0;
+
+ function emit_bytes_to_buffer(bit_block_size) {
+ while (cur_shift >= bit_block_size) {
+ buf[p++] = cur & 0xff;
+ cur >>= 8;cur_shift -= 8;
+ if (p === cur_subblock + 256) {
+ // Finished a subblock.
+ buf[cur_subblock] = 255;
+ cur_subblock = p++;
+ }
+ }
+ }
+
+ function emit_code(c) {
+ cur |= c << cur_shift;
+ cur_shift += cur_code_size;
+ emit_bytes_to_buffer(8);
+ }
+
+ // I am not an expert on the topic, and I don't want to write a thesis.
+ // However, it is good to outline here the basic algorithm and the few data
+ // structures and optimizations here that make this implementation fast.
+ // The basic idea behind LZW is to build a table of previously seen runs
+ // addressed by a short id (herein called output code). All data is
+ // referenced by a code, which represents one or more values from the
+ // original input stream. All input bytes can be referenced as the same
+ // value as an output code. So if you didn't want any compression, you
+ // could more or less just output the original bytes as codes (there are
+ // some details to this, but it is the idea). In order to achieve
+ // compression, values greater then the input range (codes can be up to
+ // 12-bit while input only 8-bit) represent a sequence of previously seen
+ // inputs. The decompressor is able to build the same mapping while
+ // decoding, so there is always a shared common knowledge between the
+ // encoding and decoder, which is also important for "timing" aspects like
+ // how to handle variable bit width code encoding.
+ //
+ // One obvious but very important consequence of the table system is there
+ // is always a unique id (at most 12-bits) to map the runs. 'A' might be
+ // 4, then 'AA' might be 10, 'AAA' 11, 'AAAA' 12, etc. This relationship
+ // can be used for an effecient lookup strategy for the code mapping. We
+ // need to know if a run has been seen before, and be able to map that run
+ // to the output code. Since we start with known unique ids (input bytes),
+ // and then from those build more unique ids (table entries), we can
+ // continue this chain (almost like a linked list) to always have small
+ // integer values that represent the current byte chains in the encoder.
+ // This means instead of tracking the input bytes (AAAABCD) to know our
+ // current state, we can track the table entry for AAAABC (it is guaranteed
+ // to exist by the nature of the algorithm) and the next character D.
+ // Therefor the tuple of (table_entry, byte) is guaranteed to also be
+ // unique. This allows us to create a simple lookup key for mapping input
+ // sequences to codes (table indices) without having to store or search
+ // any of the code sequences. So if 'AAAA' has a table entry of 12, the
+ // tuple of ('AAAA', K) for any input byte K will be unique, and can be our
+ // key. This leads to a integer value at most 20-bits, which can always
+ // fit in an SMI value and be used as a fast sparse array / object key.
+
+ // Output code for the current contents of the index buffer.
+ var ib_code = index_stream[0] & code_mask; // Load first input index.
+ var code_table = {}; // Key'd on our 20-bit "tuple".
+
+ emit_code(clear_code); // Spec says first code should be a clear code.
+
+ // First index already loaded, process the rest of the stream.
+ for (var i = 1, il = index_stream.length; i < il; ++i) {
+ var k = index_stream[i] & code_mask;
+ var cur_key = ib_code << 8 | k; // (prev, k) unique tuple.
+ var cur_code = code_table[cur_key]; // buffer + k.
+
+ // Check if we have to create a new code table entry.
+ if (cur_code === undefined) {
+ // We don't have buffer + k.
+ // Emit index buffer (without k).
+ // This is an inline version of emit_code, because this is the core
+ // writing routine of the compressor (and V8 cannot inline emit_code
+ // because it is a closure here in a different context). Additionally
+ // we can call emit_byte_to_buffer less often, because we can have
+ // 30-bits (from our 31-bit signed SMI), and we know our codes will only
+ // be 12-bits, so can safely have 18-bits there without overflow.
+ // emit_code(ib_code);
+ cur |= ib_code << cur_shift;
+ cur_shift += cur_code_size;
+ while (cur_shift >= 8) {
+ buf[p++] = cur & 0xff;
+ cur >>= 8;cur_shift -= 8;
+ if (p === cur_subblock + 256) {
+ // Finished a subblock.
+ buf[cur_subblock] = 255;
+ cur_subblock = p++;
+ }
+ }
+
+ if (next_code === 4096) {
+ // Table full, need a clear.
+ emit_code(clear_code);
+ next_code = eoi_code + 1;
+ cur_code_size = min_code_size + 1;
+ code_table = {};
+ } else {
+ // Table not full, insert a new entry.
+ // Increase our variable bit code sizes if necessary. This is a bit
+ // tricky as it is based on "timing" between the encoding and
+ // decoder. From the encoders perspective this should happen after
+ // we've already emitted the index buffer and are about to create the
+ // first table entry that would overflow our current code bit size.
+ if (next_code >= 1 << cur_code_size) ++cur_code_size;
+ code_table[cur_key] = next_code++; // Insert into code table.
+ }
+
+ ib_code = k; // Index buffer to single input k.
+ } else {
+ ib_code = cur_code; // Index buffer to sequence in code table.
+ }
+ }
+
+ emit_code(ib_code); // There will still be something in the index buffer.
+ emit_code(eoi_code); // End Of Information.
+
+ // Flush / finalize the sub-blocks stream to the buffer.
+ emit_bytes_to_buffer(1);
+
+ // Finish the sub-blocks, writing out any unfinished lengths and
+ // terminating with a sub-block of length 0. If we have already started
+ // but not yet used a sub-block it can just become the terminator.
+ if (cur_subblock + 1 === p) {
+ // Started but unused.
+ buf[cur_subblock] = 0;
+ } else {
+ // Started and used, write length and additional terminator block.
+ buf[cur_subblock] = p - cur_subblock - 1;
+ buf[p++] = 0;
+ }
+ return p;
+}
diff --git a/www/external/imagesloaded.pkgd.js b/www/external/origjs/imagesloaded.pkgd.js
index ef23971b..ef23971b 100644
--- a/www/external/imagesloaded.pkgd.js
+++ b/www/external/origjs/imagesloaded.pkgd.js
diff --git a/www/external/ion-pullup.js b/www/external/origjs/ion-pullup.js
index ae9109a8..ae9109a8 100755
--- a/www/external/ion-pullup.js
+++ b/www/external/origjs/ion-pullup.js
diff --git a/www/external/ionRadio.js b/www/external/origjs/ionRadio.js
index cb240b76..cb240b76 100644
--- a/www/external/ionRadio.js
+++ b/www/external/origjs/ionRadio.js
diff --git a/www/external/ionic.content.banner.js b/www/external/origjs/ionic.content.banner.js
index 900b96a6..900b96a6 100644
--- a/www/external/ionic.content.banner.js
+++ b/www/external/origjs/ionic.content.banner.js
diff --git a/www/external/ionic.scroll.sista.js b/www/external/origjs/ionic.scroll.sista.js
index f8dd8143..f8dd8143 100644
--- a/www/external/ionic.scroll.sista.js
+++ b/www/external/origjs/ionic.scroll.sista.js
diff --git a/www/external/ng-websocket.js b/www/external/origjs/ng-websocket.js
index 02f92d2b..02f92d2b 100644
--- a/www/external/ng-websocket.js
+++ b/www/external/origjs/ng-websocket.js
diff --git a/www/external/packery.pkgd.js b/www/external/origjs/packery.pkgd.js
index 7576576b..fa4e8308 100644
--- a/www/external/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
new file mode 100644
index 00000000..0a072330
--- /dev/null
+++ b/www/external/packery.pkgd.min.js
@@ -0,0 +1,270 @@
+/*!
+ * Packery PACKAGED v2.0.0
+ * Gapless, draggable grid layouts
+ *
+ * Licensed GPLv3 for open source use
+ * or Packery Commercial License for commercial use
+ *
+ * http://packery.metafizzy.co
+ * Copyright 2016 Metafizzy
+ */
+!function(t,e){"use strict"
+"function"==typeof define&&define.amd?define("jquery-bridget/jquery-bridget",["jquery"],function(i){e(t,i)}):"object"==typeof module&&module.exports?module.exports=e(t,require("jquery")):t.jQueryBridget=e(t,t.jQuery)}(window,function(t,e){"use strict"
+function i(i,s,a){function h(t,e,n){var o,s="$()."+i+'("'+e+'")'
+return t.each(function(t,h){var u=a.data(h,i)
+if(!u)return void r(i+" not initialized. Cannot call methods, i.e. "+s)
+var c=u[e]
+if(!c||"_"==e.charAt(0))return void r(s+" is not a valid method")
+var d=c.apply(u,n)
+o=void 0===o?d:o}),void 0!==o?o:t}function u(t,e){t.each(function(t,n){var o=a.data(n,i)
+o?(o.option(e),o._init()):(o=new s(n,e),a.data(n,i,o))})}a=a||e||t.jQuery,a&&(s.prototype.option||(s.prototype.option=function(t){a.isPlainObject(t)&&(this.options=a.extend(!0,this.options,t))}),a.fn[i]=function(t){if("string"==typeof t){var e=o.call(arguments,1)
+return h(this,t,e)}return u(this,t),this},n(a))}function n(t){!t||t&&t.bridget||(t.bridget=i)}var o=Array.prototype.slice,s=t.console,r="undefined"==typeof s?function(){}:function(t){s.error(t)}
+return n(e||t.jQuery),i}),function(t,e){"use strict"
+"function"==typeof define&&define.amd?define("get-size/get-size",[],function(){return e()}):"object"==typeof module&&module.exports?module.exports=e():t.getSize=e()}(window,function(){"use strict"
+function t(t){var e=parseFloat(t),i=t.indexOf("%")==-1&&!isNaN(e)
+return i&&e}function e(){}function i(){for(var t={width:0,height:0,innerWidth:0,innerHeight:0,outerWidth:0,outerHeight:0},e=0;e<u;e++){var i=h[e]
+t[i]=0}return t}function n(t){var e=getComputedStyle(t)
+return e||a("Style returned "+e+". Are you running this code in a hidden iframe on Firefox? See http://bit.ly/getsizebug1"),e}function o(){if(!c){c=!0
+var e=document.createElement("div")
+e.style.width="200px",e.style.padding="1px 2px 3px 4px",e.style.borderStyle="solid",e.style.borderWidth="1px 2px 3px 4px",e.style.boxSizing="border-box"
+var i=document.body||document.documentElement
+i.appendChild(e)
+var o=n(e)
+s.isBoxSizeOuter=r=200==Math.round(t(o.width)),i.removeChild(e)}}function s(e){if(o(),"string"==typeof e&&(e=document.querySelector(e)),e&&"object"==typeof e&&e.nodeType){var s=n(e)
+if("none"==s.display)return i()
+var a={}
+a.width=e.offsetWidth,a.height=e.offsetHeight
+for(var c=a.isBorderBox="border-box"==s.boxSizing,d=0;d<u;d++){var l=h[d],f=s[l],p=parseFloat(f)
+a[l]=isNaN(p)?0:p}var m=a.paddingLeft+a.paddingRight,g=a.paddingTop+a.paddingBottom,y=a.marginLeft+a.marginRight,v=a.marginTop+a.marginBottom,x=a.borderLeftWidth+a.borderRightWidth,_=a.borderTopWidth+a.borderBottomWidth,b=c&&r,E=t(s.width)
+E!==!1&&(a.width=E+(b?0:m+x))
+var z=t(s.height)
+return z!==!1&&(a.height=z+(b?0:g+_)),a.innerWidth=a.width-(m+x),a.innerHeight=a.height-(g+_),a.outerWidth=a.width+y,a.outerHeight=a.height+v,a}}var r,a="undefined"==typeof console?e:function(t){console.error(t)},h=["paddingLeft","paddingRight","paddingTop","paddingBottom","marginLeft","marginRight","marginTop","marginBottom","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth"],u=h.length,c=!1
+return s}),function(t,e){"function"==typeof define&&define.amd?define("ev-emitter/ev-emitter",e):"object"==typeof module&&module.exports?module.exports=e():t.EvEmitter=e()}(this,function(){function t(){}var e=t.prototype
+return e.on=function(t,e){if(t&&e){var i=this._events=this._events||{},n=i[t]=i[t]||[]
+return n.indexOf(e)==-1&&n.push(e),this}},e.once=function(t,e){if(t&&e){this.on(t,e)
+var i=this._onceEvents=this._onceEvents||{},n=i[t]=i[t]||{}
+return n[e]=!0,this}},e.off=function(t,e){var i=this._events&&this._events[t]
+if(i&&i.length){var n=i.indexOf(e)
+return n!=-1&&i.splice(n,1),this}},e.emitEvent=function(t,e){var i=this._events&&this._events[t]
+if(i&&i.length){var n=0,o=i[n]
+e=e||[]
+for(var s=this._onceEvents&&this._onceEvents[t];o;){var r=s&&s[o]
+r&&(this.off(t,o),delete s[o]),o.apply(this,e),n+=r?0:1,o=i[n]}return this}},t}),function(t,e){"use strict"
+"function"==typeof define&&define.amd?define("desandro-matches-selector/matches-selector",e):"object"==typeof module&&module.exports?module.exports=e():t.matchesSelector=e()}(window,function(){"use strict"
+var t=function(){var t=Element.prototype
+if(t.matches)return"matches"
+if(t.matchesSelector)return"matchesSelector"
+for(var e=["webkit","moz","ms","o"],i=0;i<e.length;i++){var n=e[i],o=n+"MatchesSelector"
+if(t[o])return o}}()
+return function(e,i){return e[t](i)}}),function(t,e){"function"==typeof define&&define.amd?define("fizzy-ui-utils/utils",["desandro-matches-selector/matches-selector"],function(i){return e(t,i)}):"object"==typeof module&&module.exports?module.exports=e(t,require("desandro-matches-selector")):t.fizzyUIUtils=e(t,t.matchesSelector)}(window,function(t,e){var i={}
+i.extend=function(t,e){for(var i in e)t[i]=e[i]
+return t},i.modulo=function(t,e){return(t%e+e)%e},i.makeArray=function(t){var e=[]
+if(Array.isArray(t))e=t
+else if(t&&"number"==typeof t.length)for(var i=0;i<t.length;i++)e.push(t[i])
+else e.push(t)
+return e},i.removeFrom=function(t,e){var i=t.indexOf(e)
+i!=-1&&t.splice(i,1)},i.getParent=function(t,i){for(;t!=document.body;)if(t=t.parentNode,e(t,i))return t},i.getQueryElement=function(t){return"string"==typeof t?document.querySelector(t):t},i.handleEvent=function(t){var e="on"+t.type
+this[e]&&this[e](t)},i.filterFindElements=function(t,n){t=i.makeArray(t)
+var o=[]
+return t.forEach(function(t){if(t instanceof HTMLElement){if(!n)return void o.push(t)
+e(t,n)&&o.push(t)
+for(var i=t.querySelectorAll(n),s=0;s<i.length;s++)o.push(i[s])}}),o},i.debounceMethod=function(t,e,i){var n=t.prototype[e],o=e+"Timeout"
+t.prototype[e]=function(){var t=this[o]
+t&&clearTimeout(t)
+var e=arguments,s=this
+this[o]=setTimeout(function(){n.apply(s,e),delete s[o]},i||100)}},i.docReady=function(t){"complete"==document.readyState?t():document.addEventListener("DOMContentLoaded",t)},i.toDashed=function(t){return t.replace(/(.)([A-Z])/g,function(t,e,i){return e+"-"+i}).toLowerCase()}
+var n=t.console
+return i.htmlInit=function(e,o){i.docReady(function(){var s=i.toDashed(o),r="data-"+s,a=document.querySelectorAll("["+r+"]"),h=document.querySelectorAll(".js-"+s),u=i.makeArray(a).concat(i.makeArray(h)),c=r+"-options",d=t.jQuery
+u.forEach(function(t){var i,s=t.getAttribute(r)||t.getAttribute(c)
+try{i=s&&JSON.parse(s)}catch(e){return void(n&&n.error("Error parsing "+r+" on "+t.className+": "+e))}var a=new e(t,i)
+d&&d.data(t,o,a)})})},i}),function(t,e){"function"==typeof define&&define.amd?define("outlayer/item",["ev-emitter/ev-emitter","get-size/get-size"],e):"object"==typeof module&&module.exports?module.exports=e(require("ev-emitter"),require("get-size")):(t.Outlayer={},t.Outlayer.Item=e(t.EvEmitter,t.getSize))}(window,function(t,e){"use strict"
+function i(t){for(var e in t)return!1
+return e=null,!0}function n(t,e){t&&(this.element=t,this.layout=e,this.position={x:0,y:0},this._create())}function o(t){return t.replace(/([A-Z])/g,function(t){return"-"+t.toLowerCase()})}var s=document.documentElement.style,r="string"==typeof s.transition?"transition":"WebkitTransition",a="string"==typeof s.transform?"transform":"WebkitTransform",h={WebkitTransition:"webkitTransitionEnd",transition:"transitionend"}[r],u={transform:a,transition:r,transitionDuration:r+"Duration",transitionProperty:r+"Property"},c=n.prototype=Object.create(t.prototype)
+c.constructor=n,c._create=function(){this._transn={ingProperties:{},clean:{},onEnd:{}},this.css({position:"absolute"})},c.handleEvent=function(t){var e="on"+t.type
+this[e]&&this[e](t)},c.getSize=function(){this.size=e(this.element)},c.css=function(t){var e=this.element.style
+for(var i in t){var n=u[i]||i
+e[n]=t[i]}},c.getPosition=function(){var t=getComputedStyle(this.element),e=this.layout._getOption("originLeft"),i=this.layout._getOption("originTop"),n=t[e?"left":"right"],o=t[i?"top":"bottom"],s=this.layout.size,r=n.indexOf("%")!=-1?parseFloat(n)/100*s.width:parseInt(n,10),a=o.indexOf("%")!=-1?parseFloat(o)/100*s.height:parseInt(o,10)
+r=isNaN(r)?0:r,a=isNaN(a)?0:a,r-=e?s.paddingLeft:s.paddingRight,a-=i?s.paddingTop:s.paddingBottom,this.position.x=r,this.position.y=a},c.layoutPosition=function(){var t=this.layout.size,e={},i=this.layout._getOption("originLeft"),n=this.layout._getOption("originTop"),o=i?"paddingLeft":"paddingRight",s=i?"left":"right",r=i?"right":"left",a=this.position.x+t[o]
+e[s]=this.getXValue(a),e[r]=""
+var h=n?"paddingTop":"paddingBottom",u=n?"top":"bottom",c=n?"bottom":"top",d=this.position.y+t[h]
+e[u]=this.getYValue(d),e[c]="",this.css(e),this.emitEvent("layout",[this])},c.getXValue=function(t){var e=this.layout._getOption("horizontal")
+return this.layout.options.percentPosition&&!e?t/this.layout.size.width*100+"%":t+"px"},c.getYValue=function(t){var e=this.layout._getOption("horizontal")
+return this.layout.options.percentPosition&&e?t/this.layout.size.height*100+"%":t+"px"},c._transitionTo=function(t,e){this.getPosition()
+var i=this.position.x,n=this.position.y,o=parseInt(t,10),s=parseInt(e,10),r=o===this.position.x&&s===this.position.y
+if(this.setPosition(t,e),r&&!this.isTransitioning)return void this.layoutPosition()
+var a=t-i,h=e-n,u={}
+u.transform=this.getTranslate(a,h),this.transition({to:u,onTransitionEnd:{transform:this.layoutPosition},isCleaning:!0})},c.getTranslate=function(t,e){var i=this.layout._getOption("originLeft"),n=this.layout._getOption("originTop")
+return t=i?t:-t,e=n?e:-e,"translate3d("+t+"px, "+e+"px, 0)"},c.goTo=function(t,e){this.setPosition(t,e),this.layoutPosition()},c.moveTo=c._transitionTo,c.setPosition=function(t,e){this.position.x=parseInt(t,10),this.position.y=parseInt(e,10)},c._nonTransition=function(t){this.css(t.to),t.isCleaning&&this._removeStyles(t.to)
+for(var e in t.onTransitionEnd)t.onTransitionEnd[e].call(this)},c.transition=function(t){if(!parseFloat(this.layout.options.transitionDuration))return void this._nonTransition(t)
+var e=this._transn
+for(var i in t.onTransitionEnd)e.onEnd[i]=t.onTransitionEnd[i]
+for(i in t.to)e.ingProperties[i]=!0,t.isCleaning&&(e.clean[i]=!0)
+if(t.from){this.css(t.from)
+var n=this.element.offsetHeight
+n=null}this.enableTransition(t.to),this.css(t.to),this.isTransitioning=!0}
+var d="opacity,"+o(a)
+c.enableTransition=function(){this.isTransitioning||(this.css({transitionProperty:d,transitionDuration:this.layout.options.transitionDuration}),this.element.addEventListener(h,this,!1))},c.onwebkitTransitionEnd=function(t){this.ontransitionend(t)},c.onotransitionend=function(t){this.ontransitionend(t)}
+var l={"-webkit-transform":"transform"}
+c.ontransitionend=function(t){if(t.target===this.element){var e=this._transn,n=l[t.propertyName]||t.propertyName
+if(delete e.ingProperties[n],i(e.ingProperties)&&this.disableTransition(),n in e.clean&&(this.element.style[t.propertyName]="",delete e.clean[n]),n in e.onEnd){var o=e.onEnd[n]
+o.call(this),delete e.onEnd[n]}this.emitEvent("transitionEnd",[this])}},c.disableTransition=function(){this.removeTransitionStyles(),this.element.removeEventListener(h,this,!1),this.isTransitioning=!1},c._removeStyles=function(t){var e={}
+for(var i in t)e[i]=""
+this.css(e)}
+var f={transitionProperty:"",transitionDuration:""}
+return c.removeTransitionStyles=function(){this.css(f)},c.removeElem=function(){this.element.parentNode.removeChild(this.element),this.css({display:""}),this.emitEvent("remove",[this])},c.remove=function(){return r&&parseFloat(this.layout.options.transitionDuration)?(this.once("transitionEnd",function(){this.removeElem()}),void this.hide()):void this.removeElem()},c.reveal=function(){delete this.isHidden,this.css({display:""})
+var t=this.layout.options,e={},i=this.getHideRevealTransitionEndProperty("visibleStyle")
+e[i]=this.onRevealTransitionEnd,this.transition({from:t.hiddenStyle,to:t.visibleStyle,isCleaning:!0,onTransitionEnd:e})},c.onRevealTransitionEnd=function(){this.isHidden||this.emitEvent("reveal")},c.getHideRevealTransitionEndProperty=function(t){var e=this.layout.options[t]
+if(e.opacity)return"opacity"
+for(var i in e)return i},c.hide=function(){this.isHidden=!0,this.css({display:""})
+var t=this.layout.options,e={},i=this.getHideRevealTransitionEndProperty("hiddenStyle")
+e[i]=this.onHideTransitionEnd,this.transition({from:t.visibleStyle,to:t.hiddenStyle,isCleaning:!0,onTransitionEnd:e})},c.onHideTransitionEnd=function(){this.isHidden&&(this.css({display:"none"}),this.emitEvent("hide"))},c.destroy=function(){this.css({position:"",left:"",right:"",top:"",bottom:"",transition:"",transform:""})},n}),function(t,e){"use strict"
+"function"==typeof define&&define.amd?define("outlayer/outlayer",["ev-emitter/ev-emitter","get-size/get-size","fizzy-ui-utils/utils","./item"],function(i,n,o,s){return e(t,i,n,o,s)}):"object"==typeof module&&module.exports?module.exports=e(t,require("ev-emitter"),require("get-size"),require("fizzy-ui-utils"),require("./item")):t.Outlayer=e(t,t.EvEmitter,t.getSize,t.fizzyUIUtils,t.Outlayer.Item)}(window,function(t,e,i,n,o){"use strict"
+function s(t,e){var i=n.getQueryElement(t)
+if(!i)return void(a&&a.error("Bad element for "+this.constructor.namespace+": "+(i||t)))
+this.element=i,h&&(this.$element=h(this.element)),this.options=n.extend({},this.constructor.defaults),this.option(e)
+var o=++c
+this.element.outlayerGUID=o,d[o]=this,this._create()
+var s=this._getOption("initLayout")
+s&&this.layout()}function r(t){function e(){t.apply(this,arguments)}return e.prototype=Object.create(t.prototype),e.prototype.constructor=e,e}var a=t.console,h=t.jQuery,u=function(){},c=0,d={}
+s.namespace="outlayer",s.Item=o,s.defaults={containerStyle:{position:"relative"},initLayout:!0,originLeft:!0,originTop:!0,resize:!0,resizeContainer:!0,transitionDuration:"0.4s",hiddenStyle:{opacity:0,transform:"scale(0.001)"},visibleStyle:{opacity:1,transform:"scale(1)"}}
+var l=s.prototype
+return n.extend(l,e.prototype),l.option=function(t){n.extend(this.options,t)},l._getOption=function(t){var e=this.constructor.compatOptions[t]
+return e&&void 0!==this.options[e]?this.options[e]:this.options[t]},s.compatOptions={initLayout:"isInitLayout",horizontal:"isHorizontal",layoutInstant:"isLayoutInstant",originLeft:"isOriginLeft",originTop:"isOriginTop",resize:"isResizeBound",resizeContainer:"isResizingContainer"},l._create=function(){this.reloadItems(),this.stamps=[],this.stamp(this.options.stamp),n.extend(this.element.style,this.options.containerStyle)
+var t=this._getOption("resize")
+t&&this.bindResize()},l.reloadItems=function(){this.items=this._itemize(this.element.children)},l._itemize=function(t){for(var e=this._filterFindItemElements(t),i=this.constructor.Item,n=[],o=0;o<e.length;o++){var s=e[o],r=new i(s,this)
+n.push(r)}return n},l._filterFindItemElements=function(t){return n.filterFindElements(t,this.options.itemSelector)},l.getItemElements=function(){return this.items.map(function(t){return t.element})},l.layout=function(){this._resetLayout(),this._manageStamps()
+var t=this._getOption("layoutInstant"),e=void 0!==t?t:!this._isLayoutInited
+this.layoutItems(this.items,e),this._isLayoutInited=!0},l._init=l.layout,l._resetLayout=function(){this.getSize()},l.getSize=function(){this.size=i(this.element)},l._getMeasurement=function(t,e){var n,o=this.options[t]
+o?("string"==typeof o?n=this.element.querySelector(o):o instanceof HTMLElement&&(n=o),this[t]=n?i(n)[e]:o):this[t]=0},l.layoutItems=function(t,e){t=this._getItemsForLayout(t),this._layoutItems(t,e),this._postLayout()},l._getItemsForLayout=function(t){return t.filter(function(t){return!t.isIgnored})},l._layoutItems=function(t,e){if(this._emitCompleteOnItems("layout",t),t&&t.length){var i=[]
+t.forEach(function(t){var n=this._getItemLayoutPosition(t)
+n.item=t,n.isInstant=e||t.isLayoutInstant,i.push(n)},this),this._processLayoutQueue(i)}},l._getItemLayoutPosition=function(){return{x:0,y:0}},l._processLayoutQueue=function(t){t.forEach(function(t){this._positionItem(t.item,t.x,t.y,t.isInstant)},this)},l._positionItem=function(t,e,i,n){n?t.goTo(e,i):t.moveTo(e,i)},l._postLayout=function(){this.resizeContainer()},l.resizeContainer=function(){var t=this._getOption("resizeContainer")
+if(t){var e=this._getContainerSize()
+e&&(this._setContainerMeasure(e.width,!0),this._setContainerMeasure(e.height,!1))}},l._getContainerSize=u,l._setContainerMeasure=function(t,e){if(void 0!==t){var i=this.size
+i.isBorderBox&&(t+=e?i.paddingLeft+i.paddingRight+i.borderLeftWidth+i.borderRightWidth:i.paddingBottom+i.paddingTop+i.borderTopWidth+i.borderBottomWidth),t=Math.max(t,0),this.element.style[e?"width":"height"]=t+"px"}},l._emitCompleteOnItems=function(t,e){function i(){o.dispatchEvent(t+"Complete",null,[e])}function n(){r++,r==s&&i()}var o=this,s=e.length
+if(!e||!s)return void i()
+var r=0
+e.forEach(function(e){e.once(t,n)})},l.dispatchEvent=function(t,e,i){var n=e?[e].concat(i):i
+if(this.emitEvent(t,n),h)if(this.$element=this.$element||h(this.element),e){var o=h.Event(e)
+o.type=t,this.$element.trigger(o,i)}else this.$element.trigger(t,i)},l.ignore=function(t){var e=this.getItem(t)
+e&&(e.isIgnored=!0)},l.unignore=function(t){var e=this.getItem(t)
+e&&delete e.isIgnored},l.stamp=function(t){t=this._find(t),t&&(this.stamps=this.stamps.concat(t),t.forEach(this.ignore,this))},l.unstamp=function(t){t=this._find(t),t&&t.forEach(function(t){n.removeFrom(this.stamps,t),this.unignore(t)},this)},l._find=function(t){if(t)return"string"==typeof t&&(t=this.element.querySelectorAll(t)),t=n.makeArray(t)},l._manageStamps=function(){this.stamps&&this.stamps.length&&(this._getBoundingRect(),this.stamps.forEach(this._manageStamp,this))},l._getBoundingRect=function(){var t=this.element.getBoundingClientRect(),e=this.size
+this._boundingRect={left:t.left+e.paddingLeft+e.borderLeftWidth,top:t.top+e.paddingTop+e.borderTopWidth,right:t.right-(e.paddingRight+e.borderRightWidth),bottom:t.bottom-(e.paddingBottom+e.borderBottomWidth)}},l._manageStamp=u,l._getElementOffset=function(t){var e=t.getBoundingClientRect(),n=this._boundingRect,o=i(t),s={left:e.left-n.left-o.marginLeft,top:e.top-n.top-o.marginTop,right:n.right-e.right-o.marginRight,bottom:n.bottom-e.bottom-o.marginBottom}
+return s},l.handleEvent=n.handleEvent,l.bindResize=function(){t.addEventListener("resize",this),this.isResizeBound=!0},l.unbindResize=function(){t.removeEventListener("resize",this),this.isResizeBound=!1},l.onresize=function(){this.resize()},n.debounceMethod(s,"onresize",100),l.resize=function(){this.isResizeBound&&this.needsResizeLayout()&&this.layout()},l.needsResizeLayout=function(){var t=i(this.element),e=this.size&&t
+return e&&t.innerWidth!==this.size.innerWidth},l.addItems=function(t){var e=this._itemize(t)
+return e.length&&(this.items=this.items.concat(e)),e},l.appended=function(t){var e=this.addItems(t)
+e.length&&(this.layoutItems(e,!0),this.reveal(e))},l.prepended=function(t){var e=this._itemize(t)
+if(e.length){var i=this.items.slice(0)
+this.items=e.concat(i),this._resetLayout(),this._manageStamps(),this.layoutItems(e,!0),this.reveal(e),this.layoutItems(i)}},l.reveal=function(t){this._emitCompleteOnItems("reveal",t),t&&t.length&&t.forEach(function(t){t.reveal()})},l.hide=function(t){this._emitCompleteOnItems("hide",t),t&&t.length&&t.forEach(function(t){t.hide()})},l.revealItemElements=function(t){var e=this.getItems(t)
+this.reveal(e)},l.hideItemElements=function(t){var e=this.getItems(t)
+this.hide(e)},l.getItem=function(t){for(var e=0;e<this.items.length;e++){var i=this.items[e]
+if(i.element==t)return i}},l.getItems=function(t){t=n.makeArray(t)
+var e=[]
+return t.forEach(function(t){var i=this.getItem(t)
+i&&e.push(i)},this),e},l.remove=function(t){var e=this.getItems(t)
+this._emitCompleteOnItems("remove",e),e&&e.length&&e.forEach(function(t){t.remove(),n.removeFrom(this.items,t)},this)},l.destroy=function(){var t=this.element.style
+t.height="",t.position="",t.width="",this.items.forEach(function(t){t.destroy()}),this.unbindResize()
+var e=this.element.outlayerGUID
+delete d[e],delete this.element.outlayerGUID,h&&h.removeData(this.element,this.constructor.namespace)},s.data=function(t){t=n.getQueryElement(t)
+var e=t&&t.outlayerGUID
+return e&&d[e]},s.create=function(t,e){var i=r(s)
+return i.defaults=n.extend({},s.defaults),n.extend(i.defaults,e),i.compatOptions=n.extend({},s.compatOptions),i.namespace=t,i.data=s.data,i.Item=r(o),n.htmlInit(i,t),h&&h.bridget&&h.bridget(t,i),i},s.Item=o,s}),function(t,e){"function"==typeof define&&define.amd?define("packery/rect",e):"object"==typeof module&&module.exports?module.exports=e():(t.Packery=t.Packery||{},t.Packery.Rect=e())}(window,function(){"use strict"
+function t(e){for(var i in t.defaults)this[i]=t.defaults[i]
+for(i in e)this[i]=e[i]}t.defaults={x:0,y:0,width:0,height:0}
+var e=t.prototype
+return e.contains=function(t){var e=t.width||0,i=t.height||0
+return this.x<=t.x&&this.y<=t.y&&this.x+this.width>=t.x+e&&this.y+this.height>=t.y+i},e.overlaps=function(t){var e=this.x+this.width,i=this.y+this.height,n=t.x+t.width,o=t.y+t.height
+return this.x<n&&e>t.x&&this.y<o&&i>t.y},e.getMaximalFreeRects=function(e){if(!this.overlaps(e))return!1
+var i,n=[],o=this.x+this.width,s=this.y+this.height,r=e.x+e.width,a=e.y+e.height
+return this.y<e.y&&(i=new t({x:this.x,y:this.y,width:this.width,height:e.y-this.y}),n.push(i)),o>r&&(i=new t({x:r,y:this.y,width:o-r,height:this.height}),n.push(i)),s>a&&(i=new t({x:this.x,y:a,width:this.width,height:s-a}),n.push(i)),this.x<e.x&&(i=new t({x:this.x,y:this.y,width:e.x-this.x,height:this.height}),n.push(i)),n},e.canFit=function(t){return this.width>=t.width&&this.height>=t.height},t}),function(t,e){if("function"==typeof define&&define.amd)define("packery/packer",["./rect"],e)
+else if("object"==typeof module&&module.exports)module.exports=e(require("./rect"))
+else{var i=t.Packery=t.Packery||{}
+i.Packer=e(i.Rect)}}(window,function(t){"use strict"
+function e(t,e,i){this.width=t||0,this.height=e||0,this.sortDirection=i||"downwardLeftToRight",this.reset()}var i=e.prototype
+i.reset=function(){this.spaces=[]
+var e=new t({x:0,y:0,width:this.width,height:this.height})
+this.spaces.push(e),this.sorter=n[this.sortDirection]||n.downwardLeftToRight},i.pack=function(t){for(var e=0;e<this.spaces.length;e++){var i=this.spaces[e]
+if(i.canFit(t)){this.placeInSpace(t,i)
+break}}},i.columnPack=function(t){for(var e=0;e<this.spaces.length;e++){var i=this.spaces[e],n=i.x<=t.x&&i.x+i.width>=t.x+t.width&&i.height>=t.height-.01
+if(n){t.y=i.y,this.placed(t)
+break}}},i.rowPack=function(t){for(var e=0;e<this.spaces.length;e++){var i=this.spaces[e],n=i.y<=t.y&&i.y+i.height>=t.y+t.height&&i.width>=t.width-.01
+if(n){t.x=i.x,this.placed(t)
+break}}},i.placeInSpace=function(t,e){t.x=e.x,t.y=e.y,this.placed(t)},i.placed=function(t){for(var e=[],i=0;i<this.spaces.length;i++){var n=this.spaces[i],o=n.getMaximalFreeRects(t)
+o?e.push.apply(e,o):e.push(n)}this.spaces=e,this.mergeSortSpaces()},i.mergeSortSpaces=function(){e.mergeRects(this.spaces),this.spaces.sort(this.sorter)},i.addSpace=function(t){this.spaces.push(t),this.mergeSortSpaces()},e.mergeRects=function(t){var e=0,i=t[e]
+t:for(;i;){for(var n=0,o=t[e+n];o;){if(o==i)n++
+else{if(o.contains(i)){t.splice(e,1),i=t[e]
+continue t}i.contains(o)?t.splice(e+n,1):n++}o=t[e+n]}e++,i=t[e]}return t}
+var n={downwardLeftToRight:function(t,e){return t.y-e.y||t.x-e.x},rightwardTopToBottom:function(t,e){return t.x-e.x||t.y-e.y}}
+return e}),function(t,e){"function"==typeof define&&define.amd?define("packery/item",["outlayer/outlayer","./rect"],e):"object"==typeof module&&module.exports?module.exports=e(require("outlayer"),require("./rect")):t.Packery.Item=e(t.Outlayer,t.Packery.Rect)}(window,function(t,e){"use strict"
+var i=document.documentElement.style,n="string"==typeof i.transform?"transform":"WebkitTransform",o=function(){t.Item.apply(this,arguments)},s=o.prototype=Object.create(t.Item.prototype),r=s._create
+s._create=function(){r.call(this),this.rect=new e}
+var a=s.moveTo
+return s.moveTo=function(t,e){var i=Math.abs(this.position.x-t),n=Math.abs(this.position.y-e),o=this.layout.dragItemCount&&!this.isPlacing&&!this.isTransitioning&&i<1&&n<1
+return o?void this.goTo(t,e):void a.apply(this,arguments)},s.enablePlacing=function(){this.removeTransitionStyles(),this.isTransitioning&&n&&(this.element.style[n]="none"),this.isTransitioning=!1,this.getSize(),this.layout._setRectSize(this.element,this.rect),this.isPlacing=!0},s.disablePlacing=function(){this.isPlacing=!1},s.removeElem=function(){this.element.parentNode.removeChild(this.element),this.layout.packer.addSpace(this.rect),this.emitEvent("remove",[this])},s.showDropPlaceholder=function(){var t=this.dropPlaceholder
+t||(t=this.dropPlaceholder=document.createElement("div"),t.className="packery-drop-placeholder",t.style.position="absolute"),t.style.width=this.size.width+"px",t.style.height=this.size.height+"px",this.positionDropPlaceholder(),this.layout.element.appendChild(t)},s.positionDropPlaceholder=function(){this.dropPlaceholder.style[n]="translate("+this.rect.x+"px, "+this.rect.y+"px)"},s.hideDropPlaceholder=function(){this.layout.element.removeChild(this.dropPlaceholder)},o}),function(t,e){"function"==typeof define&&define.amd?define(["get-size/get-size","outlayer/outlayer","./rect","./packer","./item"],e):"object"==typeof module&&module.exports?module.exports=e(require("get-size"),require("outlayer"),require("./rect"),require("./packer"),require("./item")):t.Packery=e(t.getSize,t.Outlayer,t.Packery.Rect,t.Packery.Packer,t.Packery.Item)}(window,function(t,e,i,n,o){"use strict"
+function s(t,e){return t.position.y-e.position.y||t.position.x-e.position.x}function r(t,e){return t.position.x-e.position.x||t.position.y-e.position.y}function a(t,e){var i=e.x-t.x,n=e.y-t.y
+return Math.sqrt(i*i+n*n)}i.prototype.canFit=function(t){return this.width>=t.width-1&&this.height>=t.height-1}
+var h=e.create("packery")
+h.Item=o
+var u=h.prototype
+h.prototype.getShiftPositions=function(t){t=t||"id"
+var e=this
+return this.items.map(function(i){return{attr:i.element.getAttribute(t),size:i.element.getAttribute("data-item-size"),display:i.element.getAttribute("data-item-listdisplay"),x:i.rect.x/e.packer.width,y:i.rect.y/e.packer.height}})},h.prototype.EHgetShiftPositions=function(t){t=t||"id"
+var e=this
+return this.items.map(function(i){return{attr:i.element.getAttribute(t),size:i.element.getAttribute("eh-data-item-size"),display:i.element.getAttribute("eh-data-item-listdisplay"),x:i.rect.x/e.packer.width,y:i.rect.y/e.packer.height}})},h.prototype.initShiftLayout=function(t,e){if(!t)return void this.layout()
+if("string"==typeof t)try{t=JSON.parse(t)}catch(t){return console.error("JSON parse error: "+t),void this.layout()}e=e||"id",this._resetLayout(),this.items=t.map(function(t){var i="["+e+'="'+t.attr+'"]',n=this.element.querySelector(i),o=this.getItem(n)
+return o.rect.x=t.x*this.packer.width,o.rect.y=t.y*this.packer.height,o},this),this.shiftLayout()},u._create=function(){e.prototype._create.call(this),this.packer=new n,this.shiftPacker=new n,this.isEnabled=!0,this.dragItemCount=0
+var t=this
+this.handleDraggabilly={dragStart:function(){t.itemDragStart(this.element)},dragMove:function(){t.itemDragMove(this.element,this.position.x,this.position.y)},dragEnd:function(){t.itemDragEnd(this.element)}},this.handleUIDraggable={start:function(e,i){i&&t.itemDragStart(e.currentTarget)},drag:function(e,i){i&&t.itemDragMove(e.currentTarget,i.position.left,i.position.top)},stop:function(e,i){i&&t.itemDragEnd(e.currentTarget)}}},u._resetLayout=function(){this.getSize(),this._getMeasurements()
+var t,e,i
+this._getOption("horizontal")?(t=1/0,e=this.size.innerHeight+this.gutter,i="rightwardTopToBottom"):(t=this.size.innerWidth+this.gutter,e=1/0,i="downwardLeftToRight"),this.packer.width=this.shiftPacker.width=t,this.packer.height=this.shiftPacker.height=e,this.packer.sortDirection=this.shiftPacker.sortDirection=i,this.packer.reset(),this.maxY=0,this.maxX=0},u._getMeasurements=function(){this._getMeasurement("columnWidth","width"),this._getMeasurement("rowHeight","height"),this._getMeasurement("gutter","width")},u._getItemLayoutPosition=function(t){if(this._setRectSize(t.element,t.rect),this.isShifting||this.dragItemCount>0){var e=this._getPackMethod()
+this.packer[e](t.rect)}else this.packer.pack(t.rect)
+return this._setMaxXY(t.rect),t.rect},u.shiftLayout=function(){this.isShifting=!0,this.layout(),delete this.isShifting},u._getPackMethod=function(){return this._getOption("horizontal")?"rowPack":"columnPack"},u._setMaxXY=function(t){this.maxX=Math.max(t.x+t.width,this.maxX),this.maxY=Math.max(t.y+t.height,this.maxY)},u._setRectSize=function(e,i){var n=t(e),o=n.outerWidth,s=n.outerHeight;(o||s)&&(o=this._applyGridGutter(o,this.columnWidth),s=this._applyGridGutter(s,this.rowHeight)),i.width=Math.min(o,this.packer.width),i.height=Math.min(s,this.packer.height)},u._applyGridGutter=function(t,e){if(!e)return t+this.gutter
+e+=this.gutter
+var i=t%e,n=i&&i<1?"round":"ceil"
+return t=Math[n](t/e)*e},u._getContainerSize=function(){return this._getOption("horizontal")?{width:this.maxX-this.gutter}:{height:this.maxY-this.gutter}},u._manageStamp=function(t){var e,n=this.getItem(t)
+if(n&&n.isPlacing)e=n.rect
+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(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
+e.forEach(function(t){var e=Math.round(t.rect[n]/a)
+t.rect[n]=e*h})}else{var u=t(this.element)[r]+this.gutter,c=this.packer[o]
+e.forEach(function(t){t.rect[n]=t.rect[n]/c*u})}this.shiftLayout()},u.itemDragStart=function(t){if(this.isEnabled){this.stamp(t)
+var e=this.getItem(t)
+e&&(e.enablePlacing(),e.showDropPlaceholder(),this.dragItemCount++,this.updateShiftTargets(e))}},u.updateShiftTargets=function(t){this.shiftPacker.reset(),this._getBoundingRect()
+var e=this._getOption("originLeft"),n=this._getOption("originTop")
+this.stamps.forEach(function(t){var o=this.getItem(t)
+if(!o||!o.isPlacing){var s=this._getElementOffset(t),r=new i({x:e?s.left:s.right,y:n?s.top:s.bottom})
+this._setRectSize(t,r),this.shiftPacker.placed(r)}},this)
+var o=this._getOption("horizontal"),s=o?"rowHeight":"columnWidth",r=o?"height":"width"
+this.shiftTargetKeys=[],this.shiftTargets=[]
+var a,h=this[s]
+if(h=h&&h+this.gutter){var u=Math.ceil(t.rect[r]/h),c=Math.floor((this.shiftPacker[r]+this.gutter)/h)
+a=(c-u)*h
+for(var d=0;d<c;d++)this._addShiftTarget(d*h,0,a)}else a=this.shiftPacker[r]+this.gutter-t.rect[r],this._addShiftTarget(0,0,a)
+var l=this._getItemsForLayout(this.items),f=this._getPackMethod()
+l.forEach(function(t){var e=t.rect
+this._setRectSize(t.element,e),this.shiftPacker[f](e),this._addShiftTarget(e.x,e.y,a)
+var i=o?e.x+e.width:e.x,n=o?e.y:e.y+e.height
+if(this._addShiftTarget(i,n,a),h)for(var s=Math.round(e[r]/h),u=1;u<s;u++){var c=o?i:e.x+h*u,d=o?e.y+h*u:n
+this._addShiftTarget(c,d,a)}},this)},u._addShiftTarget=function(t,e,i){var n=this._getOption("horizontal")?e:t
+if(!(0!==n&&n>i)){var o=t+","+e,s=this.shiftTargetKeys.indexOf(o)!=-1
+s||(this.shiftTargetKeys.push(o),this.shiftTargets.push({x:t,y:e}))}},u.shift=function(t,e,i){var n,o=1/0,s={x:e,y:i}
+this.shiftTargets.forEach(function(t){var e=a(t,s)
+e<o&&(n=t,o=e)}),t.rect.x=n.x,t.rect.y=n.y}
+var c=120
+u.itemDragMove=function(t,e,i){function n(){s.shift(o,e,i),o.positionDropPlaceholder(),s.layout()}var o=this.isEnabled&&this.getItem(t)
+if(o){e-=this.size.paddingLeft,i-=this.size.paddingTop
+var s=this,r=new Date
+this._itemDragTime&&r-this._itemDragTime<c?(clearTimeout(this.dragTimeout),this.dragTimeout=setTimeout(n,c)):(n(),this._itemDragTime=r)}},u.itemDragEnd=function(t){function e(){n++,2==n&&(i.element.classList.remove("is-positioning-post-drag"),i.hideDropPlaceholder(),o.dispatchEvent("dragItemPositioned",null,[i]))}var i=this.isEnabled&&this.getItem(t)
+if(i){clearTimeout(this.dragTimeout),i.element.classList.add("is-positioning-post-drag")
+var n=0,o=this
+i.once("layout",e),this.once("layoutComplete",e),i.moveTo(i.rect.x,i.rect.y),this.layout(),this.dragItemCount=Math.max(0,this.dragItemCount-1),this.sortItemsByPosition(),i.disablePlacing(),this.unstamp(i.element)}},u.bindDraggabillyEvents=function(t){this._bindDraggabillyEvents(t,"on")},u.unbindDraggabillyEvents=function(t){this._bindDraggabillyEvents(t,"off")},u._bindDraggabillyEvents=function(t,e){var i=this.handleDraggabilly
+t[e]("dragStart",i.dragStart),t[e]("dragMove",i.dragMove),t[e]("dragEnd",i.dragEnd)},u.bindUIDraggableEvents=function(t){this._bindUIDraggableEvents(t,"on")},u.unbindUIDraggableEvents=function(t){this._bindUIDraggableEvents(t,"off")},u._bindUIDraggableEvents=function(t,e){var i=this.handleUIDraggable
+t[e]("dragstart",i.start)[e]("drag",i.drag)[e]("dragstop",i.stop)}
+var d=u.destroy
+return u.destroy=function(){d.apply(this,arguments),this.isEnabled=!1},h.Rect=i,h.Packer=n,h})
diff --git a/www/external/polyfill.min.js b/www/external/polyfill.min.js
new file mode 100644
index 00000000..dd753762
--- /dev/null
+++ b/www/external/polyfill.min.js
@@ -0,0 +1,4 @@
+!function(f){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=f();else if("function"==typeof define&&define.amd)define([],f);else{var g;g="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,g["default"]=f()}}(function(){return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;o<r.length;o++)s(r[o]);return s}({1:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var _require=require("./spec/reference-implementation/lib/readable-stream"),ReadableStream=_require.ReadableStream,_require2=require("./spec/reference-implementation/lib/writable-stream"),WritableStream=_require2.WritableStream,ByteLengthQueuingStrategy=require("./spec/reference-implementation/lib/byte-length-queuing-strategy"),CountQueuingStrategy=require("./spec/reference-implementation/lib/count-queuing-strategy"),TransformStream=require("./spec/reference-implementation/lib/transform-stream");exports.ByteLengthQueuingStrategy=ByteLengthQueuingStrategy,exports.CountQueuingStrategy=CountQueuingStrategy,exports.TransformStream=TransformStream,exports.ReadableStream=ReadableStream,exports.WritableStream=WritableStream;var interfaces={ReadableStream:ReadableStream,WritableStream:WritableStream,ByteLengthQueuingStrategy:ByteLengthQueuingStrategy,CountQueuingStrategy:CountQueuingStrategy,TransformStream:TransformStream};exports["default"]=interfaces,"undefined"!=typeof window&&Object.assign(window,interfaces)},{"./spec/reference-implementation/lib/byte-length-queuing-strategy":7,"./spec/reference-implementation/lib/count-queuing-strategy":8,"./spec/reference-implementation/lib/readable-stream":11,"./spec/reference-implementation/lib/transform-stream":12,"./spec/reference-implementation/lib/writable-stream":14}],2:[function(require,module,exports){function replacer(key,value){return util.isUndefined(value)?""+value:util.isNumber(value)&&!isFinite(value)?value.toString():util.isFunction(value)||util.isRegExp(value)?value.toString():value}function truncate(s,n){return util.isString(s)?s.length<n?s:s.slice(0,n):s}function getMessage(self){return truncate(JSON.stringify(self.actual,replacer),128)+" "+self.operator+" "+truncate(JSON.stringify(self.expected,replacer),128)}function fail(actual,expected,message,operator,stackStartFunction){throw new assert.AssertionError({message:message,actual:actual,expected:expected,operator:operator,stackStartFunction:stackStartFunction})}function ok(value,message){value||fail(value,!0,message,"==",assert.ok)}function _deepEqual(actual,expected){if(actual===expected)return!0;if(util.isBuffer(actual)&&util.isBuffer(expected)){if(actual.length!=expected.length)return!1;for(var i=0;i<actual.length;i++)if(actual[i]!==expected[i])return!1;return!0}return util.isDate(actual)&&util.isDate(expected)?actual.getTime()===expected.getTime():util.isRegExp(actual)&&util.isRegExp(expected)?actual.source===expected.source&&actual.global===expected.global&&actual.multiline===expected.multiline&&actual.lastIndex===expected.lastIndex&&actual.ignoreCase===expected.ignoreCase:util.isObject(actual)||util.isObject(expected)?objEquiv(actual,expected):actual==expected}function isArguments(object){return"[object Arguments]"==Object.prototype.toString.call(object)}function objEquiv(a,b){if(util.isNullOrUndefined(a)||util.isNullOrUndefined(b))return!1;if(a.prototype!==b.prototype)return!1;if(util.isPrimitive(a)||util.isPrimitive(b))return a===b;var aIsArgs=isArguments(a),bIsArgs=isArguments(b);if(aIsArgs&&!bIsArgs||!aIsArgs&&bIsArgs)return!1;if(aIsArgs)return a=pSlice.call(a),b=pSlice.call(b),_deepEqual(a,b);var key,i,ka=objectKeys(a),kb=objectKeys(b);if(ka.length!=kb.length)return!1;for(ka.sort(),kb.sort(),i=ka.length-1;i>=0;i--)if(ka[i]!=kb[i])return!1;for(i=ka.length-1;i>=0;i--)if(key=ka[i],!_deepEqual(a[key],b[key]))return!1;return!0}function expectedException(actual,expected){return actual&&expected?"[object RegExp]"==Object.prototype.toString.call(expected)?expected.test(actual):actual instanceof expected?!0:expected.call({},actual)===!0:!1}function _throws(shouldThrow,block,expected,message){var actual;util.isString(expected)&&(message=expected,expected=null);try{block()}catch(e){actual=e}if(message=(expected&&expected.name?" ("+expected.name+").":".")+(message?" "+message:"."),shouldThrow&&!actual&&fail(actual,expected,"Missing expected exception"+message),!shouldThrow&&expectedException(actual,expected)&&fail(actual,expected,"Got unwanted exception"+message),shouldThrow&&actual&&expected&&!expectedException(actual,expected)||!shouldThrow&&actual)throw actual}var util=require("util/"),pSlice=Array.prototype.slice,hasOwn=Object.prototype.hasOwnProperty,assert=module.exports=ok;assert.AssertionError=function(options){this.name="AssertionError",this.actual=options.actual,this.expected=options.expected,this.operator=options.operator,options.message?(this.message=options.message,this.generatedMessage=!1):(this.message=getMessage(this),this.generatedMessage=!0);var stackStartFunction=options.stackStartFunction||fail;if(Error.captureStackTrace)Error.captureStackTrace(this,stackStartFunction);else{var err=new Error;if(err.stack){var out=err.stack,fn_name=stackStartFunction.name,idx=out.indexOf("\n"+fn_name);if(idx>=0){var next_line=out.indexOf("\n",idx+1);out=out.substring(next_line+1)}this.stack=out}}},util.inherits(assert.AssertionError,Error),assert.fail=fail,assert.ok=ok,assert.equal=function(actual,expected,message){actual!=expected&&fail(actual,expected,message,"==",assert.equal)},assert.notEqual=function(actual,expected,message){actual==expected&&fail(actual,expected,message,"!=",assert.notEqual)},assert.deepEqual=function(actual,expected,message){_deepEqual(actual,expected)||fail(actual,expected,message,"deepEqual",assert.deepEqual)},assert.notDeepEqual=function(actual,expected,message){_deepEqual(actual,expected)&&fail(actual,expected,message,"notDeepEqual",assert.notDeepEqual)},assert.strictEqual=function(actual,expected,message){actual!==expected&&fail(actual,expected,message,"===",assert.strictEqual)},assert.notStrictEqual=function(actual,expected,message){actual===expected&&fail(actual,expected,message,"!==",assert.notStrictEqual)},assert["throws"]=function(block,error,message){_throws.apply(this,[!0].concat(pSlice.call(arguments)))},assert.doesNotThrow=function(block,message){_throws.apply(this,[!1].concat(pSlice.call(arguments)))},assert.ifError=function(err){if(err)throw err};var objectKeys=Object.keys||function(obj){var keys=[];for(var key in obj)hasOwn.call(obj,key)&&keys.push(key);return keys}},{"util/":6}],3:[function(require,module,exports){"function"==typeof Object.create?module.exports=function(ctor,superCtor){ctor.super_=superCtor,ctor.prototype=Object.create(superCtor.prototype,{constructor:{value:ctor,enumerable:!1,writable:!0,configurable:!0}})}:module.exports=function(ctor,superCtor){ctor.super_=superCtor;var TempCtor=function(){};TempCtor.prototype=superCtor.prototype,ctor.prototype=new TempCtor,ctor.prototype.constructor=ctor}},{}],4:[function(require,module,exports){function cleanUpNextTick(){draining&&currentQueue&&(draining=!1,currentQueue.length?queue=currentQueue.concat(queue):queueIndex=-1,queue.length&&drainQueue())}function drainQueue(){if(!draining){var timeout=cachedSetTimeout(cleanUpNextTick);draining=!0;for(var len=queue.length;len;){for(currentQueue=queue,queue=[];++queueIndex<len;)currentQueue&&currentQueue[queueIndex].run();queueIndex=-1,len=queue.length}currentQueue=null,draining=!1,cachedClearTimeout(timeout)}}function Item(fun,array){this.fun=fun,this.array=array}function noop(){}var cachedSetTimeout,cachedClearTimeout,process=module.exports={};!function(){try{cachedSetTimeout=setTimeout}catch(e){cachedSetTimeout=function(){throw new Error("setTimeout is not defined")}}try{cachedClearTimeout=clearTimeout}catch(e){cachedClearTimeout=function(){throw new Error("clearTimeout is not defined")}}}();var currentQueue,queue=[],draining=!1,queueIndex=-1;process.nextTick=function(fun){var args=new Array(arguments.length-1);if(arguments.length>1)for(var i=1;i<arguments.length;i++)args[i-1]=arguments[i];queue.push(new Item(fun,args)),1!==queue.length||draining||cachedSetTimeout(drainQueue,0)},Item.prototype.run=function(){this.fun.apply(null,this.array)},process.title="browser",process.browser=!0,process.env={},process.argv=[],process.version="",process.versions={},process.on=noop,process.addListener=noop,process.once=noop,process.off=noop,process.removeListener=noop,process.removeAllListeners=noop,process.emit=noop,process.binding=function(name){throw new Error("process.binding is not supported")},process.cwd=function(){return"/"},process.chdir=function(dir){throw new Error("process.chdir is not supported")},process.umask=function(){return 0}},{}],5:[function(require,module,exports){module.exports=function(arg){return arg&&"object"==typeof arg&&"function"==typeof arg.copy&&"function"==typeof arg.fill&&"function"==typeof arg.readUInt8}},{}],6:[function(require,module,exports){(function(process,global){function inspect(obj,opts){var ctx={seen:[],stylize:stylizeNoColor};return arguments.length>=3&&(ctx.depth=arguments[2]),arguments.length>=4&&(ctx.colors=arguments[3]),isBoolean(opts)?ctx.showHidden=opts:opts&&exports._extend(ctx,opts),isUndefined(ctx.showHidden)&&(ctx.showHidden=!1),isUndefined(ctx.depth)&&(ctx.depth=2),isUndefined(ctx.colors)&&(ctx.colors=!1),isUndefined(ctx.customInspect)&&(ctx.customInspect=!0),ctx.colors&&(ctx.stylize=stylizeWithColor),formatValue(ctx,obj,ctx.depth)}function stylizeWithColor(str,styleType){var style=inspect.styles[styleType];return style?"["+inspect.colors[style][0]+"m"+str+"["+inspect.colors[style][1]+"m":str}function stylizeNoColor(str,styleType){return str}function arrayToHash(array){var hash={};return array.forEach(function(val,idx){hash[val]=!0}),hash}function formatValue(ctx,value,recurseTimes){if(ctx.customInspect&&value&&isFunction(value.inspect)&&value.inspect!==exports.inspect&&(!value.constructor||value.constructor.prototype!==value)){var ret=value.inspect(recurseTimes,ctx);return isString(ret)||(ret=formatValue(ctx,ret,recurseTimes)),ret}var primitive=formatPrimitive(ctx,value);if(primitive)return primitive;var keys=Object.keys(value),visibleKeys=arrayToHash(keys);if(ctx.showHidden&&(keys=Object.getOwnPropertyNames(value)),isError(value)&&(keys.indexOf("message")>=0||keys.indexOf("description")>=0))return formatError(value);if(0===keys.length){if(isFunction(value)){var name=value.name?": "+value.name:"";return ctx.stylize("[Function"+name+"]","special")}if(isRegExp(value))return ctx.stylize(RegExp.prototype.toString.call(value),"regexp");if(isDate(value))return ctx.stylize(Date.prototype.toString.call(value),"date");if(isError(value))return formatError(value)}var base="",array=!1,braces=["{","}"];if(isArray(value)&&(array=!0,braces=["[","]"]),isFunction(value)){var n=value.name?": "+value.name:"";base=" [Function"+n+"]"}if(isRegExp(value)&&(base=" "+RegExp.prototype.toString.call(value)),isDate(value)&&(base=" "+Date.prototype.toUTCString.call(value)),isError(value)&&(base=" "+formatError(value)),0===keys.length&&(!array||0==value.length))return braces[0]+base+braces[1];if(0>recurseTimes)return isRegExp(value)?ctx.stylize(RegExp.prototype.toString.call(value),"regexp"):ctx.stylize("[Object]","special");ctx.seen.push(value);var output;return output=array?formatArray(ctx,value,recurseTimes,visibleKeys,keys):keys.map(function(key){return formatProperty(ctx,value,recurseTimes,visibleKeys,key,array)}),ctx.seen.pop(),reduceToSingleString(output,base,braces)}function formatPrimitive(ctx,value){if(isUndefined(value))return ctx.stylize("undefined","undefined");if(isString(value)){var simple="'"+JSON.stringify(value).replace(/^"|"$/g,"").replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return ctx.stylize(simple,"string")}return isNumber(value)?ctx.stylize(""+value,"number"):isBoolean(value)?ctx.stylize(""+value,"boolean"):isNull(value)?ctx.stylize("null","null"):void 0}function formatError(value){return"["+Error.prototype.toString.call(value)+"]"}function formatArray(ctx,value,recurseTimes,visibleKeys,keys){for(var output=[],i=0,l=value.length;l>i;++i)hasOwnProperty(value,String(i))?output.push(formatProperty(ctx,value,recurseTimes,visibleKeys,String(i),!0)):output.push("");return keys.forEach(function(key){key.match(/^\d+$/)||output.push(formatProperty(ctx,value,recurseTimes,visibleKeys,key,!0))}),output}function formatProperty(ctx,value,recurseTimes,visibleKeys,key,array){var name,str,desc;if(desc=Object.getOwnPropertyDescriptor(value,key)||{value:value[key]},desc.get?str=desc.set?ctx.stylize("[Getter/Setter]","special"):ctx.stylize("[Getter]","special"):desc.set&&(str=ctx.stylize("[Setter]","special")),hasOwnProperty(visibleKeys,key)||(name="["+key+"]"),str||(ctx.seen.indexOf(desc.value)<0?(str=isNull(recurseTimes)?formatValue(ctx,desc.value,null):formatValue(ctx,desc.value,recurseTimes-1),str.indexOf("\n")>-1&&(str=array?str.split("\n").map(function(line){return" "+line}).join("\n").substr(2):"\n"+str.split("\n").map(function(line){return" "+line}).join("\n"))):str=ctx.stylize("[Circular]","special")),isUndefined(name)){if(array&&key.match(/^\d+$/))return str;name=JSON.stringify(""+key),name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(name=name.substr(1,name.length-2),name=ctx.stylize(name,"name")):(name=name.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),name=ctx.stylize(name,"string"))}return name+": "+str}function reduceToSingleString(output,base,braces){var numLinesEst=0,length=output.reduce(function(prev,cur){return numLinesEst++,cur.indexOf("\n")>=0&&numLinesEst++,prev+cur.replace(/\u001b\[\d\d?m/g,"").length+1},0);return length>60?braces[0]+(""===base?"":base+"\n ")+" "+output.join(",\n ")+" "+braces[1]:braces[0]+base+" "+output.join(", ")+" "+braces[1]}function isArray(ar){return Array.isArray(ar)}function isBoolean(arg){return"boolean"==typeof arg}function isNull(arg){return null===arg}function isNullOrUndefined(arg){return null==arg}function isNumber(arg){return"number"==typeof arg}function isString(arg){return"string"==typeof arg}function isSymbol(arg){return"symbol"==typeof arg}function isUndefined(arg){return void 0===arg}function isRegExp(re){return isObject(re)&&"[object RegExp]"===objectToString(re)}function isObject(arg){return"object"==typeof arg&&null!==arg}function isDate(d){return isObject(d)&&"[object Date]"===objectToString(d)}function isError(e){return isObject(e)&&("[object Error]"===objectToString(e)||e instanceof Error)}function isFunction(arg){return"function"==typeof arg}function isPrimitive(arg){return null===arg||"boolean"==typeof arg||"number"==typeof arg||"string"==typeof arg||"symbol"==typeof arg||"undefined"==typeof arg}function objectToString(o){return Object.prototype.toString.call(o)}function pad(n){return 10>n?"0"+n.toString(10):n.toString(10)}function timestamp(){var d=new Date,time=[pad(d.getHours()),pad(d.getMinutes()),pad(d.getSeconds())].join(":");return[d.getDate(),months[d.getMonth()],time].join(" ")}function hasOwnProperty(obj,prop){return Object.prototype.hasOwnProperty.call(obj,prop)}var formatRegExp=/%[sdj%]/g;exports.format=function(f){if(!isString(f)){for(var objects=[],i=0;i<arguments.length;i++)objects.push(inspect(arguments[i]));return objects.join(" ")}for(var i=1,args=arguments,len=args.length,str=String(f).replace(formatRegExp,function(x){if("%%"===x)return"%";if(i>=len)return x;switch(x){case"%s":return String(args[i++]);case"%d":return Number(args[i++]);case"%j":try{return JSON.stringify(args[i++])}catch(_){return"[Circular]"}default:return x}}),x=args[i];len>i;x=args[++i])str+=isNull(x)||!isObject(x)?" "+x:" "+inspect(x);return str},exports.deprecate=function(fn,msg){function deprecated(){if(!warned){if(process.throwDeprecation)throw new Error(msg);process.traceDeprecation?console.trace(msg):console.error(msg),warned=!0}return fn.apply(this,arguments)}if(isUndefined(global.process))return function(){return exports.deprecate(fn,msg).apply(this,arguments)};if(process.noDeprecation===!0)return fn;var warned=!1;return deprecated};var debugEnviron,debugs={};exports.debuglog=function(set){if(isUndefined(debugEnviron)&&(debugEnviron=process.env.NODE_DEBUG||""),set=set.toUpperCase(),!debugs[set])if(new RegExp("\\b"+set+"\\b","i").test(debugEnviron)){var pid=process.pid;debugs[set]=function(){var msg=exports.format.apply(exports,arguments);console.error("%s %d: %s",set,pid,msg)}}else debugs[set]=function(){};return debugs[set]},exports.inspect=inspect,inspect.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]},inspect.styles={special:"cyan",number:"yellow","boolean":"yellow",undefined:"grey","null":"bold",string:"green",date:"magenta",regexp:"red"},exports.isArray=isArray,exports.isBoolean=isBoolean,exports.isNull=isNull,exports.isNullOrUndefined=isNullOrUndefined,exports.isNumber=isNumber,exports.isString=isString,exports.isSymbol=isSymbol,exports.isUndefined=isUndefined,exports.isRegExp=isRegExp,exports.isObject=isObject,exports.isDate=isDate,exports.isError=isError,exports.isFunction=isFunction,exports.isPrimitive=isPrimitive,exports.isBuffer=require("./support/isBuffer");var months=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];exports.log=function(){console.log("%s - %s",timestamp(),exports.format.apply(exports,arguments))},exports.inherits=require("inherits"),exports._extend=function(origin,add){if(!add||!isObject(add))return origin;for(var keys=Object.keys(add),i=keys.length;i--;)origin[keys[i]]=add[keys[i]];return origin}}).call(this,require("_process"),"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./support/isBuffer":5,_process:4,inherits:3}],7:[function(require,module,exports){"use strict";function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor))throw new TypeError("Cannot call a class as a function")}var _createClass=function(){function defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||!1,descriptor.configurable=!0,"value"in descriptor&&(descriptor.writable=!0),Object.defineProperty(target,descriptor.key,descriptor)}}return function(Constructor,protoProps,staticProps){return protoProps&&defineProperties(Constructor.prototype,protoProps),staticProps&&defineProperties(Constructor,staticProps),Constructor}}(),_require=require("./helpers.js"),createDataProperty=_require.createDataProperty;module.exports=function(){function ByteLengthQueuingStrategy(_ref){var highWaterMark=_ref.highWaterMark;_classCallCheck(this,ByteLengthQueuingStrategy),createDataProperty(this,"highWaterMark",highWaterMark)}return _createClass(ByteLengthQueuingStrategy,[{key:"size",value:function(chunk){return chunk.byteLength}}]),ByteLengthQueuingStrategy}()},{"./helpers.js":9}],8:[function(require,module,exports){"use strict";function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor))throw new TypeError("Cannot call a class as a function")}var _createClass=function(){function defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||!1,descriptor.configurable=!0,"value"in descriptor&&(descriptor.writable=!0),Object.defineProperty(target,descriptor.key,descriptor)}}return function(Constructor,protoProps,staticProps){return protoProps&&defineProperties(Constructor.prototype,protoProps),staticProps&&defineProperties(Constructor,staticProps),Constructor}}(),_require=require("./helpers.js"),createDataProperty=_require.createDataProperty;module.exports=function(){function CountQueuingStrategy(_ref){var highWaterMark=_ref.highWaterMark;_classCallCheck(this,CountQueuingStrategy),createDataProperty(this,"highWaterMark",highWaterMark)}return _createClass(CountQueuingStrategy,[{key:"size",value:function(){return 1}}]),CountQueuingStrategy}()},{"./helpers.js":9}],9:[function(require,module,exports){"use strict";var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(obj){return typeof obj}:function(obj){return obj&&"function"==typeof Symbol&&obj.constructor===Symbol?"symbol":typeof obj},assert=require("assert");exports.promiseCall=function(func){for(var _len=arguments.length,args=Array(_len>1?_len-1:0),_key=1;_len>_key;_key++)args[_key-1]=arguments[_key];try{return Promise.resolve(func.apply(void 0,args))}catch(e){return Promise.reject(e)}},exports.typeIsObject=function(x){return"object"===("undefined"==typeof x?"undefined":_typeof(x))&&null!==x||"function"==typeof x},exports.toInteger=function(v){return v=Number(v),isNaN(v)?0:0>v?-1*Math.floor(Math.abs(v)):Math.floor(Math.abs(v))},exports.createDataProperty=function(o,p,v){assert(exports.typeIsObject(o)),Object.defineProperty(o,p,{value:v,writable:!0,enumerable:!0,configurable:!0})},exports.createArrayFromList=function(elements){return elements.slice()},exports.ArrayBufferCopy=function(dest,destOffset,src,srcOffset,n){new Uint8Array(dest).set(new Uint8Array(src,srcOffset,n),destOffset)},exports.CreateIterResultObject=function(value,done){assert("boolean"==typeof done);var obj={};return Object.defineProperty(obj,"value",{value:value,enumerable:!0,writable:!0,configurable:!0}),Object.defineProperty(obj,"done",{value:done,enumerable:!0,writable:!0,configurable:!0}),obj},exports.IsFiniteNonNegativeNumber=function(v){return Number.isNaN(v)?!1:v===1/0?!1:!(0>v)},exports.InvokeOrNoop=function(O,P,args){var method=O[P];if(void 0!==method)return method.apply(O,args)},exports.PromiseInvokeOrNoop=function(O,P,args){var method=void 0;try{method=O[P]}catch(methodE){return Promise.reject(methodE)}if(void 0===method)return Promise.resolve(void 0);try{return Promise.resolve(method.apply(O,args))}catch(e){return Promise.reject(e)}},exports.PromiseInvokeOrFallbackOrNoop=function(O,P1,args1,P2,args2){var method=void 0;try{method=O[P1]}catch(methodE){return Promise.reject(methodE)}if(void 0===method)return exports.PromiseInvokeOrNoop(O,P2,args2);try{return Promise.resolve(method.apply(O,args1))}catch(e){return Promise.reject(e)}},exports.SameRealmTransfer=function(O){return O},exports.ValidateAndNormalizeHighWaterMark=function(highWaterMark){if(highWaterMark=Number(highWaterMark),Number.isNaN(highWaterMark)||0>highWaterMark)throw new RangeError("highWaterMark property of a queuing strategy must be nonnegative and non-NaN");return highWaterMark},exports.ValidateAndNormalizeQueuingStrategy=function(size,highWaterMark){if(void 0!==size&&"function"!=typeof size)throw new TypeError("size property of a queuing strategy must be a function");return highWaterMark=exports.ValidateAndNormalizeHighWaterMark(highWaterMark),{size:size,highWaterMark:highWaterMark}}},{assert:2}],10:[function(require,module,exports){"use strict";var assert=require("assert"),_require=require("./helpers.js"),IsFiniteNonNegativeNumber=_require.IsFiniteNonNegativeNumber;exports.DequeueValue=function(queue){assert(queue.length>0,"Spec-level failure: should never dequeue from an empty queue.");var pair=queue.shift();return queue._totalSize-=pair.size,pair.value},exports.EnqueueValueWithSize=function(queue,value,size){if(size=Number(size),!IsFiniteNonNegativeNumber(size))throw new RangeError("Size must be a finite, non-NaN, non-negative number.");queue.push({value:value,size:size}),void 0===queue._totalSize&&(queue._totalSize=0),queue._totalSize+=size},exports.GetTotalQueueSize=function(queue){return void 0===queue._totalSize&&(queue._totalSize=0),queue._totalSize},exports.PeekQueueValue=function(queue){assert(queue.length>0,"Spec-level failure: should never peek at an empty queue.");var pair=queue[0];return pair.value}},{"./helpers.js":9,assert:2}],11:[function(require,module,exports){"use strict";function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor))throw new TypeError("Cannot call a class as a function")}function AcquireReadableStreamBYOBReader(stream){return new ReadableStreamBYOBReader(stream)}function AcquireReadableStreamDefaultReader(stream){return new ReadableStreamDefaultReader(stream)}function IsReadableStream(x){return typeIsObject(x)?!!Object.prototype.hasOwnProperty.call(x,"_readableStreamController"):!1}function IsReadableStreamDisturbed(stream){return assert(IsReadableStream(stream)===!0,"IsReadableStreamDisturbed should only be used on known readable streams"),stream._disturbed}function IsReadableStreamLocked(stream){return assert(IsReadableStream(stream)===!0,"IsReadableStreamLocked should only be used on known readable streams"),void 0!==stream._reader}function ReadableStreamTee(stream,shouldClone){assert(IsReadableStream(stream)===!0),assert("boolean"==typeof shouldClone);var reader=AcquireReadableStreamDefaultReader(stream),teeState={closedOrErrored:!1,canceled1:!1,canceled2:!1,reason1:void 0,reason2:void 0};teeState.promise=new Promise(function(resolve){teeState._resolve=resolve});var pull=create_ReadableStreamTeePullFunction();pull._reader=reader,pull._teeState=teeState,pull._shouldClone=shouldClone;var cancel1=create_ReadableStreamTeeBranch1CancelFunction();cancel1._stream=stream,cancel1._teeState=teeState;var cancel2=create_ReadableStreamTeeBranch2CancelFunction();cancel2._stream=stream,cancel2._teeState=teeState;var underlyingSource1=Object.create(Object.prototype);createDataProperty(underlyingSource1,"pull",pull),createDataProperty(underlyingSource1,"cancel",cancel1);var branch1Stream=new ReadableStream(underlyingSource1),underlyingSource2=Object.create(Object.prototype);createDataProperty(underlyingSource2,"pull",pull),createDataProperty(underlyingSource2,"cancel",cancel2);var branch2Stream=new ReadableStream(underlyingSource2);return pull._branch1=branch1Stream._readableStreamController,pull._branch2=branch2Stream._readableStreamController,reader._closedPromise["catch"](function(r){teeState.closedOrErrored!==!0&&(ReadableStreamDefaultControllerError(pull._branch1,r),ReadableStreamDefaultControllerError(pull._branch2,r),teeState.closedOrErrored=!0)}),[branch1Stream,branch2Stream]}function create_ReadableStreamTeePullFunction(){function f(){var reader=f._reader,branch1=f._branch1,branch2=f._branch2,teeState=f._teeState;return ReadableStreamDefaultReaderRead(reader).then(function(result){assert(typeIsObject(result));var value=result.value,done=result.done;if(assert("boolean"==typeof done),done===!0&&teeState.closedOrErrored===!1&&(teeState.canceled1===!1&&ReadableStreamDefaultControllerClose(branch1),teeState.canceled2===!1&&ReadableStreamDefaultControllerClose(branch2),teeState.closedOrErrored=!0),teeState.closedOrErrored!==!0){if(teeState.canceled1===!1){var value1=value;ReadableStreamDefaultControllerEnqueue(branch1,value1)}if(teeState.canceled2===!1){var value2=value;ReadableStreamDefaultControllerEnqueue(branch2,value2)}}})}return f}function create_ReadableStreamTeeBranch1CancelFunction(){function f(reason){var stream=f._stream,teeState=f._teeState;if(teeState.canceled1=!0,teeState.reason1=reason,teeState.canceled2===!0){var compositeReason=createArrayFromList([teeState.reason1,teeState.reason2]),cancelResult=ReadableStreamCancel(stream,compositeReason);teeState._resolve(cancelResult)}return teeState.promise}return f}function create_ReadableStreamTeeBranch2CancelFunction(){function f(reason){var stream=f._stream,teeState=f._teeState;if(teeState.canceled2=!0,teeState.reason2=reason,teeState.canceled1===!0){var compositeReason=createArrayFromList([teeState.reason1,teeState.reason2]),cancelResult=ReadableStreamCancel(stream,compositeReason);teeState._resolve(cancelResult)}return teeState.promise}return f}function ReadableStreamAddReadIntoRequest(stream){assert(IsReadableStreamBYOBReader(stream._reader)===!0),assert("readable"===stream._state||"closed"===stream._state);var promise=new Promise(function(resolve,reject){var readIntoRequest={_resolve:resolve,_reject:reject};stream._reader._readIntoRequests.push(readIntoRequest)});return promise}function ReadableStreamAddReadRequest(stream){assert(IsReadableStreamDefaultReader(stream._reader)===!0),assert("readable"===stream._state);var promise=new Promise(function(resolve,reject){var readRequest={_resolve:resolve,_reject:reject};stream._reader._readRequests.push(readRequest)});return promise}function ReadableStreamCancel(stream,reason){if(stream._disturbed=!0,"closed"===stream._state)return Promise.resolve(void 0);if("errored"===stream._state)return Promise.reject(stream._storedError);ReadableStreamClose(stream);var sourceCancelPromise=stream._readableStreamController[InternalCancel](reason);return sourceCancelPromise.then(function(){})}function ReadableStreamClose(stream){assert("readable"===stream._state),stream._state="closed";var reader=stream._reader;if(void 0!==reader){if(IsReadableStreamDefaultReader(reader)===!0){var _iteratorNormalCompletion=!0,_didIteratorError=!1,_iteratorError=void 0;try{for(var _step,_iterator=reader._readRequests[Symbol.iterator]();!(_iteratorNormalCompletion=(_step=_iterator.next()).done);_iteratorNormalCompletion=!0){var _resolve=_step.value._resolve;_resolve(CreateIterResultObject(void 0,!0))}}catch(err){_didIteratorError=!0,_iteratorError=err}finally{try{!_iteratorNormalCompletion&&_iterator["return"]&&_iterator["return"]()}finally{if(_didIteratorError)throw _iteratorError}}reader._readRequests=[]}defaultReaderClosedPromiseResolve(reader)}}function ReadableStreamError(stream,e){assert(IsReadableStream(stream)===!0,"stream must be ReadableStream"),assert("readable"===stream._state,"state must be readable"),stream._state="errored",stream._storedError=e;var reader=stream._reader;if(void 0!==reader){if(IsReadableStreamDefaultReader(reader)===!0){var _iteratorNormalCompletion2=!0,_didIteratorError2=!1,_iteratorError2=void 0;try{for(var _step2,_iterator2=reader._readRequests[Symbol.iterator]();!(_iteratorNormalCompletion2=(_step2=_iterator2.next()).done);_iteratorNormalCompletion2=!0){var readRequest=_step2.value;readRequest._reject(e)}}catch(err){_didIteratorError2=!0,_iteratorError2=err}finally{try{!_iteratorNormalCompletion2&&_iterator2["return"]&&_iterator2["return"]()}finally{if(_didIteratorError2)throw _iteratorError2}}reader._readRequests=[]}else{assert(IsReadableStreamBYOBReader(reader),"reader must be ReadableStreamBYOBReader");var _iteratorNormalCompletion3=!0,_didIteratorError3=!1,_iteratorError3=void 0;try{for(var _step3,_iterator3=reader._readIntoRequests[Symbol.iterator]();!(_iteratorNormalCompletion3=(_step3=_iterator3.next()).done);_iteratorNormalCompletion3=!0){var readIntoRequest=_step3.value;readIntoRequest._reject(e)}}catch(err){_didIteratorError3=!0,_iteratorError3=err}finally{try{!_iteratorNormalCompletion3&&_iterator3["return"]&&_iterator3["return"]()}finally{if(_didIteratorError3)throw _iteratorError3}}reader._readIntoRequests=[]}defaultReaderClosedPromiseReject(reader,e)}}function ReadableStreamFulfillReadIntoRequest(stream,chunk,done){var reader=stream._reader;assert(reader._readIntoRequests.length>0);var readIntoRequest=reader._readIntoRequests.shift();readIntoRequest._resolve(CreateIterResultObject(chunk,done))}function ReadableStreamFulfillReadRequest(stream,chunk,done){var reader=stream._reader;assert(reader._readRequests.length>0);var readRequest=reader._readRequests.shift();readRequest._resolve(CreateIterResultObject(chunk,done))}function ReadableStreamGetNumReadIntoRequests(stream){return stream._reader._readIntoRequests.length}function ReadableStreamGetNumReadRequests(stream){
+return stream._reader._readRequests.length}function ReadableStreamHasBYOBReader(stream){var reader=stream._reader;return void 0===reader?!1:IsReadableStreamBYOBReader(reader)!==!1}function ReadableStreamHasDefaultReader(stream){var reader=stream._reader;return void 0===reader?!1:IsReadableStreamDefaultReader(reader)!==!1}function IsReadableStreamBYOBReader(x){return typeIsObject(x)?!!Object.prototype.hasOwnProperty.call(x,"_readIntoRequests"):!1}function IsReadableStreamDefaultReader(x){return typeIsObject(x)?!!Object.prototype.hasOwnProperty.call(x,"_readRequests"):!1}function ReadableStreamReaderGenericInitialize(reader,stream){reader._ownerReadableStream=stream,stream._reader=reader,"readable"===stream._state?defaultReaderClosedPromiseInitialize(reader):"closed"===stream._state?defaultReaderClosedPromiseInitializeAsResolved(reader):(assert("errored"===stream._state,"state must be errored"),defaultReaderClosedPromiseInitializeAsRejected(reader,stream._storedError))}function ReadableStreamReaderGenericCancel(reader,reason){var stream=reader._ownerReadableStream;return assert(void 0!==stream),ReadableStreamCancel(stream,reason)}function ReadableStreamReaderGenericRelease(reader){assert(void 0!==reader._ownerReadableStream),assert(reader._ownerReadableStream._reader===reader),"readable"===reader._ownerReadableStream._state?defaultReaderClosedPromiseReject(reader,new TypeError("Reader was released and can no longer be used to monitor the stream's closedness")):defaultReaderClosedPromiseResetToRejected(reader,new TypeError("Reader was released and can no longer be used to monitor the stream's closedness")),reader._ownerReadableStream._reader=void 0,reader._ownerReadableStream=void 0}function ReadableStreamBYOBReaderRead(reader,view){var stream=reader._ownerReadableStream;return assert(void 0!==stream),stream._disturbed=!0,"errored"===stream._state?Promise.reject(stream._storedError):ReadableByteStreamControllerPullInto(stream._readableStreamController,view)}function ReadableStreamDefaultReaderRead(reader){var stream=reader._ownerReadableStream;return assert(void 0!==stream),stream._disturbed=!0,"closed"===stream._state?Promise.resolve(CreateIterResultObject(void 0,!0)):"errored"===stream._state?Promise.reject(stream._storedError):(assert("readable"===stream._state),stream._readableStreamController[InternalPull]())}function IsReadableStreamDefaultController(x){return typeIsObject(x)?!!Object.prototype.hasOwnProperty.call(x,"_underlyingSource"):!1}function ReadableStreamDefaultControllerCallPullIfNeeded(controller){var shouldPull=ReadableStreamDefaultControllerShouldCallPull(controller);if(shouldPull!==!1){if(controller._pulling===!0)return void(controller._pullAgain=!0);controller._pulling=!0;var pullPromise=PromiseInvokeOrNoop(controller._underlyingSource,"pull",[controller]);pullPromise.then(function(){return controller._pulling=!1,controller._pullAgain===!0?(controller._pullAgain=!1,ReadableStreamDefaultControllerCallPullIfNeeded(controller)):void 0},function(e){ReadableStreamDefaultControllerErrorIfNeeded(controller,e)})["catch"](rethrowAssertionErrorRejection)}}function ReadableStreamDefaultControllerShouldCallPull(controller){var stream=controller._controlledReadableStream;if("closed"===stream._state||"errored"===stream._state)return!1;if(controller._closeRequested===!0)return!1;if(controller._started===!1)return!1;if(IsReadableStreamLocked(stream)===!0&&ReadableStreamGetNumReadRequests(stream)>0)return!0;var desiredSize=ReadableStreamDefaultControllerGetDesiredSize(controller);return desiredSize>0}function ReadableStreamDefaultControllerClose(controller){var stream=controller._controlledReadableStream;assert(controller._closeRequested===!1),assert("readable"===stream._state),controller._closeRequested=!0,0===controller._queue.length&&ReadableStreamClose(stream)}function ReadableStreamDefaultControllerEnqueue(controller,chunk){var stream=controller._controlledReadableStream;if(assert(controller._closeRequested===!1),assert("readable"===stream._state),IsReadableStreamLocked(stream)===!0&&ReadableStreamGetNumReadRequests(stream)>0)ReadableStreamFulfillReadRequest(stream,chunk,!1);else{var chunkSize=1;if(void 0!==controller._strategySize)try{chunkSize=controller._strategySize(chunk)}catch(chunkSizeE){throw ReadableStreamDefaultControllerErrorIfNeeded(controller,chunkSizeE),chunkSizeE}try{EnqueueValueWithSize(controller._queue,chunk,chunkSize)}catch(enqueueE){throw ReadableStreamDefaultControllerErrorIfNeeded(controller,enqueueE),enqueueE}}ReadableStreamDefaultControllerCallPullIfNeeded(controller)}function ReadableStreamDefaultControllerError(controller,e){var stream=controller._controlledReadableStream;assert("readable"===stream._state),controller._queue=[],ReadableStreamError(stream,e)}function ReadableStreamDefaultControllerErrorIfNeeded(controller,e){"readable"===controller._controlledReadableStream._state&&ReadableStreamDefaultControllerError(controller,e)}function ReadableStreamDefaultControllerGetDesiredSize(controller){var queueSize=GetTotalQueueSize(controller._queue);return controller._strategyHWM-queueSize}function IsReadableByteStreamController(x){return typeIsObject(x)?!!Object.prototype.hasOwnProperty.call(x,"_underlyingByteSource"):!1}function IsReadableStreamBYOBRequest(x){return typeIsObject(x)?!!Object.prototype.hasOwnProperty.call(x,"_associatedReadableByteStreamController"):!1}function ReadableByteStreamControllerCallPullIfNeeded(controller){var shouldPull=ReadableByteStreamControllerShouldCallPull(controller);if(shouldPull!==!1){if(controller._pulling===!0)return void(controller._pullAgain=!0);controller._pullAgain=!1,controller._pulling=!0;var pullPromise=PromiseInvokeOrNoop(controller._underlyingByteSource,"pull",[controller]);pullPromise.then(function(){controller._pulling=!1,controller._pullAgain===!0&&(controller._pullAgain=!1,ReadableByteStreamControllerCallPullIfNeeded(controller))},function(e){"readable"===controller._controlledReadableStream._state&&ReadableByteStreamControllerError(controller,e)})["catch"](rethrowAssertionErrorRejection)}}function ReadableByteStreamControllerClearPendingPullIntos(controller){ReadableByteStreamControllerInvalidateBYOBRequest(controller),controller._pendingPullIntos=[]}function ReadableByteStreamControllerCommitPullIntoDescriptor(stream,pullIntoDescriptor){assert("errored"!==stream._state,"state must not be errored");var done=!1;"closed"===stream._state&&(assert(0===pullIntoDescriptor.bytesFilled),done=!0);var filledView=ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor);"default"===pullIntoDescriptor.readerType?ReadableStreamFulfillReadRequest(stream,filledView,done):(assert("byob"===pullIntoDescriptor.readerType),ReadableStreamFulfillReadIntoRequest(stream,filledView,done))}function ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor){var bytesFilled=pullIntoDescriptor.bytesFilled,elementSize=pullIntoDescriptor.elementSize;return assert(bytesFilled<=pullIntoDescriptor.byteLength),assert(bytesFilled%elementSize===0),new pullIntoDescriptor.ctor(pullIntoDescriptor.buffer,pullIntoDescriptor.byteOffset,bytesFilled/elementSize)}function ReadableByteStreamControllerEnqueueChunkToQueue(controller,buffer,byteOffset,byteLength){controller._queue.push({buffer:buffer,byteOffset:byteOffset,byteLength:byteLength}),controller._totalQueuedBytes+=byteLength}function ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller,pullIntoDescriptor){var elementSize=pullIntoDescriptor.elementSize,currentAlignedBytes=pullIntoDescriptor.bytesFilled-pullIntoDescriptor.bytesFilled%elementSize,maxBytesToCopy=Math.min(controller._totalQueuedBytes,pullIntoDescriptor.byteLength-pullIntoDescriptor.bytesFilled),maxBytesFilled=pullIntoDescriptor.bytesFilled+maxBytesToCopy,maxAlignedBytes=maxBytesFilled-maxBytesFilled%elementSize,totalBytesToCopyRemaining=maxBytesToCopy,ready=!1;maxAlignedBytes>currentAlignedBytes&&(totalBytesToCopyRemaining=maxAlignedBytes-pullIntoDescriptor.bytesFilled,ready=!0);for(var queue=controller._queue;totalBytesToCopyRemaining>0;){var headOfQueue=queue[0],bytesToCopy=Math.min(totalBytesToCopyRemaining,headOfQueue.byteLength),destStart=pullIntoDescriptor.byteOffset+pullIntoDescriptor.bytesFilled;ArrayBufferCopy(pullIntoDescriptor.buffer,destStart,headOfQueue.buffer,headOfQueue.byteOffset,bytesToCopy),headOfQueue.byteLength===bytesToCopy?queue.shift():(headOfQueue.byteOffset+=bytesToCopy,headOfQueue.byteLength-=bytesToCopy),controller._totalQueuedBytes-=bytesToCopy,ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller,bytesToCopy,pullIntoDescriptor),totalBytesToCopyRemaining-=bytesToCopy}return ready===!1&&(assert(0===controller._totalQueuedBytes,"queue must be empty"),assert(pullIntoDescriptor.bytesFilled>0),assert(pullIntoDescriptor.bytesFilled<pullIntoDescriptor.elementSize)),ready}function ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller,size,pullIntoDescriptor){assert(0===controller._pendingPullIntos.length||controller._pendingPullIntos[0]===pullIntoDescriptor),ReadableByteStreamControllerInvalidateBYOBRequest(controller),pullIntoDescriptor.bytesFilled+=size}function ReadableByteStreamControllerHandleQueueDrain(controller){assert("readable"===controller._controlledReadableStream._state),0===controller._totalQueuedBytes&&controller._closeRequested===!0?ReadableStreamClose(controller._controlledReadableStream):ReadableByteStreamControllerCallPullIfNeeded(controller)}function ReadableByteStreamControllerInvalidateBYOBRequest(controller){void 0!==controller._byobRequest&&(controller._byobRequest._associatedReadableByteStreamController=void 0,controller._byobRequest._view=void 0,controller._byobRequest=void 0)}function ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller){for(assert(controller._closeRequested===!1);controller._pendingPullIntos.length>0;){if(0===controller._totalQueuedBytes)return;var pullIntoDescriptor=controller._pendingPullIntos[0];ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller,pullIntoDescriptor)===!0&&(ReadableByteStreamControllerShiftPendingPullInto(controller),ReadableByteStreamControllerCommitPullIntoDescriptor(controller._controlledReadableStream,pullIntoDescriptor))}}function ReadableByteStreamControllerPullInto(controller,view){var stream=controller._controlledReadableStream,elementSize=1;view.constructor!==DataView&&(elementSize=view.constructor.BYTES_PER_ELEMENT);var ctor=view.constructor,pullIntoDescriptor={buffer:view.buffer,byteOffset:view.byteOffset,byteLength:view.byteLength,bytesFilled:0,elementSize:elementSize,ctor:ctor,readerType:"byob"};if(controller._pendingPullIntos.length>0)return pullIntoDescriptor.buffer=SameRealmTransfer(pullIntoDescriptor.buffer),controller._pendingPullIntos.push(pullIntoDescriptor),ReadableStreamAddReadIntoRequest(stream);if("closed"===stream._state){var emptyView=new view.constructor(view.buffer,view.byteOffset,0);return Promise.resolve(CreateIterResultObject(emptyView,!0))}if(controller._totalQueuedBytes>0){if(ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller,pullIntoDescriptor)===!0){var filledView=ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor);return ReadableByteStreamControllerHandleQueueDrain(controller),Promise.resolve(CreateIterResultObject(filledView,!1))}if(controller._closeRequested===!0){var e=new TypeError("Insufficient bytes to fill elements in the given buffer");return ReadableByteStreamControllerError(controller,e),Promise.reject(e)}}pullIntoDescriptor.buffer=SameRealmTransfer(pullIntoDescriptor.buffer),controller._pendingPullIntos.push(pullIntoDescriptor);var promise=ReadableStreamAddReadIntoRequest(stream);return ReadableByteStreamControllerCallPullIfNeeded(controller),promise}function ReadableByteStreamControllerRespondInClosedState(controller,firstDescriptor){firstDescriptor.buffer=SameRealmTransfer(firstDescriptor.buffer),assert(0===firstDescriptor.bytesFilled,"bytesFilled must be 0");for(var stream=controller._controlledReadableStream;ReadableStreamGetNumReadIntoRequests(stream)>0;){var pullIntoDescriptor=ReadableByteStreamControllerShiftPendingPullInto(controller);ReadableByteStreamControllerCommitPullIntoDescriptor(stream,pullIntoDescriptor)}}function ReadableByteStreamControllerRespondInReadableState(controller,bytesWritten,pullIntoDescriptor){if(pullIntoDescriptor.bytesFilled+bytesWritten>pullIntoDescriptor.byteLength)throw new RangeError("bytesWritten out of range");if(ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller,bytesWritten,pullIntoDescriptor),!(pullIntoDescriptor.bytesFilled<pullIntoDescriptor.elementSize)){ReadableByteStreamControllerShiftPendingPullInto(controller);var remainderSize=pullIntoDescriptor.bytesFilled%pullIntoDescriptor.elementSize;if(remainderSize>0){var end=pullIntoDescriptor.byteOffset+pullIntoDescriptor.bytesFilled,remainder=pullIntoDescriptor.buffer.slice(end-remainderSize,end);ReadableByteStreamControllerEnqueueChunkToQueue(controller,remainder,0,remainder.byteLength)}pullIntoDescriptor.buffer=SameRealmTransfer(pullIntoDescriptor.buffer),pullIntoDescriptor.bytesFilled-=remainderSize,ReadableByteStreamControllerCommitPullIntoDescriptor(controller._controlledReadableStream,pullIntoDescriptor),ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller)}}function ReadableByteStreamControllerRespondInternal(controller,bytesWritten){var firstDescriptor=controller._pendingPullIntos[0],stream=controller._controlledReadableStream;if("closed"===stream._state){if(0!==bytesWritten)throw new TypeError("bytesWritten must be 0 when calling respond() on a closed stream");ReadableByteStreamControllerRespondInClosedState(controller,firstDescriptor)}else assert("readable"===stream._state),ReadableByteStreamControllerRespondInReadableState(controller,bytesWritten,firstDescriptor)}function ReadableByteStreamControllerShiftPendingPullInto(controller){var descriptor=controller._pendingPullIntos.shift();return ReadableByteStreamControllerInvalidateBYOBRequest(controller),descriptor}function ReadableByteStreamControllerShouldCallPull(controller){var stream=controller._controlledReadableStream;return"readable"!==stream._state?!1:controller._closeRequested===!0?!1:controller._started===!1?!1:ReadableStreamHasDefaultReader(stream)&&ReadableStreamGetNumReadRequests(stream)>0?!0:ReadableStreamHasBYOBReader(stream)&&ReadableStreamGetNumReadIntoRequests(stream)>0?!0:ReadableByteStreamControllerGetDesiredSize(controller)>0}function ReadableByteStreamControllerClose(controller){var stream=controller._controlledReadableStream;if(assert(controller._closeRequested===!1),assert("readable"===stream._state),controller._totalQueuedBytes>0)return void(controller._closeRequested=!0);if(controller._pendingPullIntos.length>0){var firstPendingPullInto=controller._pendingPullIntos[0];if(firstPendingPullInto.bytesFilled>0){var e=new TypeError("Insufficient bytes to fill elements in the given buffer");throw ReadableByteStreamControllerError(controller,e),e}}ReadableStreamClose(stream)}function ReadableByteStreamControllerEnqueue(controller,chunk){var stream=controller._controlledReadableStream;assert(controller._closeRequested===!1),assert("readable"===stream._state);var buffer=chunk.buffer,byteOffset=chunk.byteOffset,byteLength=chunk.byteLength,transferredBuffer=SameRealmTransfer(buffer);if(ReadableStreamHasDefaultReader(stream)===!0)if(0===ReadableStreamGetNumReadRequests(stream))ReadableByteStreamControllerEnqueueChunkToQueue(controller,transferredBuffer,byteOffset,byteLength);else{assert(0===controller._queue.length);var transferredView=new Uint8Array(transferredBuffer,byteOffset,byteLength);ReadableStreamFulfillReadRequest(stream,transferredView,!1)}else ReadableStreamHasBYOBReader(stream)===!0?(ReadableByteStreamControllerEnqueueChunkToQueue(controller,transferredBuffer,byteOffset,byteLength),ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller)):(assert(IsReadableStreamLocked(stream)===!1,"stream must not be locked"),ReadableByteStreamControllerEnqueueChunkToQueue(controller,transferredBuffer,byteOffset,byteLength))}function ReadableByteStreamControllerError(controller,e){var stream=controller._controlledReadableStream;assert("readable"===stream._state),ReadableByteStreamControllerClearPendingPullIntos(controller),controller._queue=[],ReadableStreamError(stream,e)}function ReadableByteStreamControllerGetDesiredSize(controller){return controller._strategyHWM-controller._totalQueuedBytes}function ReadableByteStreamControllerRespond(controller,bytesWritten){if(bytesWritten=Number(bytesWritten),IsFiniteNonNegativeNumber(bytesWritten)===!1)throw new RangeError("bytesWritten must be a finite");assert(controller._pendingPullIntos.length>0),ReadableByteStreamControllerRespondInternal(controller,bytesWritten)}function ReadableByteStreamControllerRespondWithNewView(controller,view){assert(controller._pendingPullIntos.length>0);var firstDescriptor=controller._pendingPullIntos[0];if(firstDescriptor.byteOffset+firstDescriptor.bytesFilled!==view.byteOffset)throw new RangeError("The region specified by view does not match byobRequest");if(firstDescriptor.byteLength!==view.byteLength)throw new RangeError("The buffer of view has different capacity than byobRequest");firstDescriptor.buffer=view.buffer,ReadableByteStreamControllerRespondInternal(controller,view.byteLength)}function streamBrandCheckException(name){return new TypeError("ReadableStream.prototype."+name+" can only be used on a ReadableStream")}function readerLockException(name){return new TypeError("Cannot "+name+" a stream using a released reader")}function defaultReaderBrandCheckException(name){return new TypeError("ReadableStreamDefaultReader.prototype."+name+" can only be used on a ReadableStreamDefaultReader")}function defaultReaderClosedPromiseInitialize(reader){reader._closedPromise=new Promise(function(resolve,reject){reader._closedPromise_resolve=resolve,reader._closedPromise_reject=reject})}function defaultReaderClosedPromiseInitializeAsRejected(reader,reason){reader._closedPromise=Promise.reject(reason),reader._closedPromise_resolve=void 0,reader._closedPromise_reject=void 0}function defaultReaderClosedPromiseInitializeAsResolved(reader){reader._closedPromise=Promise.resolve(void 0),reader._closedPromise_resolve=void 0,reader._closedPromise_reject=void 0}function defaultReaderClosedPromiseReject(reader,reason){assert(void 0!==reader._closedPromise_resolve),assert(void 0!==reader._closedPromise_reject),reader._closedPromise_reject(reason),reader._closedPromise_resolve=void 0,reader._closedPromise_reject=void 0}function defaultReaderClosedPromiseResetToRejected(reader,reason){assert(void 0===reader._closedPromise_resolve),assert(void 0===reader._closedPromise_reject),reader._closedPromise=Promise.reject(reason)}function defaultReaderClosedPromiseResolve(reader){assert(void 0!==reader._closedPromise_resolve),assert(void 0!==reader._closedPromise_reject),reader._closedPromise_resolve(void 0),reader._closedPromise_resolve=void 0,reader._closedPromise_reject=void 0}function byobReaderBrandCheckException(name){return new TypeError("ReadableStreamBYOBReader.prototype."+name+" can only be used on a ReadableStreamBYOBReader")}function defaultControllerBrandCheckException(name){return new TypeError("ReadableStreamDefaultController.prototype."+name+" can only be used on a ReadableStreamDefaultController")}function byobRequestBrandCheckException(name){return new TypeError("ReadableStreamBYOBRequest.prototype."+name+" can only be used on a ReadableStreamBYOBRequest")}function byteStreamControllerBrandCheckException(name){return new TypeError("ReadableByteStreamController.prototype."+name+" can only be used on a ReadableByteStreamController")}var _slicedToArray=function(){function sliceIterator(arr,i){var _arr=[],_n=!0,_d=!1,_e=void 0;try{for(var _s,_i=arr[Symbol.iterator]();!(_n=(_s=_i.next()).done)&&(_arr.push(_s.value),!i||_arr.length!==i);_n=!0);}catch(err){_d=!0,_e=err}finally{try{!_n&&_i["return"]&&_i["return"]()}finally{if(_d)throw _e}}return _arr}return function(arr,i){if(Array.isArray(arr))return arr;if(Symbol.iterator in Object(arr))return sliceIterator(arr,i);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),_createClass=function(){function defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||!1,descriptor.configurable=!0,"value"in descriptor&&(descriptor.writable=!0),Object.defineProperty(target,descriptor.key,descriptor)}}return function(Constructor,protoProps,staticProps){return protoProps&&defineProperties(Constructor.prototype,protoProps),staticProps&&defineProperties(Constructor,staticProps),Constructor}}(),assert=require("assert"),_require=require("./helpers.js"),ArrayBufferCopy=_require.ArrayBufferCopy,CreateIterResultObject=_require.CreateIterResultObject,IsFiniteNonNegativeNumber=_require.IsFiniteNonNegativeNumber,InvokeOrNoop=_require.InvokeOrNoop,PromiseInvokeOrNoop=_require.PromiseInvokeOrNoop,SameRealmTransfer=_require.SameRealmTransfer,ValidateAndNormalizeQueuingStrategy=_require.ValidateAndNormalizeQueuingStrategy,ValidateAndNormalizeHighWaterMark=_require.ValidateAndNormalizeHighWaterMark,_require2=require("./helpers.js"),createArrayFromList=_require2.createArrayFromList,createDataProperty=_require2.createDataProperty,typeIsObject=_require2.typeIsObject,_require3=require("./utils.js"),rethrowAssertionErrorRejection=_require3.rethrowAssertionErrorRejection,_require4=require("./queue-with-sizes.js"),DequeueValue=_require4.DequeueValue,EnqueueValueWithSize=_require4.EnqueueValueWithSize,GetTotalQueueSize=_require4.GetTotalQueueSize,InternalCancel=Symbol("[[Cancel]]"),InternalPull=Symbol("[[Pull]]"),ReadableStream=function(){function ReadableStream(){var underlyingSource=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],_ref=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],size=_ref.size,highWaterMark=_ref.highWaterMark;_classCallCheck(this,ReadableStream),this._state="readable",this._reader=void 0,this._storedError=void 0,this._disturbed=!1,this._readableStreamController=void 0;var type=underlyingSource.type,typeString=String(type);if("bytes"===typeString)void 0===highWaterMark&&(highWaterMark=0),this._readableStreamController=new ReadableByteStreamController(this,underlyingSource,highWaterMark);else{if(void 0!==type)throw new RangeError("Invalid type is specified");void 0===highWaterMark&&(highWaterMark=1),this._readableStreamController=new ReadableStreamDefaultController(this,underlyingSource,size,highWaterMark)}}return _createClass(ReadableStream,[{key:"cancel",value:function(reason){return IsReadableStream(this)===!1?Promise.reject(streamBrandCheckException("cancel")):IsReadableStreamLocked(this)===!0?Promise.reject(new TypeError("Cannot cancel a stream that already has a reader")):ReadableStreamCancel(this,reason)}},{key:"getReader",value:function(){var _ref2=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],mode=_ref2.mode;if(IsReadableStream(this)===!1)throw streamBrandCheckException("getReader");if("byob"===mode){if(IsReadableByteStreamController(this._readableStreamController)===!1)throw new TypeError("Cannot get a ReadableStreamBYOBReader for a stream not constructed with a byte source");return AcquireReadableStreamBYOBReader(this)}if(void 0===mode)return AcquireReadableStreamDefaultReader(this);throw new RangeError("Invalid mode is specified")}},{key:"pipeThrough",value:function(_ref3,options){var writable=_ref3.writable,readable=_ref3.readable;return this.pipeTo(writable,options),readable}},{key:"pipeTo",value:function(dest){function releaseReader(){_reader.releaseLock(),_reader=void 0}function releaseWriter(){_writer.releaseLock(),_writer=void 0}function pipeDone(){assert(void 0===_reader),assert(void 0===_writer),_state="done",_lastRead=void 0,_lastWrite=void 0,_allWrites=void 0}function finishWithFulfillment(){_resolvePipeToPromise(void 0),_resolvePipeToPromise=void 0,_rejectPipeToPromise=void 0,pipeDone()}function finishWithRejection(reason){_rejectPipeToPromise(reason),_resolvePipeToPromise=void 0,_rejectPipeToPromise=void 0,pipeDone()}function abortWriterCancelReader(reason,skipAbort,skipCancel){var promises=[];return skipAbort===!1?(_writer.abort(reason),releaseWriter()):void 0===_lastWrite?releaseWriter():promises.push(_lastWrite.then(function(){releaseWriter()},function(){releaseWriter()})),skipCancel===!1?(_reader.cancel(reason),releaseReader()):void 0===_lastRead?releaseReader():promises.push(_lastRead.then(function(){releaseReader()},function(){releaseReader()})),promises.length>0?(Promise.all(promises).then(function(){finishWithRejection(reason)}),void(_state="waitingForLastReadAndOrLastWrite")):void finishWithRejection(reason)}function handleWriteRejection(reason){"piping"===_state&&abortWriterCancelReader(reason,preventAbort,preventCancel)}function handleReadValue(value){_lastWrite=_writer.write(value),_lastWrite["catch"](handleWriteRejection),_allWrites=Promise.all([_allWrites,_lastWrite]),doPipe()}function handleReadDone(){return releaseReader(),preventClose===!1?(_writer.close().then(function(){return _allWrites},function(reason){releaseWriter(),finishWithRejection(reason)}).then(function(){releaseWriter(),finishWithFulfillment()}),void(_state="closingDest")):void 0===_lastWrite?(releaseWriter(),void finishWithFulfillment()):(_lastWrite.then(function(){releaseWriter(),finishWithFulfillment()},function(reason){releaseWriter(),finishWithRejection(reason)}),void(_state="waitingLastWriteOnReadableClosed"))}function doPipe(){_lastRead=_reader.read(),Promise.all([_lastRead,_writer.ready]).then(function(_ref5){var _ref6=_slicedToArray(_ref5,1),_ref6$=_ref6[0],value=_ref6$.value,done=_ref6$.done;"piping"===_state&&(Boolean(done)===!1?handleReadValue(value):handleReadDone())},function(){})["catch"](rethrowAssertionErrorRejection)}function handleReaderClosedRejection(reason){"piping"===_state&&(_lastRead=void 0,abortWriterCancelReader(reason,preventAbort,!0))}function handleUnexpectedWriterCloseAndError(reason){"piping"===_state&&(_lastWrite=void 0,abortWriterCancelReader(reason,!0,preventCancel))}function handleWriterClosedFulfillment(){handleUnexpectedWriterCloseAndError(new TypeError("dest closed unexpectedly"))}function handleWriterClosedRejection(reason){handleUnexpectedWriterCloseAndError(reason)}var _ref4=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],preventClose=_ref4.preventClose,preventAbort=_ref4.preventAbort,preventCancel=_ref4.preventCancel;preventClose=Boolean(preventClose),preventAbort=Boolean(preventAbort),preventCancel=Boolean(preventCancel);var source=this,_resolvePipeToPromise=void 0,_rejectPipeToPromise=void 0,_reader=void 0,_writer=void 0,_state="piping",_lastRead=void 0,_lastWrite=void 0,_allWrites=void 0;return new Promise(function(resolve,reject){_resolvePipeToPromise=resolve,_rejectPipeToPromise=reject,_reader=source.getReader(),_writer=dest.getWriter(),_reader.closed["catch"](handleReaderClosedRejection),_writer.closed.then(handleWriterClosedFulfillment,handleWriterClosedRejection),doPipe()})}},{key:"tee",value:function(){if(IsReadableStream(this)===!1)throw streamBrandCheckException("tee");var branches=ReadableStreamTee(this,!1);return createArrayFromList(branches)}},{key:"locked",get:function(){if(IsReadableStream(this)===!1)throw streamBrandCheckException("locked");return IsReadableStreamLocked(this)}}]),ReadableStream}();exports.ReadableStream=ReadableStream,exports.IsReadableStreamDisturbed=IsReadableStreamDisturbed;var ReadableStreamDefaultReader=function(){function ReadableStreamDefaultReader(stream){if(_classCallCheck(this,ReadableStreamDefaultReader),IsReadableStream(stream)===!1)throw new TypeError("ReadableStreamDefaultReader can only be constructed with a ReadableStream instance");if(IsReadableStreamLocked(stream)===!0)throw new TypeError("This stream has already been locked for exclusive reading by another reader");ReadableStreamReaderGenericInitialize(this,stream),this._readRequests=[]}return _createClass(ReadableStreamDefaultReader,[{key:"cancel",value:function(reason){return IsReadableStreamDefaultReader(this)===!1?Promise.reject(defaultReaderBrandCheckException("cancel")):void 0===this._ownerReadableStream?Promise.reject(readerLockException("cancel")):ReadableStreamReaderGenericCancel(this,reason)}},{key:"read",value:function(){return IsReadableStreamDefaultReader(this)===!1?Promise.reject(defaultReaderBrandCheckException("read")):void 0===this._ownerReadableStream?Promise.reject(readerLockException("read from")):ReadableStreamDefaultReaderRead(this)}},{key:"releaseLock",value:function(){if(IsReadableStreamDefaultReader(this)===!1)throw defaultReaderBrandCheckException("releaseLock");if(void 0!==this._ownerReadableStream){if(this._readRequests.length>0)throw new TypeError("Tried to release a reader lock when that reader has pending read() calls un-settled");ReadableStreamReaderGenericRelease(this)}}},{key:"closed",get:function(){return IsReadableStreamDefaultReader(this)===!1?Promise.reject(defaultReaderBrandCheckException("closed")):this._closedPromise}}]),ReadableStreamDefaultReader}(),ReadableStreamBYOBReader=function(){function ReadableStreamBYOBReader(stream){if(_classCallCheck(this,ReadableStreamBYOBReader),!IsReadableStream(stream))throw new TypeError("ReadableStreamBYOBReader can only be constructed with a ReadableStream instance given a byte source");if(IsReadableStreamLocked(stream))throw new TypeError("This stream has already been locked for exclusive reading by another reader");ReadableStreamReaderGenericInitialize(this,stream),this._readIntoRequests=[]}return _createClass(ReadableStreamBYOBReader,[{key:"cancel",value:function(reason){return IsReadableStreamBYOBReader(this)?void 0===this._ownerReadableStream?Promise.reject(readerLockException("cancel")):ReadableStreamReaderGenericCancel(this,reason):Promise.reject(byobReaderBrandCheckException("cancel"))}},{key:"read",value:function(view){return IsReadableStreamBYOBReader(this)?void 0===this._ownerReadableStream?Promise.reject(readerLockException("read from")):ArrayBuffer.isView(view)?0===view.byteLength?Promise.reject(new TypeError("view must have non-zero byteLength")):ReadableStreamBYOBReaderRead(this,view):Promise.reject(new TypeError("view must be an array buffer view")):Promise.reject(byobReaderBrandCheckException("read"))}},{key:"releaseLock",value:function(){if(!IsReadableStreamBYOBReader(this))throw byobReaderBrandCheckException("releaseLock");if(void 0!==this._ownerReadableStream){if(this._readIntoRequests.length>0)throw new TypeError("Tried to release a reader lock when that reader has pending read() calls un-settled");ReadableStreamReaderGenericRelease(this)}}},{key:"closed",get:function(){return IsReadableStreamBYOBReader(this)?this._closedPromise:Promise.reject(byobReaderBrandCheckException("closed"))}}]),ReadableStreamBYOBReader}(),ReadableStreamDefaultController=function(){function ReadableStreamDefaultController(stream,underlyingSource,size,highWaterMark){if(_classCallCheck(this,ReadableStreamDefaultController),IsReadableStream(stream)===!1)throw new TypeError("ReadableStreamDefaultController can only be constructed with a ReadableStream instance");if(void 0!==stream._readableStreamController)throw new TypeError("ReadableStreamDefaultController instances can only be created by the ReadableStream constructor");this._controlledReadableStream=stream,this._underlyingSource=underlyingSource,this._queue=[],this._started=!1,this._closeRequested=!1,this._pullAgain=!1,this._pulling=!1;var normalizedStrategy=ValidateAndNormalizeQueuingStrategy(size,highWaterMark);this._strategySize=normalizedStrategy.size,this._strategyHWM=normalizedStrategy.highWaterMark;var controller=this,startResult=InvokeOrNoop(underlyingSource,"start",[this]);Promise.resolve(startResult).then(function(){controller._started=!0,
+ReadableStreamDefaultControllerCallPullIfNeeded(controller)},function(r){ReadableStreamDefaultControllerErrorIfNeeded(controller,r)})["catch"](rethrowAssertionErrorRejection)}return _createClass(ReadableStreamDefaultController,[{key:"close",value:function(){if(IsReadableStreamDefaultController(this)===!1)throw defaultControllerBrandCheckException("close");if(this._closeRequested===!0)throw new TypeError("The stream has already been closed; do not close it again!");var state=this._controlledReadableStream._state;if("readable"!==state)throw new TypeError("The stream (in "+state+" state) is not in the readable state and cannot be closed");ReadableStreamDefaultControllerClose(this)}},{key:"enqueue",value:function(chunk){if(IsReadableStreamDefaultController(this)===!1)throw defaultControllerBrandCheckException("enqueue");if(this._closeRequested===!0)throw new TypeError("stream is closed or draining");var state=this._controlledReadableStream._state;if("readable"!==state)throw new TypeError("The stream (in "+state+" state) is not in the readable state and cannot be enqueued to");return ReadableStreamDefaultControllerEnqueue(this,chunk)}},{key:"error",value:function(e){if(IsReadableStreamDefaultController(this)===!1)throw defaultControllerBrandCheckException("error");var stream=this._controlledReadableStream;if("readable"!==stream._state)throw new TypeError("The stream is "+stream._state+" and so cannot be errored");ReadableStreamDefaultControllerError(this,e)}},{key:InternalCancel,value:function(reason){return this._queue=[],PromiseInvokeOrNoop(this._underlyingSource,"cancel",[reason])}},{key:InternalPull,value:function(){var stream=this._controlledReadableStream;if(this._queue.length>0){var chunk=DequeueValue(this._queue);return this._closeRequested===!0&&0===this._queue.length?ReadableStreamClose(stream):ReadableStreamDefaultControllerCallPullIfNeeded(this),Promise.resolve(CreateIterResultObject(chunk,!1))}var pendingPromise=ReadableStreamAddReadRequest(stream);return ReadableStreamDefaultControllerCallPullIfNeeded(this),pendingPromise}},{key:"desiredSize",get:function(){if(IsReadableStreamDefaultController(this)===!1)throw defaultControllerBrandCheckException("desiredSize");return ReadableStreamDefaultControllerGetDesiredSize(this)}}]),ReadableStreamDefaultController}(),ReadableStreamBYOBRequest=function(){function ReadableStreamBYOBRequest(controller,view){_classCallCheck(this,ReadableStreamBYOBRequest),this._associatedReadableByteStreamController=controller,this._view=view}return _createClass(ReadableStreamBYOBRequest,[{key:"respond",value:function(bytesWritten){if(IsReadableStreamBYOBRequest(this)===!1)throw byobRequestBrandCheckException("respond");if(void 0===this._associatedReadableByteStreamController)throw new TypeError("This BYOB request has been invalidated");ReadableByteStreamControllerRespond(this._associatedReadableByteStreamController,bytesWritten)}},{key:"respondWithNewView",value:function(view){if(IsReadableStreamBYOBRequest(this)===!1)throw byobRequestBrandCheckException("respond");if(void 0===this._associatedReadableByteStreamController)throw new TypeError("This BYOB request has been invalidated");if(!ArrayBuffer.isView(view))throw new TypeError("You can only respond with array buffer views");ReadableByteStreamControllerRespondWithNewView(this._associatedReadableByteStreamController,view)}},{key:"view",get:function(){return this._view}}]),ReadableStreamBYOBRequest}(),ReadableByteStreamController=function(){function ReadableByteStreamController(stream,underlyingByteSource,highWaterMark){if(_classCallCheck(this,ReadableByteStreamController),IsReadableStream(stream)===!1)throw new TypeError("ReadableByteStreamController can only be constructed with a ReadableStream instance given a byte source");if(void 0!==stream._readableStreamController)throw new TypeError("ReadableByteStreamController instances can only be created by the ReadableStream constructor given a byte source");this._controlledReadableStream=stream,this._underlyingByteSource=underlyingByteSource,this._pullAgain=!1,this._pulling=!1,ReadableByteStreamControllerClearPendingPullIntos(this),this._queue=[],this._totalQueuedBytes=0,this._closeRequested=!1,this._started=!1,this._strategyHWM=ValidateAndNormalizeHighWaterMark(highWaterMark);var autoAllocateChunkSize=underlyingByteSource.autoAllocateChunkSize;if(void 0!==autoAllocateChunkSize&&(Number.isInteger(autoAllocateChunkSize)===!1||0>autoAllocateChunkSize))throw new RangeError("autoAllocateChunkSize must be a non negative integer");this._autoAllocateChunkSize=autoAllocateChunkSize,this._pendingPullIntos=[];var controller=this,startResult=InvokeOrNoop(underlyingByteSource,"start",[this]);Promise.resolve(startResult).then(function(){controller._started=!0,assert(controller._pulling===!1),assert(controller._pullAgain===!1),ReadableByteStreamControllerCallPullIfNeeded(controller)},function(r){"readable"===stream._state&&ReadableByteStreamControllerError(controller,r)})["catch"](rethrowAssertionErrorRejection)}return _createClass(ReadableByteStreamController,[{key:"close",value:function(){if(IsReadableByteStreamController(this)===!1)throw byteStreamControllerBrandCheckException("close");if(this._closeRequested===!0)throw new TypeError("The stream has already been closed; do not close it again!");var state=this._controlledReadableStream._state;if("readable"!==state)throw new TypeError("The stream (in "+state+" state) is not in the readable state and cannot be closed");ReadableByteStreamControllerClose(this)}},{key:"enqueue",value:function(chunk){if(IsReadableByteStreamController(this)===!1)throw byteStreamControllerBrandCheckException("enqueue");if(this._closeRequested===!0)throw new TypeError("stream is closed or draining");var state=this._controlledReadableStream._state;if("readable"!==state)throw new TypeError("The stream (in "+state+" state) is not in the readable state and cannot be enqueued to");if(!ArrayBuffer.isView(chunk))throw new TypeError("You can only enqueue array buffer views when using a ReadableByteStreamController");ReadableByteStreamControllerEnqueue(this,chunk)}},{key:"error",value:function(e){if(IsReadableByteStreamController(this)===!1)throw byteStreamControllerBrandCheckException("error");var stream=this._controlledReadableStream;if("readable"!==stream._state)throw new TypeError("The stream is "+stream._state+" and so cannot be errored");ReadableByteStreamControllerError(this,e)}},{key:InternalCancel,value:function(reason){if(this._pendingPullIntos.length>0){var firstDescriptor=this._pendingPullIntos[0];firstDescriptor.bytesFilled=0}return this._queue=[],this._totalQueuedBytes=0,PromiseInvokeOrNoop(this._underlyingByteSource,"cancel",[reason])}},{key:InternalPull,value:function(){var stream=this._controlledReadableStream;if(0===ReadableStreamGetNumReadRequests(stream)){if(this._totalQueuedBytes>0){var entry=this._queue.shift();this._totalQueuedBytes-=entry.byteLength,ReadableByteStreamControllerHandleQueueDrain(this);var view=void 0;try{view=new Uint8Array(entry.buffer,entry.byteOffset,entry.byteLength)}catch(viewE){return Promise.reject(viewE)}return Promise.resolve(CreateIterResultObject(view,!1))}var autoAllocateChunkSize=this._autoAllocateChunkSize;if(void 0!==autoAllocateChunkSize){var buffer=void 0;try{buffer=new ArrayBuffer(autoAllocateChunkSize)}catch(bufferE){return Promise.reject(bufferE)}var pullIntoDescriptor={buffer:buffer,byteOffset:0,byteLength:autoAllocateChunkSize,bytesFilled:0,elementSize:1,ctor:Uint8Array,readerType:"default"};this._pendingPullIntos.push(pullIntoDescriptor)}}else assert(void 0===this._autoAllocateChunkSize);var promise=ReadableStreamAddReadRequest(stream);return ReadableByteStreamControllerCallPullIfNeeded(this),promise}},{key:"byobRequest",get:function(){if(IsReadableByteStreamController(this)===!1)throw byteStreamControllerBrandCheckException("byobRequest");if(void 0===this._byobRequest&&this._pendingPullIntos.length>0){var firstDescriptor=this._pendingPullIntos[0],view=new Uint8Array(firstDescriptor.buffer,firstDescriptor.byteOffset+firstDescriptor.bytesFilled,firstDescriptor.byteLength-firstDescriptor.bytesFilled);this._byobRequest=new ReadableStreamBYOBRequest(this,view)}return this._byobRequest}},{key:"desiredSize",get:function(){if(IsReadableByteStreamController(this)===!1)throw byteStreamControllerBrandCheckException("desiredSize");return ReadableByteStreamControllerGetDesiredSize(this)}}]),ReadableByteStreamController}()},{"./helpers.js":9,"./queue-with-sizes.js":10,"./utils.js":13,assert:2}],12:[function(require,module,exports){"use strict";function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor))throw new TypeError("Cannot call a class as a function")}function TransformStreamCloseReadable(transformStream){if(transformStream._errored===!0)throw new TypeError("TransformStream is already errored");if(transformStream._readableClosed===!0)throw new TypeError("Readable side is already closed");try{transformStream._readableController.close()}catch(e){assert(!1)}transformStream._readableClosed=!0}function TransformStreamEnqueueToReadable(transformStream,chunk){if(transformStream._errroed===!0)throw new TypeError("TransformStream is already errored");if(transformStream._readableClosed===!0)throw new TypeError("Readable side is already closed");var controller=transformStream._readableController;transformStream._readableBackpressure=!0;try{controller.enqueue(chunk)}catch(e){if(transformStream._error===!1){var reason=new TypeError("Failed to enqueue to readable side");TransformStreamErrorInternal(transformStream,reason)}throw transformStream._error}var backpressure=void 0;try{backpressure=controller.desiredSize<=0}catch(e){if(transformStream._error===!1){var reason=new TypeError("Failed to calculate backpressure of readable side");TransformStreamError(transformStream,reason)}throw transformStream._error}backpressure&&(transformStream._readableBackpressure=!1)}function TransformStreamError(transformStream,e){if(transformStream._errored===!0)throw new TypeError("TransformStream is already errored");TransformStreamErrorInternal(transformStream,e)}function TransformStreamChunkDone(transformStream){if(transformStream._errroed===!0)throw new TypeError("TransformStream is already errored");if(transformStream._transforming===!1)throw new TypeError("No active transform is running");assert(void 0!==transformStream._resolveWrite),transformStream._transforming=!1,transformStream._resolveWrite(void 0),transformStream._resolveWrite=void 0,TransformStreamTransformIfNeeded(transformStream)}function TransformStreamErrorInternal(transformStream,e){transformStream._errored=!0,transformStream._writableDone===!1&&transformStream._writableController.error(e),transformStream._readableClosed===!1&&transformStream._readableController.error(e),transformStream._chunk=void 0,void 0!==transformStream._resolveWriter&&transformStream._resolveWriter(void 0)}function TransformStreamTransformIfNeeded(transformStream){if(transformStream._chunkPending!==!1&&(assert(void 0!==transformStream._resolveWrite),transformStream._transforming!==!0&&transformStream._readableBackpressure!==!0)){transformStream._transforming=!0;var chunk=transformStream._chunk;transformStream._chunkPending=!1,transformStream._chunk=void 0;try{void 0!==transformStream._transformer.transform&&transformStream._transformer.transform(chunk,TransformStreamChunkDone.bind(void 0,transformStream),transformStream._enqueueFunction,transformStream._closeFunction,transformStream._errorFunction)}catch(e){transformStream._errored===!1&&TransformStreamErrorInternal(transformStream,e)}}}function TransformStreamStart(transformStream){void 0!==transformStream._transformer.start&&transformStream._transformer.start(transformStream._enqueueFunction,transformStream._closeFunction,transformStream._errorFunction)}var _createClass=function(){function defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||!1,descriptor.configurable=!0,"value"in descriptor&&(descriptor.writable=!0),Object.defineProperty(target,descriptor.key,descriptor)}}return function(Constructor,protoProps,staticProps){return protoProps&&defineProperties(Constructor.prototype,protoProps),staticProps&&defineProperties(Constructor,staticProps),Constructor}}(),assert=require("assert"),_require=require("./readable-stream.js"),ReadableStream=_require.ReadableStream,_require2=require("./writable-stream.js"),WritableStream=_require2.WritableStream,TransformStreamSink=function(){function TransformStreamSink(transformStream){_classCallCheck(this,TransformStreamSink),this._transformStream=transformStream}return _createClass(TransformStreamSink,[{key:"start",value:function(c){var transformStream=this._transformStream;transformStream._writableController=c,void 0!==transformStream._readableController&&TransformStreamStart(transformStream)}},{key:"write",value:function(chunk){var transformStream=this._transformStream;assert(transformStream._errored===!1),assert(transformStream._chunkPending===!1),assert(void 0===transformStream._chunk),assert(void 0===transformStream._resolveWrite),transformStream._chunkPending=!0,transformStream._chunk=chunk;var promise=new Promise(function(resolve){transformStream._resolveWrite=resolve});return TransformStreamTransformIfNeeded(transformStream),promise}},{key:"abort",value:function(){var transformStream=this._transformStream;transformStream._writableDone=!0,TransformStreamErrorInternal(transformStream,new TypeError("Writable side aborted"))}},{key:"close",value:function(){var transformStream=this._transformStream;if(assert(transformStream._chunkPending===!1),assert(void 0===transformStream._chunk),assert(void 0===transformStream._resolveWrite),assert(transformStream._transforming===!1),transformStream._writableDone=!0,void 0===transformStream._transformer.flush)TransformStreamCloseReadable(transformStream);else try{transformStream._transformer.flush(transformStream._enqueueFunction,transformStream._closeFunction,transformStream._errorFunction)}catch(e){if(transformStream._errored===!1)throw TransformStreamErrorInternal(transformStream,e),e}}}]),TransformStreamSink}(),TransformStreamSource=function(){function TransformStreamSource(transformStream){_classCallCheck(this,TransformStreamSource),this._transformStream=transformStream}return _createClass(TransformStreamSource,[{key:"start",value:function(c){var transformStream=this._transformStream;transformStream._readableController=c,void 0!==transformStream._writableController&&TransformStreamStart(transformStream)}},{key:"pull",value:function(){this._transformStream._readableBackpressure=!1,TransformStreamTransformIfNeeded(this._transformStream)}},{key:"cancel",value:function(){var transformStream=this._transformStream;transformStream._readableClosed=!0,TransformStreamErrorInternal(transformStream,new TypeError("Readable side canceled"))}}]),TransformStreamSource}();module.exports=function TransformStream(transformer){if(_classCallCheck(this,TransformStream),void 0!==transformer.start&&"function"!=typeof transformer.start)throw new TypeError("start must be a function or undefined");if("function"!=typeof transformer.transform)throw new TypeError("transform must be a function");if(void 0!==transformer.flush&&"function"!=typeof transformer.flush)throw new TypeError("flush must be a function or undefined");this._transformer=transformer,this._transforming=!1,this._errored=!1,this._writableController=void 0,this._readableController=void 0,this._writableDone=!1,this._readableClosed=!1,this._resolveWrite=void 0,this._chunkPending=!1,this._chunk=void 0,this._enqueueFunction=TransformStreamEnqueueToReadable.bind(void 0,this),this._closeFunction=TransformStreamCloseReadable.bind(void 0,this),this._errorFunction=TransformStreamError.bind(void 0,this);var sink=new TransformStreamSink(this);try{this.writable=new WritableStream(sink,transformer.writableStrategy)}catch(e){if(this._errored===!1)throw TransformStreamError(this,e),e;return}var source=new TransformStreamSource(this);try{this.readable=new ReadableStream(source,transformer.readableStrategy)}catch(e){if(this.writable=void 0,this._errored===!1)throw TransformStreamError(this,e),e}}},{"./readable-stream.js":11,"./writable-stream.js":14,assert:2}],13:[function(require,module,exports){"use strict";var assert=require("assert");exports.rethrowAssertionErrorRejection=function(e){e&&e.constructor===assert.AssertionError&&setTimeout(function(){throw e},0)}},{assert:2}],14:[function(require,module,exports){"use strict";function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor))throw new TypeError("Cannot call a class as a function")}function AcquireWritableStreamDefaultWriter(stream){return new WritableStreamDefaultWriter(stream)}function IsWritableStream(x){return typeIsObject(x)?!!Object.prototype.hasOwnProperty.call(x,"_writableStreamController"):!1}function IsWritableStreamLocked(stream){return assert(IsWritableStream(stream)===!0,"IsWritableStreamLocked should only be used on known writable streams"),void 0!==stream._writer}function WritableStreamAbort(stream,reason){var state=stream._state;if("closed"===state)return Promise.resolve(void 0);if("errored"===state)return Promise.reject(stream._storedError);assert("writable"===state||"closing"===state);var error=new TypeError("Aborted");return WritableStreamError(stream,error),WritableStreamDefaultControllerAbort(stream._writableStreamController,reason)}function WritableStreamAddWriteRequest(stream){assert(IsWritableStreamLocked(stream)===!0),assert("writable"===stream._state);var promise=new Promise(function(resolve,reject){var writeRequest={_resolve:resolve,_reject:reject};stream._writeRequests.push(writeRequest)});return promise}function WritableStreamError(stream,e){var state=stream._state;assert("writable"===state||"closing"===state);var _iteratorNormalCompletion=!0,_didIteratorError=!1,_iteratorError=void 0;try{for(var _step,_iterator=stream._writeRequests[Symbol.iterator]();!(_iteratorNormalCompletion=(_step=_iterator.next()).done);_iteratorNormalCompletion=!0){var writeRequest=_step.value;writeRequest._reject(e)}}catch(err){_didIteratorError=!0,_iteratorError=err}finally{try{!_iteratorNormalCompletion&&_iterator["return"]&&_iterator["return"]()}finally{if(_didIteratorError)throw _iteratorError}}stream._writeRequests=[];var writer=stream._writer;void 0!==writer&&(defaultWriterClosedPromiseReject(writer,e),"writable"===state&&WritableStreamDefaultControllerGetBackpressure(stream._writableStreamController)===!0&&defaultWriterReadyPromiseResolve(writer)),stream._state="errored",stream._storedError=e}function WritableStreamFinishClose(stream){assert("closing"===stream._state),assert(void 0!==stream._writer),stream._state="closed",defaultWriterClosedPromiseResolve(stream._writer)}function WritableStreamFulfillWriteRequest(stream){assert(stream._writeRequests.length>0);var writeRequest=stream._writeRequests.shift();writeRequest._resolve(void 0)}function WritableStreamUpdateBackpressure(stream,backpressure){assert("writable"===stream._state);var writer=stream._writer;void 0!==writer&&(backpressure===!0?defaultWriterReadyPromiseReset(writer):(assert(backpressure===!1),defaultWriterReadyPromiseResolve(writer)))}function IsWritableStreamDefaultWriter(x){return typeIsObject(x)?!!Object.prototype.hasOwnProperty.call(x,"_ownerWritableStream"):!1}function WritableStreamDefaultWriterAbort(writer,reason){var stream=writer._ownerWritableStream;return assert(void 0!==stream),WritableStreamAbort(stream,reason)}function WritableStreamDefaultWriterClose(writer){var stream=writer._ownerWritableStream;assert(void 0!==stream);var state=stream._state;if("closed"===state||"errored"===state)return Promise.reject(new TypeError("The stream (in "+state+" state) is not in the writable state and cannot be closed"));assert("writable"===state);var promise=WritableStreamAddWriteRequest(stream);return WritableStreamDefaultControllerGetBackpressure(stream._writableStreamController)===!0&&defaultWriterReadyPromiseResolve(writer),stream._state="closing",WritableStreamDefaultControllerClose(stream._writableStreamController),promise}function WritableStreamDefaultWriterGetDesiredSize(writer){var stream=writer._ownerWritableStream,state=stream._state;return"errored"===state?null:"closed"===state?0:WritableStreamDefaultControllerGetDesiredSize(stream._writableStreamController)}function WritableStreamDefaultWriterWrite(writer,chunk){var stream=writer._ownerWritableStream;assert(void 0!==stream);var state=stream._state;if("closed"===state||"errored"===state)return Promise.reject(new TypeError("The stream (in "+state+" state) is not in the writable state and cannot be written to"));assert("writable"===state);var promise=WritableStreamAddWriteRequest(stream);return WritableStreamDefaultControllerWrite(stream._writableStreamController,chunk),promise}function WritableStreamDefaultControllerAbort(controller,reason){controller._queue=[];var sinkAbortPromise=PromiseInvokeOrFallbackOrNoop(controller._underlyingSink,"abort",[reason],"close",[]);return sinkAbortPromise.then(function(){})}function WritableStreamDefaultControllerClose(controller){EnqueueValueWithSize(controller._queue,"close",0),WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller)}function WritableStreamDefaultControllerGetDesiredSize(controller){var queueSize=GetTotalQueueSize(controller._queue);return controller._strategyHWM-queueSize}function WritableStreamDefaultControllerWrite(controller,chunk){var stream=controller._controlledWritableStream;assert("writable"===stream._state);var chunkSize=1;if(void 0!==controller._strategySize)try{chunkSize=controller._strategySize(chunk)}catch(chunkSizeE){return WritableStreamDefaultControllerErrorIfNeeded(controller,chunkSizeE),Promise.reject(chunkSizeE)}var writeRecord={chunk:chunk},lastBackpressure=WritableStreamDefaultControllerGetBackpressure(controller);try{EnqueueValueWithSize(controller._queue,writeRecord,chunkSize)}catch(enqueueE){return WritableStreamDefaultControllerErrorIfNeeded(controller,enqueueE),Promise.reject(enqueueE)}if("writable"===stream._state){var backpressure=WritableStreamDefaultControllerGetBackpressure(controller);lastBackpressure!==backpressure&&WritableStreamUpdateBackpressure(stream,backpressure)}WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller)}function IsWritableStreamDefaultController(x){return typeIsObject(x)?!!Object.prototype.hasOwnProperty.call(x,"_underlyingSink"):!1}function WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller){if("closed"!==controller._controlledWritableStream._state&&"errored"!==controller._controlledWritableStream._state&&controller._started!==!1&&controller._writing!==!0&&0!==controller._queue.length){var writeRecord=PeekQueueValue(controller._queue);"close"===writeRecord?WritableStreamDefaultControllerProcessClose(controller):WritableStreamDefaultControllerProcessWrite(controller,writeRecord.chunk)}}function WritableStreamDefaultControllerErrorIfNeeded(controller,e){"writable"!==controller._controlledWritableStream._state&&"closing"!==controller._controlledWritableStream._state||WritableStreamDefaultControllerError(controller,e)}function WritableStreamDefaultControllerProcessClose(controller){var stream=controller._controlledWritableStream;assert("closing"===stream._state,"can't process final write record unless already closed"),DequeueValue(controller._queue),assert(0===controller._queue.length,"queue must be empty once the final write record is dequeued");var sinkClosePromise=PromiseInvokeOrNoop(controller._underlyingSink,"close");sinkClosePromise.then(function(){"closing"===stream._state&&(WritableStreamFulfillWriteRequest(stream),WritableStreamFinishClose(stream))},function(r){WritableStreamDefaultControllerErrorIfNeeded(controller,r)})["catch"](rethrowAssertionErrorRejection)}function WritableStreamDefaultControllerProcessWrite(controller,chunk){controller._writing=!0;var sinkWritePromise=PromiseInvokeOrNoop(controller._underlyingSink,"write",[chunk]);sinkWritePromise.then(function(){var stream=controller._controlledWritableStream,state=stream._state;if("errored"!==state&&"closed"!==state){controller._writing=!1,WritableStreamFulfillWriteRequest(stream);var lastBackpressure=WritableStreamDefaultControllerGetBackpressure(controller);if(DequeueValue(controller._queue),"closing"!==state){var backpressure=WritableStreamDefaultControllerGetBackpressure(controller);lastBackpressure!==backpressure&&WritableStreamUpdateBackpressure(controller._controlledWritableStream,backpressure)}WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller)}},function(r){WritableStreamDefaultControllerErrorIfNeeded(controller,r)})["catch"](rethrowAssertionErrorRejection)}function WritableStreamDefaultControllerGetBackpressure(controller){var desiredSize=WritableStreamDefaultControllerGetDesiredSize(controller);return 0>=desiredSize}function WritableStreamDefaultControllerError(controller,e){var stream=controller._controlledWritableStream;assert("writable"===stream._state||"closing"===stream._state),WritableStreamError(stream,e),controller._queue=[]}function streamBrandCheckException(name){return new TypeError("WritableStream.prototype."+name+" can only be used on a WritableStream")}function defaultWriterBrandCheckException(name){return new TypeError("WritableStreamDefaultWriter.prototype."+name+" can only be used on a WritableStreamDefaultWriter")}function defaultWriterLockException(name){return new TypeError("Cannot "+name+" a stream using a released writer")}function defaultWriterClosedPromiseInitialize(writer){writer._closedPromise=new Promise(function(resolve,reject){writer._closedPromise_resolve=resolve,writer._closedPromise_reject=reject})}function defaultWriterClosedPromiseInitializeAsRejected(writer,reason){writer._closedPromise=Promise.reject(reason),writer._closedPromise_resolve=void 0,writer._closedPromise_reject=void 0}function defaultWriterClosedPromiseInitializeAsResolved(writer){writer._closedPromise=Promise.resolve(void 0),writer._closedPromise_resolve=void 0,writer._closedPromise_reject=void 0}function defaultWriterClosedPromiseReject(writer,reason){assert(void 0!==writer._closedPromise_resolve),assert(void 0!==writer._closedPromise_reject),writer._closedPromise_reject(reason),writer._closedPromise_resolve=void 0,writer._closedPromise_reject=void 0}function defaultWriterClosedPromiseResetToRejected(writer,reason){assert(void 0===writer._closedPromise_resolve),assert(void 0===writer._closedPromise_reject),writer._closedPromise=Promise.reject(reason)}function defaultWriterClosedPromiseResolve(writer){assert(void 0!==writer._closedPromise_resolve),assert(void 0!==writer._closedPromise_reject),writer._closedPromise_resolve(void 0),writer._closedPromise_resolve=void 0,writer._closedPromise_reject=void 0}function defaultWriterReadyPromiseInitialize(writer){writer._readyPromise=new Promise(function(resolve,reject){writer._readyPromise_resolve=resolve,writer._readyPromise_reject=reject})}function defaultWriterReadyPromiseInitializeAsResolved(writer){writer._readyPromise=Promise.resolve(void 0),writer._readyPromise_resolve=void 0,writer._readyPromise_reject=void 0}function defaultWriterReadyPromiseReject(writer,reason){assert(void 0!==writer._readyPromise_resolve),assert(void 0!==writer._readyPromise_reject),writer._readyPromise_reject(reason),writer._readyPromise_resolve=void 0,writer._readyPromise_reject=void 0}function defaultWriterReadyPromiseReset(writer){assert(void 0===writer._readyPromise_resolve),assert(void 0===writer._readyPromise_reject),writer._readyPromise=new Promise(function(resolve,reject){writer._readyPromise_resolve=resolve,writer._readyPromise_reject=reject})}function defaultWriterReadyPromiseResetToRejected(writer,reason){assert(void 0===writer._readyPromise_resolve),assert(void 0===writer._readyPromise_reject),writer._readyPromise=Promise.reject(reason)}function defaultWriterReadyPromiseResolve(writer){assert(void 0!==writer._readyPromise_resolve),assert(void 0!==writer._readyPromise_reject),writer._readyPromise_resolve(void 0),writer._readyPromise_resolve=void 0,writer._readyPromise_reject=void 0}var _createClass=function(){function defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||!1,descriptor.configurable=!0,"value"in descriptor&&(descriptor.writable=!0),Object.defineProperty(target,descriptor.key,descriptor)}}return function(Constructor,protoProps,staticProps){return protoProps&&defineProperties(Constructor.prototype,protoProps),staticProps&&defineProperties(Constructor,staticProps),Constructor}}(),assert=require("assert"),_require=require("./helpers.js"),InvokeOrNoop=_require.InvokeOrNoop,PromiseInvokeOrNoop=_require.PromiseInvokeOrNoop,PromiseInvokeOrFallbackOrNoop=_require.PromiseInvokeOrFallbackOrNoop,ValidateAndNormalizeQueuingStrategy=_require.ValidateAndNormalizeQueuingStrategy,typeIsObject=_require.typeIsObject,_require2=require("./utils.js"),rethrowAssertionErrorRejection=_require2.rethrowAssertionErrorRejection,_require3=require("./queue-with-sizes.js"),DequeueValue=_require3.DequeueValue,EnqueueValueWithSize=_require3.EnqueueValueWithSize,GetTotalQueueSize=_require3.GetTotalQueueSize,PeekQueueValue=_require3.PeekQueueValue,WritableStream=function(){function WritableStream(){var underlyingSink=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],_ref=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],size=_ref.size,_ref$highWaterMark=_ref.highWaterMark,highWaterMark=void 0===_ref$highWaterMark?1:_ref$highWaterMark;_classCallCheck(this,WritableStream),this._state="writable",this._storedError=void 0,this._writer=void 0,this._writableStreamController=void 0,this._writeRequests=[];var type=underlyingSink.type;if(void 0!==type)throw new RangeError("Invalid type is specified");this._writableStreamController=new WritableStreamDefaultController(this,underlyingSink,size,highWaterMark)}return _createClass(WritableStream,[{key:"abort",value:function(reason){return IsWritableStream(this)===!1?Promise.reject(streamBrandCheckException("abort")):IsWritableStreamLocked(this)===!0?Promise.reject(new TypeError("Cannot abort a stream that already has a writer")):WritableStreamAbort(this,reason)}},{key:"getWriter",value:function(){if(IsWritableStream(this)===!1)throw streamBrandCheckException("getWriter");return AcquireWritableStreamDefaultWriter(this)}},{key:"locked",get:function(){if(IsWritableStream(this)===!1)throw streamBrandCheckException("locked");return IsWritableStreamLocked(this)}}]),WritableStream}();exports.WritableStream=WritableStream;var WritableStreamDefaultWriter=function(){function WritableStreamDefaultWriter(stream){if(_classCallCheck(this,WritableStreamDefaultWriter),IsWritableStream(stream)===!1)throw new TypeError("WritableStreamDefaultWriter can only be constructed with a WritableStream instance");if(IsWritableStreamLocked(stream)===!0)throw new TypeError("This stream has already been locked for exclusive writing by another writer");this._ownerWritableStream=stream,stream._writer=this;var state=stream._state;"writable"===state||"closing"===state?defaultWriterClosedPromiseInitialize(this):"closed"===state?defaultWriterClosedPromiseInitializeAsResolved(this):(assert("errored"===state,"state must be errored"),defaultWriterClosedPromiseInitializeAsRejected(this,stream._storedError)),"writable"===state&&WritableStreamDefaultControllerGetBackpressure(stream._writableStreamController)===!0?defaultWriterReadyPromiseInitialize(this):defaultWriterReadyPromiseInitializeAsResolved(this,void 0)}return _createClass(WritableStreamDefaultWriter,[{key:"abort",value:function(reason){return IsWritableStreamDefaultWriter(this)===!1?Promise.reject(defaultWriterBrandCheckException("abort")):void 0===this._ownerWritableStream?Promise.reject(defaultWriterLockException("abort")):WritableStreamDefaultWriterAbort(this,reason);
+}},{key:"close",value:function(){if(IsWritableStreamDefaultWriter(this)===!1)return Promise.reject(defaultWriterBrandCheckException("close"));var stream=this._ownerWritableStream;return void 0===stream?Promise.reject(defaultWriterLockException("close")):"closing"===stream._state?Promise.reject(new TypeError("cannot close an already-closing stream")):WritableStreamDefaultWriterClose(this)}},{key:"releaseLock",value:function(){if(IsWritableStreamDefaultWriter(this)===!1)throw defaultWriterBrandCheckException("releaseLock");var stream=this._ownerWritableStream;if(void 0!==stream){assert(void 0!==stream._writer);var state=stream._state,releasedException=new TypeError("Writer was released and can no longer be used to monitor the stream's closedness");"writable"===state||"closing"===state?defaultWriterClosedPromiseReject(this,releasedException):defaultWriterClosedPromiseResetToRejected(this,releasedException),"writable"===state&&WritableStreamDefaultControllerGetBackpressure(stream._writableStreamController)===!0?defaultWriterReadyPromiseReject(this,releasedException):defaultWriterReadyPromiseResetToRejected(this,releasedException),stream._writer=void 0,this._ownerWritableStream=void 0}}},{key:"write",value:function(chunk){if(IsWritableStreamDefaultWriter(this)===!1)return Promise.reject(defaultWriterBrandCheckException("write"));var stream=this._ownerWritableStream;return void 0===stream?Promise.reject(defaultWriterLockException("write to")):"closing"===stream._state?Promise.reject(new TypeError("Cannot write to an already-closed stream")):WritableStreamDefaultWriterWrite(this,chunk)}},{key:"closed",get:function(){return IsWritableStreamDefaultWriter(this)===!1?Promise.reject(defaultWriterBrandCheckException("closed")):this._closedPromise}},{key:"desiredSize",get:function(){if(IsWritableStreamDefaultWriter(this)===!1)throw defaultWriterBrandCheckException("desiredSize");if(void 0===this._ownerWritableStream)throw defaultWriterLockException("desiredSize");return WritableStreamDefaultWriterGetDesiredSize(this)}},{key:"ready",get:function(){return IsWritableStreamDefaultWriter(this)===!1?Promise.reject(defaultWriterBrandCheckException("ready")):this._readyPromise}}]),WritableStreamDefaultWriter}(),WritableStreamDefaultController=function(){function WritableStreamDefaultController(stream,underlyingSink,size,highWaterMark){if(_classCallCheck(this,WritableStreamDefaultController),IsWritableStream(stream)===!1)throw new TypeError("WritableStreamDefaultController can only be constructed with a WritableStream instance");if(void 0!==stream._writableStreamController)throw new TypeError("WritableStreamDefaultController instances can only be created by the WritableStream constructor");this._controlledWritableStream=stream,this._underlyingSink=underlyingSink,this._queue=[],this._started=!1,this._writing=!1;var normalizedStrategy=ValidateAndNormalizeQueuingStrategy(size,highWaterMark);this._strategySize=normalizedStrategy.size,this._strategyHWM=normalizedStrategy.highWaterMark;var backpressure=WritableStreamDefaultControllerGetBackpressure(this);backpressure===!0&&WritableStreamUpdateBackpressure(stream,backpressure);var controller=this,startResult=InvokeOrNoop(underlyingSink,"start",[this]);Promise.resolve(startResult).then(function(){controller._started=!0,WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller)},function(r){WritableStreamDefaultControllerErrorIfNeeded(controller,r)})["catch"](rethrowAssertionErrorRejection)}return _createClass(WritableStreamDefaultController,[{key:"error",value:function(e){if(IsWritableStreamDefaultController(this)===!1)throw new TypeError("WritableStreamDefaultController.prototype.error can only be used on a WritableStreamDefaultController");var state=this._controlledWritableStream._state;if("closed"===state||"errored"===state)throw new TypeError("The stream is "+state+" and so cannot be errored");WritableStreamDefaultControllerError(this,e)}}]),WritableStreamDefaultController}()},{"./helpers.js":9,"./queue-with-sizes.js":10,"./utils.js":13,assert:2}]},{},[1])(1)}); \ No newline at end of file
diff --git a/www/external/screwdriver.js b/www/external/screwdriver.js
new file mode 100644
index 00000000..c6733776
--- /dev/null
+++ b/www/external/screwdriver.js
@@ -0,0 +1,151 @@
+;(function() {
+
+ if(typeof Blob === 'undefined')
+ return console.warn('Screw-FileReader is only meant to work in those' +
+ 'engine who already has some basic support for blob')
+
+ var blob = Blob.prototype
+ var fullStreamSupport = false
+ var basicStreamSupport = false
+ var fetchTransform = false
+ var URL = window.URL || window.webkitURL
+
+ function promisify(obj) {
+ return new Promise(function(resolve, reject) {
+ obj.onload =
+ obj.onerror = function(evt) {
+ obj.onload =
+ obj.onerror = null
+
+ evt.type === 'load'
+ ? resolve(obj.result || obj)
+ : reject(obj.error)
+ }
+ })
+ }
+
+ function toImage(url) {
+ var img = new Image
+ img.src = url
+ return promisify(img)
+ }
+
+ try {
+ new ReadableStream({})
+ basicStreamSupport = true
+ } catch (e) {}
+
+ try {
+ new ReadableStream({type: 'bytes'})
+ fullStreamSupport = true
+ } catch (e) {}
+
+ try {
+ (new Response(new Blob)).getReader()
+ fetchTransform = true
+ } catch (e) {}
+
+ if(!blob.arrayBuffer) {
+ blob.arrayBuffer = function arrayBuffer() {
+ var fr = new FileReader
+ fr.readAsArrayBuffer(this)
+ return promisify(fr)
+ }
+ }
+
+ if(!blob.text) {
+ blob.text = function text() {
+ var fr = new FileReader
+ fr.readAsText(this)
+ return promisify(fr)
+ }
+ }
+
+ if(!blob.dataURL) {
+ blob.dataURL = function dataURL() {
+ var fr = new FileReader
+ fr.readAsDataURL(this)
+ return promisify(fr)
+ }
+ }
+
+ if(!blob.url) {
+ blob.url = function url() {
+ return URL ? URL.createObjectURL(this) : null
+ }
+ }
+
+ if(!blob.json) {
+ blob.json = function json() {
+ return this.text().then(JSON.parse)
+ }
+ }
+
+ if(!blob.image) {
+ blob.image = function image() {
+ return Promise.resolve(this.url() || this.dataURL()).then(toImage)
+ }
+ }
+
+ if(!blob.stream) {
+ blob.stream =
+
+ fullStreamSupport ? function stream() {
+ var position = 0
+ var blob = this
+
+ return new ReadableStream({
+ type: 'bytes',
+ autoAllocateChunkSize: 524288,
+
+ pull: function (controller) {
+ var v = controller.byobRequest.view
+ var chunk = blob.slice(position, position + v.byteLength)
+ return chunk.arrayBuffer()
+ .then(function (buffer) {
+ let uint8array = new Uint8Array(buffer)
+ let bytesRead = uint8array.byteLength
+
+ position += bytesRead
+ v.set(uint8array)
+ controller.byobRequest.respond(bytesRead)
+
+ if(position >= blob.size)
+ controller.close()
+ })
+ }
+ })
+ }:
+
+ // basic stream support
+ basicStreamSupport ? function stream(blob){
+ var position = 0
+ var blob = this
+
+ return new ReadableStream({
+ pull: function (controller) {
+ var chunk = blob.slice(position, position + 524288)
+
+ return chunk.arrayBuffer().then(function (buffer) {
+ position += buffer.byteLength
+ let uint8array = new Uint8Array(buffer)
+ controller.enqueue(uint8array)
+
+ if(position == blob.size)
+ controller.close()
+ })
+ }
+ })
+ }:
+
+ // fetchTransform
+ fetchTransform ? function stream() {
+ return (new Response(this)).body
+ }:
+
+ function stream() {
+ throw new Error('Include https://github.com/creatorrr/web-streams-polyfill')
+ }
+ }
+
+}());
diff --git a/www/img/gif-anim.png b/www/img/gif-anim.png
new file mode 100644
index 00000000..1f7e8e3f
--- /dev/null
+++ b/www/img/gif-anim.png
Binary files differ
diff --git a/www/img/gif.svg b/www/img/gif.svg
new file mode 100644
index 00000000..86f92951
--- /dev/null
+++ b/www/img/gif.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generated by IcoMoon.io -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1024" height="1024" viewBox="0 0 1024 1024">
+<g id="icomoon-ignore">
+</g>
+<path fill="#000" d="M256.219 320h543.562c53.467 0 96.219 42.963 96.219 95.961v224.078c0 53.009-43.079 95.961-96.219 95.961h-543.562c-53.467 0-96.219-42.963-96.219-95.961v-224.078c0-53.009 43.079-95.961 96.219-95.961zM255.826 352c-35.25 0-63.826 28.806-63.826 63.745v224.511c0 35.205 28.875 63.745 63.826 63.745h544.348c35.25 0 63.826-28.806 63.826-63.745v-224.511c0-35.205-28.875-63.745-63.826-63.745h-544.348zM416 544v64h-64.006c-17.795 0-31.994-14.324-31.994-31.994v-96.012c0-17.795 14.324-31.994 31.994-31.994h96.006v-32h-95.844c-35.432 0-64.156 28.37-64.156 64.189v95.621c0 35.451 28.605 64.189 64.156 64.189h95.844v-128h-96v32h64zM512 448v160h-32v32h96v-32h-32v-160h32v-32h-96v32h32zM640 512v-64h128v-32h-160v224h32v-96h96v-32h-96z"></path>
+</svg>
+
+
diff --git a/www/img/mp4.svg b/www/img/mp4.svg
new file mode 100644
index 00000000..d26f884a
--- /dev/null
+++ b/www/img/mp4.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
+<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
+<g><g><g><path d="M521.3,393.5h-85.2v85.2h85.2c25.6,0,42.6-17,42.6-42.6S546.9,393.5,521.3,393.5z"/><path d="M862.2,223H137.8C65.4,223,10,278.4,10,350.9v298.3C10,721.6,65.4,777,137.8,777h724.3c72.4,0,127.8-55.4,127.8-127.8V350.9C990,278.4,934.6,223,862.2,223z M350.9,649.1h-42.6v-213l-42.6,85.2h-21.3H223l-42.6-85.2v213h-42.6V350.9h21.3h21.3l63.9,127.8l63.9-127.8h21.3h21.3V649.1z M521.3,521.3h-85.2v127.8h-42.6v-213v-85.2h127.8c46.9,0,85.2,38.3,85.2,85.2C606.5,483,568.2,521.3,521.3,521.3z M862.2,563.9h-42.6v85.2H777v-85.2H649.1v-42.6L777,350.9h42.6v170.4h42.6V563.9z"/><polygon points="777,521.3 777,414.8 700.3,521.3 "/></g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g></g>
+</svg>
diff --git a/www/index.html b/www/index.html
index 22e94f23..06b90ab7 100644
--- a/www/index.html
+++ b/www/index.html
@@ -2,16 +2,11 @@
<html>
<head>
-
-
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
-
- <meta http-equiv="Content-Security-Policy" content="img-src * android-webview-video-poster: 'self' data:; default-src * 'self' gap: wss: ws: ; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval';">
-
- <title></title>
+ <meta http-equiv="Content-Security-Policy" content="img-src * blob: android-webview-video-poster: cdvphotolibrary: 'self' data:; default-src * blob: 'self' gap: wss: ws: ; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval';">
+ <title></title>
<meta name="format-detection" content="telephone=no">
-
<link rel="stylesheet" href="css/animate.min.css">
<link rel="stylesheet" href="css/angular-circular-navigation.css">
<link rel="stylesheet" href="css/custommfb.css">
@@ -23,14 +18,10 @@
<link rel="stylesheet" href="lib/angular-wizard/dist/angular-wizard.min.css">
<link href="css/ionic.app.min.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
-
<!-- unmanaged externals -->
<link rel="stylesheet" href="external/ionic.content.banner.min.css">
<link rel="stylesheet" href="external/radio.css">
-
-
-
- <script src="lib/ionic/js/ionic.bundle.min.js"></script>
+ <script src="lib/ionic/js/ionic.bundle.js"></script>
<script src="lib/ngCordova/dist/ng-cordova.min.js"></script>
<script src="lib/filelogger/dist/filelogger.min.js"></script>
<script src="cordova.js"></script>
@@ -42,41 +33,44 @@
<script src="lib/crypto-js/crypto-js.js"></script>
<script src="lib/angular-awesome-slider/dist/angular-awesome-slider.min.js">
</script>
- <script src="lib/videogular/videogular.js"></script>
- <script src="lib/videogular-controls/vg-controls.js"></script>
- <script src="lib/videogular-overlay-play/vg-overlay-play.js"></script>
- <script src="lib/videogular-buffering/vg-buffering.js"></script>
+ <script src="lib/videogular/videogular.min.js"></script>
+ <script src="lib/videogular-controls/vg-controls.min.js"></script>
+ <script src="lib/videogular-overlay-play/vg-overlay-play.min.js"></script>
+ <script src="lib/videogular-buffering/vg-buffering.min.js"></script>
+ <script src="lib/videogular-cuepoints/cuepoints.js"></script>
<script src="lib/ion-datetime-picker/release/ion-datetime-picker.min.js"></script>
<script src="lib/angular-translate/angular-translate.min.js"></script>
<script src="lib/angular-translate-loader-static-files/angular-translate-loader-static-files.min.js"></script>
<script src="lib/vis/dist/vis-timeline-graph2d.min.js"></script>
<script src="lib/moment/min/moment-with-locales.min.js"></script>
-
<script src="lib/ng-mfb/src/mfb-directive.js"></script>
- <script src="lib/angular-touch/angular-touch.js"></script>
+ <script src="lib/angular-touch/angular-touch.min.js"></script>
<!-- unmanaged externals -->
- <script src="external/moment-timezone-with-data.min.js"></script>
+ <script src="external/moment-timezone-with-data.min.js"></script>
<script src="external/angular-ios9-uiwebview.patch.js"></script>
- <script src="external/ionRadio.js"></script>
- <script src="external/ng-websocket.js"></script>
+ <script src="external/ionRadio.min.js"></script>
+ <script src="external/ng-websocket.min.js"></script>
<script src="external/uri.min.js"></script>
- <script src="external/angular-carousel.js"></script>
- <script src="external/ion-pullup.js"></script>
- <script src="external/ionic.content.banner.js"></script>
+ <script src="external/angular-carousel.min.js"></script>
+ <script src="external/ion-pullup.min.js"></script>
+ <script src="external/ionic.content.banner.min.js"></script>
<script src="external/FileSaver.min.js"></script>
- <script src="external/canvas-toBlob.js"></script>
- <script src="external/imagesloaded.pkgd.js"></script>
- <script src="external/packery.pkgd.js"></script>
- <script src="external/draggabilly.pkgd.js"></script>
- <script src="external/ionic.scroll.sista.js"></script>
- <script src="external/angular-circular-navigation.js"></script>
+ <script src="external/canvas-toBlob.min.js"></script>
+ <script src="external/imagesloaded.pkgd.min.js"></script>
+ <script src="external/packery.pkgd.min.js"></script>
+ <script src="external/draggabilly.pkgd.min.js"></script>
+ <script src="external/ionic.scroll.sista.min.js"></script>
+ <script src="external/angular-circular-navigation.min.js"></script>
<script src="external/Chart2.min.js"></script>
-
-
+ <script src="external/screwdriver.js"></script>
+ <script src="external/polyfill.min.js"></script>
+ <script src="external/gifwriter.min.js"></script>
+ <script src="external/NeuQuant.min.js"></script>
- <!-- app related JS -->
+
+ <!-- app related JS -->
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
<script src="js/DataModel.js"></script>
@@ -105,25 +99,14 @@
<script src="js/WizardCtrl.js"></script>
<script src="js/MenuController.js"></script>
<script src="js/EventsModalGraphCtrl.js"></script>
-
-
-
</head>
-
-
-
-
-
<!-- <body ng-app="starter" > -->
<!-- I want to start angular only after cordova device is ready, so I'll tag it on device ready -->
-
<body bgcolor="#555555">
-
<!-- For some reason - which I haven't debugged yet, when I was using the ionic side menu template
I was having problems with tabs/sliders in views, I think its to do with controls being alive in
the menu. this approach puts controls in each page and that works well -->
-
<!-- This is the Side menu options -->
<!-- ng-cloak makes sure no HTML items load up before Angular is initialized -->
<!-- This avoids sudden flashes of odd/mis-aligned icons-->
@@ -136,175 +119,140 @@
</ion-nav-bar>
<ion-nav-view></ion-nav-view>
</ion-side-menu-content>
-
<ion-side-menu>
<ion-header-bar class="bar bar-header bar-stable">
<h1 class="title">{{'kMenuOptions'|translate}}</h1>
-
</ion-header-bar>
<ion-content has-header="true" mouse-wheel-scroll>
- <!-- <ion-scroll scrollbar-y="false" style="height:100%" >-->
- <ion-list>
-
- <ion-item href="#/montage" menu-close>
-
- <!--<span ng-if="$root.runMode=='lowbw'" style="float:right;margin-top:-18px;background-color:#f1c40f;color:#000;font-size:11px;opacity:0.7;width:20px;border-radius: 0px 0px 5px 5px;display:inline-block;text-align:center;">&nbsp;<i class="icon ion-arrow-graph-down-left"></i>&nbsp;</span>-->
-
- <span class=" item-icon-left">
+ <!-- <ion-scroll scrollbar-y="false" style="height:100%" >-->
+ <ion-list>
+ <ion-item href="#/montage" menu-close>
+ <!--<span ng-if="$root.runMode=='lowbw'" style="float:right;margin-top:-18px;background-color:#f1c40f;color:#000;font-size:11px;opacity:0.7;width:20px;border-radius: 0px 0px 5px 5px;display:inline-block;text-align:center;">&nbsp;<i class="icon ion-arrow-graph-down-left"></i>&nbsp;</span>-->
+ <span class=" item-icon-left">
<i class="icon ion-ios-eye"></i>
</span>{{'kMenuMontage'|translate}}
- </ion-item>
-
- <ion-item href="#/montage-history" menu-close>
- <span class=" item-icon-left">
+ </ion-item>
+ <ion-item href="#/montage-history" menu-close>
+ <span class=" item-icon-left">
<i class="icon ion-calendar"></i>
</span>{{'kMenuEventMontage'|translate}}
- </ion-item>
-
- <ion-item href="#/timeline" menu-close>
- <span class=" item-icon-left">
+ </ion-item>
+ <ion-item href="#/timeline" menu-close>
+ <span class=" item-icon-left">
<i class="icon ion-android-time"></i>
</span>{{'kMenuTimeline'|translate}}
- </ion-item>
-
- <ion-item href="#/events/0/false" menu-close>
- <span class=" item-icon-left">
+ </ion-item>
+ <ion-item href="#/events/0/false" menu-close>
+ <span class=" item-icon-left">
<i class="icon ion-ios-calendar-outline"></i>
</span>{{'kMenuEvents'|translate}}
- </ion-item>
-
- <ion-item href="#/monitors" menu-close>
- <span class=" item-icon-left">
+ </ion-item>
+ <ion-item href="#/monitors" menu-close>
+ <span class=" item-icon-left">
<i class="icon ion-ios-videocam-outline"></i>
</span>{{'kMenuMonitors'|translate}}
- </ion-item>
-
- <ion-item href="#/state" menu-close>
- <span class=" item-icon-left">
+ </ion-item>
+ <ion-item href="#/state" menu-close>
+ <span class=" item-icon-left">
<i class="icon ion-information-circled"></i>
</span> {{'kMenuSystemStatus'|translate}}
- </ion-item>
-
- <ion-item nav-clear menu-close href="#/login/false">
- <span class=" item-icon-left">
+ </ion-item>
+ <ion-item nav-clear menu-close href="#/login/false">
+ <span class=" item-icon-left">
<i class="icon ion-person"></i>
{{'kMenuZMSettings'|translate}}
<!--<span class="item-note" style="width:90px">
{{$root.getProfileName();}}
</span>-->
- <span style="float:right;margin-top:-18px;background-color:#444444;color:#fff;font-size:11px;opacity:0.7;width:90px;border-radius: 0px 0px 5px 5px;:text-overflow:ellipsis;overflow:hidden;white-space:nowrap;display:inline-block;text-align:center;">&nbsp;{{$root.getProfileName();}}&nbsp;</span>
- </span>
- </ion-item>
-
- <ion-item nav-clear menu-close href="#/devoptions">
- <span class=" item-icon-left">
+ <span style="float:right;margin-top:-18px;background-color:#444444;color:#fff;font-size:11px;opacity:0.7;width:90px;border-radius: 0px 0px 5px 5px;:text-overflow:ellipsis;overflow:hidden;white-space:nowrap;display:inline-block;text-align:center;">&nbsp;{{$root.getProfileName();}}&nbsp;</span>
+ </span>
+ </ion-item>
+ <ion-item nav-clear menu-close href="#/devoptions">
+ <span class=" item-icon-left">
<i class="icon ion-settings"></i>
</span> {{'kMenuDevSettings'|translate}}
- </ion-item>
-
-
- <div ng-controller="MenuController">
- <ion-item ng-click="switchLang()" nav-clear menu-close href="">
- <span class=" item-icon-left">
+ </ion-item>
+ <div ng-controller="MenuController">
+ <ion-item ng-click="switchLang()" nav-clear menu-close href="">
+ <span class=" item-icon-left">
<i class="icon ion-earth"></i>
</span> {{'kLanguage'|translate}}
- </ion-item>
- </div>
-
- <ion-item nav-clear menu-close href="#/help">
- <span class=" item-icon-left">
+ </ion-item>
+ </div>
+ <ion-item nav-clear menu-close href="#/help">
+ <span class=" item-icon-left">
<i class="icon ion-help"></i>
</span> {{'kMenuHelp'|translate}}
- </ion-item>
-
- <ion-item nav-clear menu-close href="#/wizard">
- <span class=" item-icon-left">
+ </ion-item>
+ <ion-item nav-clear menu-close href="#/wizard">
+ <span class=" item-icon-left">
<i class="icon ion-wand"></i>
</span> {{'kMenuWizard'|translate}}
- </ion-item>
-
- <div ng-if="$root.showBlog">
- <ion-item nav-clear menu-close href="#/news">
- <span class=" item-icon-left">
+ </ion-item>
+ <div ng-if="$root.showBlog">
+ <ion-item nav-clear menu-close href="#/news">
+ <span class=" item-icon-left">
<i class="icon ion-radio-waves"></i>
</span>{{'kMenuNews'|translate}}<span style="color:#268d3a;"> {{$root.newBlogPost}}</span>
- </ion-item>
- </div>
-
- <ion-item nav-clear menu-close href="#/log">
- <span class=" item-icon-left">
+ </ion-item>
+ </div>
+ <ion-item nav-clear menu-close href="#/log">
+ <span class=" item-icon-left">
<i class="icon ion-clipboard"></i>
</span> {{'kMenuLogs'|translate}}
- </ion-item>
-
-
-
- <div ng-if="$root.newVersionAvailable && $root.platformOS=='desktop'">
- <ion-item nav-clear menu-close href="">
- <span class=" item-icon-left">
+ </ion-item>
+ <div ng-if="$root.newVersionAvailable && $root.platformOS=='desktop'">
+ <ion-item nav-clear menu-close href="">
+ <span class=" item-icon-left">
<i class="icon ion-email-unread"></i>
</span> <span style="color:#268d3a;">{{$root.newVersionAvailable}}</span>
- </ion-item>
- </div>
-
-
-
- <div ng-if="$root.platformOS=='android'">
- <ion-item ng-click="$root.exitApp();">
- <span class=" item-icon-left">
+ </ion-item>
+ </div>
+ <div ng-if="$root.platformOS=='android'">
+ <ion-item ng-click="$root.exitApp();">
+ <span class=" item-icon-left">
<i class="icon ion-close-circled"></i>
</span> {{'kMenuExit'|translate}}
- </ion-item>
-
- </div>
-
- <ion-item style="color:rgb(106, 106, 106); font-size:90%;">
- <i class="ion-ios-location"></i> {{$root.getLocalTimeZone();}} <br/>
- <span ng-if="$root.isTzSupported()"><i class="icon icon-server"></i> {{$root.getServerTimeZoneNow();}} </span>
</ion-item>
-
-
-
-
-
- </ion-list>
+ </div>
+ <ion-item style="color:rgb(106, 106, 106); font-size:90%;">
+ <i class="ion-ios-location"></i> {{$root.getLocalTimeZone();}}
+ <br/>
+ <span ng-if="$root.isTzSupported()"><i class="icon icon-server"></i> {{$root.getServerTimeZoneNow();}} </span>
+ </ion-item>
+ </ion-list>
<!--</ion-scroll>-->
</ion-content>
</ion-side-menu>
</ion-side-menus>
-
-
-
-
<!-- This is where is bootstrap angular - if I don't do this, then the window jumps around
after the status bar comes on - because the window kicked in before phonegap got ready -->
-
<script>
- window.ionic.Platform.ready(function() {
- console.log("******* PLATFORM READY ****");
+ window.ionic.Platform.ready(function()
+ {
+ console.log("******* PLATFORM READY ****");
- angular.bootstrap(document, ['zmApp']);
- });
+ angular.bootstrap(document, ['zmApp']);
+ });
</script>
-
<!-- keyboard input jump fix
https://forum.ionicframework.com/t/ionic-keyboard-scroll-issue-ios/34420/2?u=pliablepixels
-->
-
<script>
- window.addEventListener('native.keyboardshow', keyboardShowHandler);
-
- function keyboardShowHandler(e) {
- setTimeout(function() {
- $('html, body').animate({
- scrollTop: 0
- }, 1000);
- }, 0);
- }
+ /* global angular, $*/
+ window.addEventListener('native.keyboardshow', keyboardShowHandler);
+
+ function keyboardShowHandler(e)
+ {
+ setTimeout(function()
+ {
+ $('html, body').animate(
+ {
+ scrollTop: 0
+ }, 1000);
+ }, 0);
+ }
</script>
-
-
-
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/www/js/DataModel.js b/www/js/DataModel.js
index 05972b9b..d1afce9e 100644
--- a/www/js/DataModel.js
+++ b/www/js/DataModel.js
@@ -1,6 +1,5 @@
/* jshint -W041 */
-
/* jslint browser: true*/
/* global cordova,StatusBar,angular,console, URI, moment, localforage, CryptoJS, Connection */
@@ -13,7 +12,8 @@ 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) {
+ $ionicPopup, $localstorage, $state, $ionicNativeTransitions, $translate)
+ {
var zmAppVersion = "unknown";
var isBackground = false;
@@ -28,23 +28,38 @@ angular.module('zmApp.controllers')
var tz = "";
var isTzSupported = false;
-
- var languages = [{
+ var languages = [
+ {
text: 'English',
value: 'en'
- }, {
+ },
+ {
+ text: 'العربية',
+ value: 'ar'
+ },
+ {
+ text: 'Español',
+ value: 'es'
+ },
+ {
text: 'Italian',
value: 'it'
- }, {
- text: 'Русский',
- value: 'ru'
- }, {
+ },
+ {
+ text: 'Polski',
+ value: 'pl'
+ },
+ {
text: 'Portugese',
value: 'pt'
- }, {
- text: 'العربية',
- value: 'ar'
},
+ {
+ text: 'Русский',
+ value: 'ru'
+ },
+
+
+
/* {
text: 'Hindi',
value: 'hi'
@@ -92,7 +107,7 @@ angular.module('zmApp.controllers')
'montageOrder': '',
'montageHiddenOrder': '',
'montageArraySize': '0',
-
+ 'showMontageSubMenu': false,
'graphSize': 2000,
'enableAlarmCount': true,
'minAlarmCount': 1,
@@ -106,6 +121,7 @@ angular.module('zmApp.controllers')
'enableBlog': true,
'use24hr': false,
'packeryPositions': '',
+ 'packeryPositionsArray': {},
'EHpackeryPositions': '',
'packerySizes': '',
'timelineModalGraphType': 'all',
@@ -126,21 +142,18 @@ angular.module('zmApp.controllers')
'followTimeLine': false,
'timelineScale': -1,
-
-
};
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) {
+ function isEmpty(obj)
+ {
// null and undefined are "empty"
if (obj == null) return true;
@@ -153,30 +166,36 @@ angular.module('zmApp.controllers')
// 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) {
+ for (var key in obj)
+ {
if (hasOwnProperty.call(obj, key)) return false;
}
return true;
}
- function getBandwidth() {
+ function getBandwidth()
+ {
// if mode is not on always return high
- if (loginData.enableLowBandwidth == false) {
+ if (loginData.enableLowBandwidth == false)
+ {
return "highbw";
}
// if mode is force on, return low
- if (loginData.enableLowBandwidth == true && loginData.autoSwitchBandwidth != true) {
+ if (loginData.enableLowBandwidth == true && loginData.autoSwitchBandwidth != true)
+ {
return "lowbw";
}
- if (loginData.enableLowBandwidth == true && loginData.autoSwitchBandwidth == true && $rootScope.platformOS == 'desktop') {
+ 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) {
+ switch (networkState)
+ {
case Connection.WIFI:
strState = "highbw";
@@ -197,9 +216,12 @@ angular.module('zmApp.controllers')
//--------------------------------------------------------------------------
// 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) {
+ function debug(val)
+ {
+ if (loginData.enableDebug && loginData.enableLogs)
+ {
+ if (val !== undefined)
+ {
var regex1 = /"password":".*?"/g;
var regex2 = /&pass=.*?(?=["&]|$)/g;
@@ -212,9 +234,12 @@ angular.module('zmApp.controllers')
}
}
- function log(val, logtype) {
- if (loginData.enableLogs) {
- if (val !== undefined) {
+ function log(val, logtype)
+ {
+ if (loginData.enableLogs)
+ {
+ if (val !== undefined)
+ {
var regex1 = /"password":".*?"/g;
var regex2 = /&pass=.*?(?=["&]|$)/g;
@@ -231,31 +256,44 @@ angular.module('zmApp.controllers')
}
}
-
- function reloadMonitorDisplayStatus() {
+ function reloadMonitorDisplayStatus()
+ {
debug("Loading hidden/unhidden status...");
var positionsStr = loginData.packeryPositions;
//console.log ("positionStr="+positionsStr);
var positions = {};
- if (loginData.packeryPositions != '') {
+ if (loginData.packeryPositions != '')
+ {
positions = JSON.parse(positionsStr);
- for (var m = 0; m < monitors.length; m++) {
- for (var p = 0; p < positions.length; p++) {
- if (monitors[m].Monitor.Id == positions[p].attr) {
+ for (var m = 0; m < monitors.length; m++)
+ {
+ for (var p = 0; p < positions.length; p++)
+ {
+ if (monitors[m].Monitor.Id == positions[p].attr)
+ {
monitors[m].Monitor.listDisplay = positions[p].display;
debug("DataModel: Setting MID:" + monitors[m].Monitor.Id + " to " + monitors[m].Monitor.listDisplay);
}
-
}
}
+ }
+ 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) {
+ function setLogin(newLogin)
+ {
loginData = angular.copy(newLogin);
serverGroupList[loginData.serverName] = angular.copy(loginData);
@@ -263,23 +301,21 @@ angular.module('zmApp.controllers')
//console.log ("****serverLogin was encrypted to " + ct);
//$localstorage.setObject("serverGroupList", serverGroupList);
- localforage.setItem("serverGroupList", ct, function(err) {
+ 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) {
+ 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) {
+ function versionCompare(left, right)
+ {
if (typeof left + typeof right != 'stringstring')
return false;
@@ -288,10 +324,14 @@ angular.module('zmApp.controllers')
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]))) {
+ 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]))) {
+ }
+ else if ((b[i] && !a[i] && parseInt(b[i]) > 0) || (parseInt(a[i]) < parseInt(b[i])))
+ {
return -1;
}
}
@@ -299,14 +339,15 @@ angular.module('zmApp.controllers')
return 0;
}
-
//--------------------------------------------------------------------------
// Banner display of messages
//--------------------------------------------------------------------------
- function displayBanner(mytype, mytext, myinterval, mytimer) {
+ function displayBanner(mytype, mytext, myinterval, mytimer)
+ {
var contentBannerInstance =
- $ionicContentBanner.show({
+ $ionicContentBanner.show(
+ {
text: mytext || 'no text',
interval: myinterval || 2000,
//autoClose: mytimer || 6000,
@@ -315,29 +356,30 @@ angular.module('zmApp.controllers')
//cancelOnStateChange: false
});
- $timeout(function() {
+ $timeout(function()
+ {
contentBannerInstance();
}, mytimer || 6000);
}
-
return {
//-------------------------------------------------------------
// used by various controllers to log messages to file
//-------------------------------------------------------------
- migrationComplete: function() {
+ migrationComplete: function()
+ {
migrationComplete = true;
},
-
-
- isEmpty: function(obj) {
+ isEmpty: function(obj)
+ {
return isEmpty(obj);
},
- log: function(val, type) {
+ log: function(val, type)
+ {
var logtype = 'info';
if (type != undefined)
logtype = type;
@@ -345,32 +387,35 @@ angular.module('zmApp.controllers')
},
- debug: function(val) {
-
+ debug: function(val)
+ {
debug(val);
},
- setLastUpdateCheck: function(val) {
+ setLastUpdateCheck: function(val)
+ {
lastUpdateCheck = val;
localforage.setItem("lastUpdateCheck", lastUpdateCheck);
},
- getLastUpdateCheck: function() {
+ getLastUpdateCheck: function()
+ {
return lastUpdateCheck;
},
- setLatestBlogPostChecked: function(val) {
- //console.log (">>>>>>>>>>>> Setting blog date: " + val);
+ setLatestBlogPostChecked: function(val)
+ {
+ console.log (">>>>>>>>>>>> Setting blog date: " + val);
latestBlogPostChecked = val;
localforage.setItem("latestBlogPostChecked", latestBlogPostChecked);
},
- getLatestBlogPostChecked: function() {
+ 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
@@ -380,8 +425,8 @@ angular.module('zmApp.controllers')
// the ZM authors fix this and streamline the access of images
// from APIs, I don't have an option
-
- zmStateGo: function(state, p1, p2) {
+ zmStateGo: function(state, p1, p2)
+ {
if ($rootScope.platformOS == 'desktop')
$state.go(state, p1, p2);
else
@@ -389,14 +434,16 @@ angular.module('zmApp.controllers')
},
// used when an empty server profile is created
- getDefaultLoginObject: function() {
+ getDefaultLoginObject: function()
+ {
return angular.copy(defaultLoginData);
},
-
- getReachableConfig: function(skipFirst) {
+ getReachableConfig: function(skipFirst)
+ {
var d = $q.defer();
- if (loginData.serverName == "") {
+ if (loginData.serverName == "")
+ {
log("Reachable: No server name configured, likely first use?");
d.reject("No servers");
return d.promise;
@@ -407,9 +454,11 @@ angular.module('zmApp.controllers')
//log ("Making sure " + loginData.serverName + " is reachable...");
var tLd = serverGroupList[loginData.serverName];
- if (skipFirst && tLd.fallbackConfiguration) {
+ if (skipFirst && tLd.fallbackConfiguration)
+ {
tLd = serverGroupList[tLd.fallbackConfiguration];
- if (!tLd) {
+ if (!tLd)
+ {
d.reject("No available severs");
loginData = savedLoginData;
return d.promise;
@@ -417,38 +466,43 @@ angular.module('zmApp.controllers')
}
}
-
-
var keepBuilding = true;
- while (keepBuilding == true && tLd) {
+ 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({
+ chainURLs.push(
+ {
url: tLd.url + "/index.php",
server: tLd.serverName
});
log("Fallback of " + tLd.serverName + " is " + tLd.fallbackConfiguration);
- if (tLd.fallbackConfiguration) {
+ if (tLd.fallbackConfiguration)
+ {
tLd = serverGroupList[tLd.fallbackConfiguration];
- if (tLd === undefined) {
+ 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 {
+ }
+ else
+ {
log("reached end of chain loop");
}
- } else {
+ }
+ else
+ {
log("detected loop when " + tLd.serverName + " fallsback to " + tLd.fallbackConfiguration);
keepBuilding = false;
}
}
-
-
//contactedServers.push(loginData.serverName);
- findFirstReachableUrl(chainURLs).then(function(firstReachableUrl) {
+ findFirstReachableUrl(chainURLs).then(function(firstReachableUrl)
+ {
d.resolve(firstReachableUrl);
// also make sure loginData points to this now
@@ -462,90 +516,99 @@ angular.module('zmApp.controllers')
return d.promise;
// OK: do something with firstReachableUrl
- }, function() {
+ }, 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++) {
+ 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({
+ 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) {
+ if (loginData.reachability)
+ {
//console.log ("************* AUGH");
- return $http.get(urls[0].url).then(function() {
+ return $http.get(urls[0].url).then(function()
+ {
log("Success: reachability on " + urls[0].url);
$ionicLoading.hide();
return urls[0];
- }, function(err) {
+ }, function(err)
+ {
log("Failed reachability on " + urls[0].url + " with error " + JSON.stringify(err));
return findFirstReachableUrl(urls.slice(1));
});
- } else {
+ }
+ else
+ {
log("Reachability is disabled in config, faking this test and returning success on " + urls[0]);
return urls[0];
}
- } else {
+ }
+ else
+ {
$ionicLoading.hide();
return $q.reject("No reachable URL");
}
-
}
return d.promise;
-
},
- init: function() {
+ init: function()
+ {
// console.log("****** DATAMODEL INIT SERVICE CALLED ********");
-
-
-
-
-
-
-
log("ZMData init: checking for stored variables & setting up log file");
- latestBlogPostChecked = localforage.getItem("latestBlogPostChecked") || null;
+ localforage.getItem("latestBlogPostChecked")
+ .then (function (val) {latestBlogPostChecked = val;},
+ function (err) {latestBlogPostChecked = null;});
+
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: "retrieving profile data..."
});
-
-
-
- localforage.getItem("serverGroupList").then(function(val) {
+ localforage.getItem("serverGroupList").then(function(val)
+ {
// decrypt it now
var decodedVal;
- if (typeof val == 'string') {
+ 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 {
+ }
+ else
+ {
log("user profile not encrypted");
decodedVal = val;
}
@@ -557,7 +620,6 @@ angular.module('zmApp.controllers')
$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);
@@ -565,7 +627,8 @@ angular.module('zmApp.controllers')
var isFoundDemo = false;
var as = Object.keys(serverGroupList);
- for (var x = 0; x < as.length; x++) {
+ for (var x = 0; x < as.length; x++)
+ {
if (as[x] == 'zmNinjaDemo')
isFoundDemo = true;
//console.log ("************ FOUND SERVER NAME " + as[x]);
@@ -575,25 +638,29 @@ angular.module('zmApp.controllers')
// Don't add the demo if there is another server
// because this means the user deleted it
- if (!isFoundDemo && as.length == 0) {
+ 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({
+ $ionicLoading.show(
+ {
template: "retrieving profile data..."
});
localforage.getItem("defaultServerName")
- .then(function(val) {
+ .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)) {
+ if (!isEmpty(loadedData))
+ {
loginData = loadedData;
// old version hacks for new variables
@@ -602,53 +669,57 @@ angular.module('zmApp.controllers')
loginData.persistMontageOrder = true;
loginData.enableh264 = true;
- if (typeof loginData.enableAlarmCount === 'undefined') {
+ if (typeof loginData.enableAlarmCount === 'undefined')
+ {
debug("enableAlarmCount does not exist, setting to true");
loginData.enableAlarmCount = true;
}
- if (typeof loginData.onTapScreen == 'undefined') {
+ 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')) {
+ loginData.onTapScreen != $translate.instant('kTapLiveMonitor'))
+ {
log("Invalid onTap setting found, resetting");
loginData.onTapScreen = $translate.instant('kMontage');
}
-
- if (typeof loginData.minAlarmCount === 'undefined') {
+ if (typeof loginData.minAlarmCount === 'undefined')
+ {
debug("minAlarmCount does not exist, setting to true");
loginData.minAlarmCount = 1;
}
-
- if (typeof loginData.montageSize == 'undefined') {
+ if (typeof loginData.montageSize == 'undefined')
+ {
debug("montageSize does not exist, setting to 2 (2 per col)");
loginData.montageSize = 2;
}
-
- if (typeof loginData.useNphZms == 'undefined') {
+ if (typeof loginData.useNphZms == 'undefined')
+ {
debug("useNphZms does not exist. Setting to true");
loginData.useNphZms = true;
}
-
-
- if (typeof loginData.useNphZmsForEvents == 'undefined') {
+ if (typeof loginData.useNphZmsForEvents == 'undefined')
+ {
debug("useNphZmsForEvents does not exist. Setting to true");
loginData.useNphZmsForEvents = true;
}
- if (typeof loginData.forceImageModePath == 'undefined') {
+ if (typeof loginData.forceImageModePath == 'undefined')
+ {
debug("forceImageModePath does not exist. Setting to false");
loginData.forceImageModePath = false;
}
- if (typeof loginData.reachability == 'undefined') {
+ if (typeof loginData.reachability == 'undefined')
+ {
debug("reachability does not exist. Setting to true");
loginData.reachability = true;
}
@@ -659,74 +730,88 @@ angular.module('zmApp.controllers')
loginData.useNphZms = true;
loginData.useNphZmsForEvents = true;
- if (typeof loginData.packMontage == 'undefined') {
+ if (typeof loginData.packMontage == 'undefined')
+ {
debug("packMontage does not exist. Setting to false");
loginData.packMontage = false;
}
- if (typeof loginData.forceNetworkStop == 'undefined') {
+ if (typeof loginData.forceNetworkStop == 'undefined')
+ {
debug("forceNetwork does not exist. Setting to false");
loginData.forceNetworkStop = false;
}
- if (typeof loginData.enableLogs == 'undefined') {
+ if (typeof loginData.enableLogs == 'undefined')
+ {
debug("enableLogs does not exist. Setting to true");
loginData.enableLogs = true;
}
-
-
- if (typeof loginData.defaultPushSound == 'undefined') {
+ if (typeof loginData.defaultPushSound == 'undefined')
+ {
debug("defaultPushSound does not exist. Setting to false");
loginData.defaultPushSound = false;
}
-
-
- if (typeof loginData.exitOnSleep == 'undefined') {
+ if (typeof loginData.exitOnSleep == 'undefined')
+ {
debug("exitOnSleep does not exist. Setting to false");
loginData.exitOnSleep = false;
}
- if (typeof loginData.enableBlog == 'undefined') {
+ if (typeof loginData.enableBlog == 'undefined')
+ {
debug("enableBlog does not exist. Setting to true");
loginData.enableBlog = true;
}
- if (typeof loginData.packeryPositions == 'undefined') {
+ 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') {
+ if (typeof loginData.EHpackeryPositions == 'undefined')
+ {
debug("EHpackeryPositions does not exist. Setting to empty");
loginData.EHpackeryPositions = "";
}
-
- if (typeof loginData.packerySizes == 'undefined') {
+ if (typeof loginData.packerySizes == 'undefined')
+ {
debug("packerySizes does not exist. Setting to empty");
loginData.packerySizes = "";
}
- if (typeof loginData.use24hr == 'undefined') {
+ if (typeof loginData.use24hr == 'undefined')
+ {
debug("use24hr does not exist. Setting to false");
loginData.use24hr = false;
}
- if (typeof timelineModalGraphType == 'undefined') {
+ 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') {
+ if (typeof loginData.resumeDelay == 'undefined')
+ {
debug("resumeDelay does not exist. Setting to 0");
loginData.resumeDelay = 0;
@@ -734,47 +819,50 @@ angular.module('zmApp.controllers')
// override resumeDelay - it was developed on a wrong assumption
loginData.resumeDelay = 0;
-
-
- if (typeof loginData.montageHistoryQuality == 'undefined') {
+ if (typeof loginData.montageHistoryQuality == 'undefined')
+ {
debug("montageHistoryQuality does not exist. Setting to 50");
loginData.montageHistoryQuality = "50";
}
- if (typeof loginData.disableNative == 'undefined') {
+ if (typeof loginData.disableNative == 'undefined')
+ {
debug("disableNative not found, setting to false");
loginData.disableNative = false;
}
- if (typeof loginData.vibrateOnPush == 'undefined') {
+ if (typeof loginData.vibrateOnPush == 'undefined')
+ {
debug("vibrate on push not found, setting to true");
loginData.vibrateOnPush = true;
}
- if (typeof loginData.soundOnPush == 'undefined') {
+ if (typeof loginData.soundOnPush == 'undefined')
+ {
debug("sound on push not found, setting to true");
loginData.soundOnPush = true;
}
- if (typeof loginData.cycleMonitors == 'undefined') {
+ if (typeof loginData.cycleMonitors == 'undefined')
+ {
loginData.cycleMonitors = false;
}
- if (typeof loginData.cycleMonitorsInterval == 'undefined') {
+ if (typeof loginData.cycleMonitorsInterval == 'undefined')
+ {
loginData.cycleMonitorsInterval = 10;
}
-
- if (typeof loginData.enableLowBandwidth == 'undefined') {
-
+ if (typeof loginData.enableLowBandwidth == 'undefined')
+ {
loginData.enableLowBandwidth = false;
@@ -782,11 +870,8 @@ angular.module('zmApp.controllers')
// wtf is wrong with this ternary?
//$rootScope.runMode = (loginData.enableLowBandwith==true)? "low": "normal";
-
-
-
- if (typeof loginData.autoSwitchBandwidth == 'undefined') {
-
+ if (typeof loginData.autoSwitchBandwidth == 'undefined')
+ {
loginData.autoSwitchBandwidth = false;
@@ -795,66 +880,70 @@ angular.module('zmApp.controllers')
$rootScope.runMode = getBandwidth();
log("Setting DataModel init bandwidth to: " + $rootScope.runMode);
-
-
-
- if (typeof loginData.refreshSecLowBW == 'undefined') {
+ if (typeof loginData.refreshSecLowBW == 'undefined')
+ {
loginData.refreshSecLowBW = 8;
}
- if (typeof loginData.disableAlarmCheckMontage == 'undefined') {
+ if (typeof loginData.disableAlarmCheckMontage == 'undefined')
+ {
loginData.disableAlarmCheckMontage = false;
}
-
- if (typeof loginData.useLocalTimeZone == 'undefined') {
+ if (typeof loginData.useLocalTimeZone == 'undefined')
+ {
loginData.useLocalTimeZone = true;
}
-
- if (typeof loginData.fastLogin == 'undefined') {
+ if (typeof loginData.fastLogin == 'undefined')
+ {
loginData.fastLogin = true;
}
-
- if (typeof loginData.followTimeLine == 'undefined') {
+ if (typeof loginData.followTimeLine == 'undefined')
+ {
loginData.followTimeLine = false;
}
-
- if (typeof loginData.timelineScale == 'undefined') {
+ if (typeof loginData.timelineScale == 'undefined')
+ {
loginData.timelineScale = -1;
}
+ if (typeof loginData.showMontageSubMenu == 'undefined')
+ {
+
+ loginData.showMontageSubMenu = false;
+ }
- if (typeof loginData.monSingleImageQuality == 'undefined') {
+ if (typeof loginData.monSingleImageQuality == 'undefined')
+ {
loginData.monSingleImageQuality = 100;
}
log("DataModel init recovered this loginData as " + JSON.stringify(loginData));
- } else {
+ }
+ else
+ {
log("defaultServer configuration NOT found. Keeping login at defaults");
}
-
-
-
// 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');
@@ -869,67 +958,82 @@ angular.module('zmApp.controllers')
},
- isForceNetworkStop: function() {
+ isForceNetworkStop: function()
+ {
return loginData.forceNetworkStop;
},
- setJustResumed: function(val) {
+ setJustResumed: function(val)
+ {
justResumed = true;
},
- stopNetwork: function(str) {
+ stopNetwork: function(str)
+ {
var s = "";
if (str) s = str + ":";
- if (justResumed) {
+ 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 {
+ }
+ else
+ {
log(s + " Calling window.stop()");
window.stop();
}
},
- isLoggedIn: function() {
+ isLoggedIn: function()
+ {
if ((loginData.username != "" && loginData.password != "" && loginData.url != "" &&
- loginData.apiurl != "") || (loginData.isUseAuth != '1')) {
+ loginData.apiurl != "") || (loginData.isUseAuth != '1'))
+ {
return 1;
- } else {
-
+ }
+ else
+ {
return 0;
}
},
- getLanguages: function() {
+ getLanguages: function()
+ {
return languages;
},
- setDefaultLanguage: function(l, permanent) {
+ setDefaultLanguage: function(l, permanent)
+ {
if (!l) l = 'en';
defaultLang = l;
var d = $q.defer();
- if (permanent) {
+ if (permanent)
+ {
//window.localStorage.setItem("defaultLang", l);
//console.log("setting default lang");
localforage.setItem("defaultLang", l)
- .then(function(val) {
+ .then(function(val)
+ {
log("Set language in localforage to: " + val);
});
}
//console.log("invoking translate use with " + l);
- $translate.use(l).then(function(data) {
+ $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) {
+ }, function(error)
+ {
log("Device Language error: " + error);
$translate.use('en');
moment.locale('en');
@@ -939,65 +1043,77 @@ angular.module('zmApp.controllers')
return d.promise;
},
- getDefaultLanguage: function() {
+ getDefaultLanguage: function()
+ {
return defaultLang;
//return window.localStorage.getItem("defaultLang");
},
- reloadMonitorDisplayStatus: function() {
+ reloadMonitorDisplayStatus: function()
+ {
return reloadMonitorDisplayStatus();
},
- getLogin: function() {
-
+ getLogin: function()
+ {
return angular.copy(loginData);
},
- getServerGroups: function() {
+ getServerGroups: function()
+ {
return angular.copy(serverGroupList);
},
- setServerGroups: function(sg) {
+ setServerGroups: function(sg)
+ {
serverGroupList = angular.copy(sg);
},
- getKeepAwake: function() {
+ getKeepAwake: function()
+ {
return (loginData.keepAwake == '1') ? true : false;
},
- setAppVersion: function(ver) {
+ setAppVersion: function(ver)
+ {
zmAppVersion = ver;
},
- getAppVersion: function() {
+ getAppVersion: function()
+ {
return (zmAppVersion);
},
- setBackground: function(val) {
+ setBackground: function(val)
+ {
isBackground = val;
},
- isBackground: function() {
+ isBackground: function()
+ {
return isBackground;
},
- isFirstUse: function() {
+ isFirstUse: function()
+ {
// console.log("isFirstUse is " + isFirstUse);
return isFirstUse;
// return ((window.localStorage.getItem("isFirstUse") == undefined) ? true : false);
},
- versionCompare: function(l, r) {
+ versionCompare: function(l, r)
+ {
return versionCompare(l, r);
},
//-----------------------------------------------------------------
// Allow the option to reset first use if I need it in future
//-----------------------------------------------------------------
- setFirstUse: function(val) {
+ 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));});
@@ -1007,37 +1123,47 @@ angular.module('zmApp.controllers')
},
- getTimeFormat: function() {
+ getTimeFormat: function()
+ {
return (loginData.use24hr ? "HH:mm" : "hh:mm a");
},
- getTimeFormatSec: function() {
+ getTimeFormatSec: function()
+ {
return (loginData.use24hr ? "HH:mm:ss" : "hh:mm:ss a");
},
//------------------------------------------------------------------
// switches screen to 'always on' or 'auto'
//------------------------------------------------------------------
- setAwake: function(val) {
-
+ setAwake: function(val)
+ {
//console.log ("**** setAwake called with:" + val);
// log("Switching screen always on to " + val);
- if (val) {
+ if (val)
+ {
- if (window.cordova != undefined) {
+ if (window.cordova != undefined)
+ {
window.plugins.insomnia.keepAwake();
- } else {
+ }
+ else
+ {
//console.log ("Skipping insomnia, cordova does not exist");
}
- } else {
- if (window.cordova != undefined) {
+ }
+ else
+ {
+ if (window.cordova != undefined)
+ {
window.plugins.insomnia.allowSleepAgain();
- } else {
+ }
+ else
+ {
//console.log ("Skipping insomnia, cordova does not exist");
}
-
}
},
@@ -1046,7 +1172,8 @@ angular.module('zmApp.controllers')
// writes all params to local storage. FIXME: Move all of this into a JSON
// object
//--------------------------------------------------------------------------
- setLogin: function(newLogin) {
+ setLogin: function(newLogin)
+ {
setLogin(newLogin);
$rootScope.showBlog = newLogin.enableBlog;
@@ -1056,50 +1183,60 @@ angular.module('zmApp.controllers')
//-------------------------------------------------------
// returns API version or none
//-------------------------------------------------------
- getAPIversion: function() {
+ 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) {
+ .then(function(success)
+ {
+ if (success.data.version)
+ {
d.resolve(success.data.version);
- } else {
+ }
+ else
+ {
d.resolve("0.0.0");
}
return (d.promise);
},
- function(error) {
+ function(error)
+ {
debug("getAPIversion error handler " + JSON.stringify(error));
d.reject("-1.-1.-1");
return (d.promise);
});
return (d.promise);
-
},
- displayBanner: function(mytype, mytext, myinterval, mytimer) {
+ displayBanner: function(mytype, mytext, myinterval, mytimer)
+ {
displayBanner(mytype, mytext, myinterval, mytimer);
},
- isReCaptcha: function() {
+ 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) {
+ .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 {
+ }
+ else
+ {
d.resolve(false);
log("ZM has recaptcha disabled - good");
return (d.promise);
@@ -1115,10 +1252,12 @@ angular.module('zmApp.controllers')
// need a mid as restricted users won't be able to get
// auth with just &watch
- getAuthKey: function(mid, ck) {
+ getAuthKey: function(mid, ck)
+ {
var d = $q.defer();
- if (!mid) {
+ if (!mid)
+ {
log("Deferring auth key, as monitorId unknown");
d.resolve("");
return (d.promise);
@@ -1129,20 +1268,27 @@ angular.module('zmApp.controllers')
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) {
+ .then(function(success)
+ {
// console.log ("**** RESULT IS " + JSON.stringify(success));
// Look for auth=
var auth = success.data.match("auth=(.*?)&");
- if (auth && (auth[1] != null)) {
+ if (auth && (auth[1] != null))
+ {
log("DataModel: Extracted a stream authentication key of: " + auth[1]);
d.resolve("&auth=" + auth[1]);
- } else {
+ }
+ else
+ {
log("DataModel: Did not find a stream auth key, looking for user=");
auth = success.data.match("user=(.*?)&");
- if (auth && (auth[1] != null)) {
+ if (auth && (auth[1] != null))
+ {
log("DataModel: Found simple stream auth mode (user=)");
d.resolve("&user=" + loginData.username + "&pass=" + loginData.password);
- } else {
+ }
+ else
+ {
log("Data Model: Did not find any stream mode of auth");
d.resolve("");
}
@@ -1150,7 +1296,8 @@ angular.module('zmApp.controllers')
}
},
- function(error) {
+ function(error)
+ {
log("DataModel: Error resolving auth key " + JSON.stringify(error));
d.resolve("");
return (d.promise);
@@ -1163,23 +1310,27 @@ angular.module('zmApp.controllers')
// This function returns the numdigits for padding capture images
//-----------------------------------------------------------------------------
- getKeyConfigParams: function(forceReload) {
+ getKeyConfigParams: function(forceReload)
+ {
var d = $q.defer();
- if (forceReload == 1 || configParams.ZM_EVENT_IMAGE_DIGITS == '-1') {
+ 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) {
+ .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) {
+ .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?
@@ -1187,7 +1338,9 @@ angular.module('zmApp.controllers')
d.resolve(configParams.ZM_EVENT_IMAGE_DIGITS);
return (d.promise);
});
- } else {
+ }
+ else
+ {
log("ZM_EVENT_IMAGE_DIGITS is already configured for " +
configParams.ZM_EVENT_IMAGE_DIGITS);
d.resolve(configParams.ZM_EVENT_IMAGE_DIGITS);
@@ -1200,34 +1353,36 @@ angular.module('zmApp.controllers')
// 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() {
+ 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) {
+ .success(function(data)
+ {
configParams.ZM_PATH_ZMS = data.config.Value;
d.resolve(configParams.ZM_PATH_ZMS);
return (d.promise);
})
- .error(function(error) {
+ .error(function(error)
+ {
log("Error retrieving ZM_PATH_ZMS: " + JSON.stringify(error));
d.reject("");
return (d.promise);
});
return (d.promise);
-
},
//--------------------------------------------------------------------------
// returns high or low BW mode
//--------------------------------------------------------------------------
- getBandwidth: function() {
+ 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
@@ -1236,13 +1391,24 @@ angular.module('zmApp.controllers')
// 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) {
+ getMonitors: function(forceReload)
+ {
//console.log("** Inside ZMData getMonitors with forceReload=" + forceReload);
-
-
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kLoadingMonitors'),
animation: 'fade-in',
showBackdrop: true,
@@ -1251,9 +1417,6 @@ angular.module('zmApp.controllers')
showDelay: 0
});
-
-
-
var d = $q.defer();
if ((monitorsLoaded == 0) || (forceReload == 1)) // monitors are empty or force reload
{
@@ -1263,10 +1426,12 @@ angular.module('zmApp.controllers')
var myurl = apiurl + "/monitors.json";
//console.log ("API:"+myurl);
$http.get(myurl /*,{timeout:15000}*/ )
- .success(function(data) {
+ .success(function(data)
+ {
//console.log("HTTP success got " + JSON.stringify(data.monitors));
monitors = data.monitors;
- monitors.sort(function(a, b) {
+ monitors.sort(function(a, b)
+ {
return parseInt(a.Monitor.Sequence) - parseInt(b.Monitor.Sequence);
});
//console.log("promise resolved inside HTTP success");
@@ -1274,17 +1439,17 @@ angular.module('zmApp.controllers')
reloadMonitorDisplayStatus();
-
-
debug("Now trying to get multi-server data, if present");
$http.get(apiurl + "/servers.json")
- .success(function(data) {
+ .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++) {
+ for (var i = 0; i < monitors.length; i++)
+ {
// make them all show for now
monitors[i].Monitor.listDisplay = 'show';
@@ -1292,16 +1457,19 @@ angular.module('zmApp.controllers')
monitors[i].Monitor.connKey = (Math.floor((Math.random() * 999999) + 1)).toString();
var serverFound = false;
- for (var j = 0; j < multiservers.length; j++) {
+ 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) {
+ if (multiservers[j].Server.Id == monitors[i].Monitor.ServerId)
+ {
//console.log ("Found match");
serverFound = true;
break;
}
}
- if (serverFound) {
+ if (serverFound)
+ {
debug("Monitor " + monitors[i].Monitor.Id + " has a recording server hostname of " + multiservers[j].Server.Hostname);
@@ -1319,17 +1487,17 @@ angular.module('zmApp.controllers')
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) {
+ if (!s.host)
+ {
s.host = s.path;
s.path = undefined;
}
st += s.host;
-
- if (p.port || s.port) {
+ if (p.port || s.port)
+ {
st += (s.port ? ":" + s.port : ":" + p.port);
}
@@ -1349,8 +1517,9 @@ angular.module('zmApp.controllers')
//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 {
+ }
+ else
+ {
//monitors[i].Monitor.listDisplay = 'show';
monitors[i].Monitor.isAlarmed = false;
monitors[i].Monitor.connKey = (Math.floor((Math.random() * 999999) + 1)).toString();
@@ -1358,9 +1527,9 @@ angular.module('zmApp.controllers')
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) {
+ if (loginData.forceImageModePath)
+ {
debug("Overriding, setting image mode to true as you have requested force enable");
monitors[i].Monitor.imageMode = 'path';
}
@@ -1372,11 +1541,13 @@ angular.module('zmApp.controllers')
reloadMonitorDisplayStatus();
d.resolve(monitors);
})
- .error(function(err) {
+ .error(function(err)
+ {
log("multi server list loading error");
multiservers = [];
- for (var i = 0; i < monitors.length; i++) {
+ 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();
@@ -1385,9 +1556,6 @@ angular.module('zmApp.controllers')
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);
@@ -1396,10 +1564,9 @@ angular.module('zmApp.controllers')
$ionicLoading.hide();
log("Monitor load was successful, loaded " + monitors.length + " monitors");
-
-
})
- .error(function(err) {
+ .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
@@ -1413,7 +1580,8 @@ angular.module('zmApp.controllers')
});
return d.promise;
- } else // monitors are loaded
+ }
+ else // monitors are loaded
{
//console.log("Returning pre-loaded list of " + monitors.length + " monitors");
log("Returning pre-loaded list of " + monitors.length + " monitors");
@@ -1428,48 +1596,61 @@ angular.module('zmApp.controllers')
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
- setMonitors: function(mon) {
+ setMonitors: function(mon)
+ {
//console.log("ZMData setMonitors called with " + mon.length + " monitors");
monitors = mon;
},
- processFastLogin: function() {
+ processFastLogin: function()
+ {
var d = $q.defer();
- if (1) {
+ if (1)
+ {
d.reject("not implemented");
return d.promise;
}
console.log("inside processFastLogin");
- if (!loginData.fastLogin) {
+ 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
+ }
+ else //fastlogin is on
{
localforage.getItem("lastLogin")
- .then(function(succ) {
+ .then(function(succ)
+ {
console.log("fast login DB found");
var dt = moment(succ);
- if (dt.isValid()) {
+ if (dt.isValid())
+ {
debug("Got last login as " + dt.toString());
- if (moment.duration(moment().diff(dt)).asHours() >= 2) {
+ if (moment.duration(moment().diff(dt)).asHours() >= 2)
+ {
d.reject("duration since last login >=2hrs, need to relogin");
return d.promise;
- } else {
+ }
+ else
+ {
d.resolve("fast login is valid, less then 2 hrs");
return d.promise;
}
- } else {
+ }
+ else
+ {
console.log("Invalid date found");
d.reject("last-login invalid");
return d.promise;
}
},
- function(e) {
+ function(e)
+ {
console.log("fastlogin DB not found");
d.reject("last-login not found, fastlogin rejected");
return d.promise;
@@ -1480,10 +1661,13 @@ angular.module('zmApp.controllers')
},
// returns if this mid is hidden or not
- isNotHidden: function(mid) {
+ isNotHidden: function(mid)
+ {
var notHidden = true;
- for (var i = 0; i < monitors.length; i++) {
- if (monitors[i].Monitor.Id == mid) {
+ for (var i = 0; i < monitors.length; i++)
+ {
+ if (monitors[i].Monitor.Id == mid)
+ {
notHidden = (monitors[i].Monitor.listDisplay == 'show') ? true : false;
break;
}
@@ -1493,9 +1677,14 @@ angular.module('zmApp.controllers')
},
+ getLocalTimeZoneNow: function()
+ {
+ return moment.tz.guess();
+ },
//returns TZ value immediately (sync)
- getTimeZoneNow: function() {
+ getTimeZoneNow: function()
+ {
// console.log ("getTimeZoneNow: " + tz ? tz : moment.tz.guess());
return tz ? tz : moment.tz.guess();
},
@@ -1503,28 +1692,36 @@ angular.module('zmApp.controllers')
// returns server timezone, failing which local timezone
// always resolves true
- isTzSupported: function() {
+ isTzSupported: function()
+ {
return isTzSupported;
},
- getTimeZone: function() {
+ getTimeZone: function(isForce)
+ {
var d = $q.defer();
- if (!tz) {
+ if (!tz || isForce)
+ {
log("First invocation of TimeZone, asking server");
var apiurl = loginData.apiurl + '/host/getTimeZone.json';
$http.get(apiurl)
- .then(function(success) {
+ .then(function(success)
+ {
tz = success.data.tz;
d.resolve(tz);
debug("Timezone API response is:" + success.data.tz);
- isTzSupported = true;
+ if (success.data.tz !== undefined)
+ isTzSupported = true;
+ else
+ isTzSupported = false;
$rootScope.$emit('tz-updated');
return (d.promise);
},
- function(error) {
+ function(error)
+ {
tz = moment.tz.guess();
debug("Timezone API error handler, guessing local:" + tz);
d.resolve(tz);
@@ -1532,7 +1729,9 @@ angular.module('zmApp.controllers')
return (d.promise);
});
- } else {
+ }
+ else
+ {
d.resolve(tz);
return d.promise;
}
@@ -1548,7 +1747,8 @@ angular.module('zmApp.controllers')
// All this effort because the ZM APIs return events in sorted order, oldest first. Yeesh.
//-----------------------------------------------------------------------------
- getEventsPages: function(monitorId, startTime, endTime) {
+ getEventsPages: function(monitorId, startTime, endTime)
+ {
//console.log("********** INSIDE EVENTS PAGES ");
var apiurl = loginData.apiurl;
@@ -1562,11 +1762,11 @@ angular.module('zmApp.controllers')
myurl = myurl + "/AlarmFrames >=:" + (loginData.enableAlarmCount ? loginData.minAlarmCount : 0);
-
myurl = myurl + ".json";
//console.log (">>>>>Constructed URL " + myurl);
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kCalcEventSize') + '...',
animation: 'fade-in',
showBackdrop: true,
@@ -1575,18 +1775,19 @@ angular.module('zmApp.controllers')
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) {
+ .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) {
+ .error(function(error)
+ {
$ionicLoading.hide();
// console.log("*** ERROR GETTING TOTAL PAGES ***");
log("Error retrieving page count of events " + JSON.stringify(error), "error");
@@ -1606,17 +1807,21 @@ angular.module('zmApp.controllers')
// monitorId == 0 means all monitors (ZM starts from 1)
//-----------------------------------------------------------------------------
- getEvents: function(monitorId, pageId, loadingStr, startTime, endTime) {
+ getEvents: function(monitorId, pageId, loadingStr, startTime, endTime)
+ {
//console.log("ZMData getEvents called with ID=" + monitorId + "and Page=" + pageId);
- if (!loadingStr) {
- loadingStr = "loading events...";
+ if (!loadingStr)
+ {
+ loadingStr = $translate.instant('kLoadingEvents')+"...";
}
//if (loadingStr) loa
- if (loadingStr != 'none') {
- $ionicLoading.show({
+ if (loadingStr != 'none')
+ {
+ $ionicLoading.show(
+ {
template: loadingStr,
animation: 'fade-in',
showBackdrop: true,
@@ -1641,10 +1846,12 @@ angular.module('zmApp.controllers')
myurl = myurl + "/AlarmFrames >=:" + (loginData.enableAlarmCount ? loginData.minAlarmCount : 0);
myurl = myurl + ".json";
-
- if (pageId) {
+ if (pageId)
+ {
myurl = myurl + "?page=" + pageId;
- } else {
+ }
+ else
+ {
//console.log("**** PAGE WAS " + pageId);
}
@@ -1654,15 +1861,14 @@ angular.module('zmApp.controllers')
//console.log (">>>>>Constructed URL " + myurl);
-
-
-
$http.get(myurl /*,{timeout:15000}*/ )
- .success(function(data) {
+ .success(function(data)
+ {
if (loadingStr != 'none') $ionicLoading.hide();
//myevents = data.events;
myevents = data.events.reverse();
- if (monitorId == 0) {
+ if (monitorId == 0)
+ {
oldevents = myevents;
}
//console.log (JSON.stringify(data));
@@ -1671,7 +1877,8 @@ angular.module('zmApp.controllers')
return d.promise;
})
- .error(function(err) {
+ .error(function(err)
+ {
if (loadingStr != 'none') $ionicLoading.hide();
displayBanner('error', ['error retrieving event list', 'please try again']);
//console.log("HTTP Events error " + err);
@@ -1684,7 +1891,8 @@ angular.module('zmApp.controllers')
d.reject(myevents);
// FIXME: Check what pagination does to this logic
- if (monitorId == 0) {
+ if (monitorId == 0)
+ {
oldevents = [];
}
return d.promise;
@@ -1695,26 +1903,28 @@ angular.module('zmApp.controllers')
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
- getMontageSize: function() {
+ getMontageSize: function()
+ {
return loginData.montageSize;
},
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
- setMontageSize: function(montage) {
+ setMontageSize: function(montage)
+ {
loginData.montageSize = montage;
},
-
-
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
- getMonitorsLoaded: function() {
+ getMonitorsLoaded: function()
+ {
// console.log("**** Inside promise function ");
var deferred = $q.defer();
- if (monitorsLoaded != 0) {
+ if (monitorsLoaded != 0)
+ {
deferred.resolve(monitorsLoaded);
}
@@ -1724,7 +1934,8 @@ angular.module('zmApp.controllers')
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
- setMonitorsLoaded: function(loaded) {
+ setMonitorsLoaded: function(loaded)
+ {
// console.log("ZMData.setMonitorsLoaded=" + loaded);
monitorsLoaded = loaded;
},
@@ -1733,38 +1944,45 @@ angular.module('zmApp.controllers')
// returns the next monitor ID in the list
// used for swipe next
//-----------------------------------------------------------------------------
- getNextMonitor: function(monitorId, direction) {
+ 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) {
+ for (var i = 0; i < monitors.length; i++)
+ {
+ if (parseInt(monitors[i].Monitor.Id) == id)
+ {
foundIndex = i;
break;
}
}
- if (foundIndex != -1) {
+ 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 {
+ }
+ 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) {
+ getMonitorName: function(id)
+ {
var idnum = parseInt(id);
- for (var i = 0; i < monitors.length; i++) {
- if (parseInt(monitors[i].Monitor.Id) == idnum) {
+ 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;
}
@@ -1773,10 +1991,13 @@ angular.module('zmApp.controllers')
return "(Unknown)";
},
- getMonitorObject: function(id) {
+ getMonitorObject: function(id)
+ {
var idnum = parseInt(id);
- for (var i = 0; i < monitors.length; i++) {
- if (parseInt(monitors[i].Monitor.Id) == idnum) {
+ for (var i = 0; i < monitors.length; i++)
+ {
+ if (parseInt(monitors[i].Monitor.Id) == idnum)
+ {
// console.log ("Matched, exiting getMonitorname");
return monitors[i];
}
@@ -1785,10 +2006,13 @@ angular.module('zmApp.controllers')
return "(Unknown)";
},
- getImageMode: function(id) {
+ getImageMode: function(id)
+ {
var idnum = parseInt(id);
- for (var i = 0; i < monitors.length; i++) {
- if (parseInt(monitors[i].Monitor.Id) == idnum) {
+ 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;
}
@@ -1797,10 +2021,13 @@ angular.module('zmApp.controllers')
return "(Unknown)";
},
- getStreamingURL: function(id) {
+ getStreamingURL: function(id)
+ {
var idnum = parseInt(id);
- for (var i = 0; i < monitors.length; i++) {
- if (parseInt(monitors[i].Monitor.Id) == idnum) {
+ 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;
}
@@ -1809,10 +2036,13 @@ angular.module('zmApp.controllers')
return "(Unknown)";
},
- getBaseURL: function(id) {
+ getBaseURL: function(id)
+ {
var idnum = parseInt(id);
- for (var i = 0; i < monitors.length; i++) {
- if (parseInt(monitors[i].Monitor.Id) == idnum) {
+ 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;
}
@@ -1821,7 +2051,6 @@ angular.module('zmApp.controllers')
return "(Unknown)";
},
-
};
}
]);
diff --git a/www/js/DevOptionsCtrl.js b/www/js/DevOptionsCtrl.js
index 984b59cc..970c690d 100644
--- a/www/js/DevOptionsCtrl.js
+++ b/www/js/DevOptionsCtrl.js
@@ -2,10 +2,11 @@
/* jslint browser: true*/
/* global cordova,StatusBar,angular,console */
-angular.module('zmApp.controllers').controller('zmApp.DevOptionsCtrl', ['$scope', '$rootScope', '$ionicModal', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$ionicPopup', '$http', '$q', '$ionicLoading', '$ionicHistory', '$state', 'SecuredPopups', '$translate', function ($scope, $rootScope, $ionicModal, zm, NVRDataModel, $ionicSideMenuDelegate, $ionicPopup, $http, $q, $ionicLoading, $ionicHistory, $state, SecuredPopups, $translate) {
+angular.module('zmApp.controllers').controller('zmApp.DevOptionsCtrl', ['$scope', '$rootScope', '$ionicModal', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$ionicPopup', '$http', '$q', '$ionicLoading', '$ionicHistory', '$state', 'SecuredPopups', '$translate', function($scope, $rootScope, $ionicModal, zm, NVRDataModel, $ionicSideMenuDelegate, $ionicPopup, $http, $q, $ionicLoading, $ionicHistory, $state, SecuredPopups, $translate)
+{
-
- $scope.openMenu = function () {
+ $scope.openMenu = function()
+ {
$ionicSideMenuDelegate.toggleLeft();
// $scope.this.will.crash = 1;
@@ -14,32 +15,36 @@ angular.module('zmApp.controllers').controller('zmApp.DevOptionsCtrl', ['$scope'
//----------------------------------------------------------------
// Alarm notification handling
//----------------------------------------------------------------
- $scope.handleAlarms = function () {
+ $scope.handleAlarms = function()
+ {
$rootScope.isAlarm = !$rootScope.isAlarm;
- if (!$rootScope.isAlarm) {
+ if (!$rootScope.isAlarm)
+ {
$rootScope.alarmCount = "0";
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
- $state.go("events", {
+ $state.go("events",
+ {
"id": 0,
- "playEvent":false
- }, {
+ "playEvent": false
+ },
+ {
reload: true
});
return;
}
};
-
//----------------------------------------------------------------
// Save anyway when you exit
//----------------------------------------------------------------
- $scope.$on('$ionicView.beforeLeave', function () {
+ $scope.$on('$ionicView.beforeLeave', function()
+ {
saveDevOptions();
-
});
//-------------------------------------------------------------------------
@@ -49,18 +54,19 @@ angular.module('zmApp.controllers').controller('zmApp.DevOptionsCtrl', ['$scope'
// reset power state on exit as if it is called after we enter another
// state, that effectively overwrites current view power management needs
//------------------------------------------------------------------------
- $scope.$on('$ionicView.enter', function () {
+ $scope.$on('$ionicView.enter', function()
+ {
//console.log("**VIEW ** DevOptions Ctrl Entered");
$scope.loginData = NVRDataModel.getLogin();
NVRDataModel.setAwake(false);
});
-
+
$scope.isTzSupported = function()
{
return NVRDataModel.isTzSupported();
};
-
+
$scope.getTimeZoneNow = function()
{
return NVRDataModel.getTimeZoneNow();
@@ -70,53 +76,58 @@ angular.module('zmApp.controllers').controller('zmApp.DevOptionsCtrl', ['$scope'
// Perform the login action when the user submits the login form
//------------------------------------------------------------------
- function saveDevOptions() {
+ function saveDevOptions()
+ {
NVRDataModel.debug("SaveDevOptions: called");
-
if (parseInt($scope.loginData.cycleMonitorsInterval) < zm.minCycleTime)
{
$scope.loginData.cycleMonitorsInterval = zm.minCycleTime.toString();
}
- if ((parseInt($scope.loginData.maxFPS) < 0) || (parseInt($scope.loginData.maxFPS) > zm.maxFPS)) {
+ if ((parseInt($scope.loginData.maxFPS) < 0) || (parseInt($scope.loginData.maxFPS) > zm.maxFPS))
+ {
$scope.loginData.maxFPS = zm.defaultFPS.toString();
}
- if (parseInt($scope.loginData.refreshSec) <= 0) {
+ if (parseInt($scope.loginData.refreshSec) <= 0)
+ {
NVRDataModel.debug("SaveDevOptions: refresh sec was too low at " +
$scope.loginData.refreshSec + " reset to 1");
$scope.loginData.refreshSec = 1;
}
-
if ((parseInt($scope.loginData.montageQuality) < zm.safeMontageLimit) ||
- (parseInt($scope.loginData.montageQuality) > 100)) {
+ (parseInt($scope.loginData.montageQuality) > 100))
+ {
$scope.loginData.montageQuality = 100;
}
-
if ((parseInt($scope.loginData.singleImageQuality) < zm.safeImageQuality) ||
- (parseInt($scope.loginData.singleImageQuality) > 100)) {
+ (parseInt($scope.loginData.singleImageQuality) > 100))
+ {
$scope.loginData.singleImageQuality = zm.safeImageQuality.toString();
}
-
NVRDataModel.debug("SaveDevOptions: Saving to disk");
NVRDataModel.setLogin($scope.loginData);
NVRDataModel.getMonitors(1);
-
}
- $scope.saveDevOptions = function () {
+ $scope.saveDevOptions = function()
+ {
saveDevOptions();
// $rootScope.zmPopup.close();
- $rootScope.zmPopup = SecuredPopups.show('alert', {
+ $rootScope.zmPopup = SecuredPopups.show('alert',
+ {
title: $translate.instant('kSettingsSaved'),
- template: "{{'kExploreEnjoy' | translate }} {{$root.appName}}"
- }).then(function (res) {
+ template: "{{'kExploreEnjoy' | translate }} {{$root.appName}}",
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
+ }).then(function(res)
+ {
$ionicSideMenuDelegate.toggleLeft();
});
@@ -125,9 +136,4 @@ angular.module('zmApp.controllers').controller('zmApp.DevOptionsCtrl', ['$scope'
// controller main
//------------------------------------------------------------------
-
-
-
-
-
-}]); \ No newline at end of file
+}]);
diff --git a/www/js/EventCtrl.js b/www/js/EventCtrl.js
index c076ea4d..32f8b119 100644
--- a/www/js/EventCtrl.js
+++ b/www/js/EventCtrl.js
@@ -1,6 +1,7 @@
/* jshint -W041 */
+/*jshint bitwise: false*/
/* jslint browser: true*/
-/* global saveAs, cordova,StatusBar,angular,console,moment, MobileAccessibility */
+/* global saveAs, cordova,StatusBar,angular,console,moment, MobileAccessibility, gifshot, ReadableStream , LibraryHelper, GifWriter, NeuQuant, LocalFileSystem, FileError*/
// This is the controller for Event view. StateParams is if I recall the monitor ID.
// This was before I got access to the new APIs. FIXME: Revisit this code to see what I am doing with it
@@ -9,21 +10,24 @@
angular.module('zmApp.controllers')
// alarm frames filter
-.filter('selectFrames', function ($filter, $translate) {
+.filter('selectFrames', function($filter, $translate)
+{
// Create the return function and set the required parameter name to **input**
- return function (input, typeOfFrames) {
-
+ return function(input, typeOfFrames)
+ {
var out = [];
- angular.forEach(input, function (item) {
-
+ angular.forEach(input, function(item)
+ {
- if (typeOfFrames == $translate.instant('kShowTimeDiffFrames')) {
+ if (typeOfFrames == $translate.instant('kShowTimeDiffFrames'))
+ {
if (item.type == $translate.instant('kShowTimeDiffFrames'))
out.push(item);
- } else
+ }
+ else
out.push(item);
});
@@ -33,7 +37,8 @@ angular.module('zmApp.controllers')
})
-.controller('zmApp.EventCtrl', ['$scope', '$rootScope', 'zm', 'NVRDataModel', 'message', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$ionicPlatform', '$ionicSlideBoxDelegate', '$ionicPosition', '$ionicPopover', '$ionicPopup', 'EventServer', '$sce', '$cordovaBadge', '$cordovaLocalNotification', '$q', 'carouselUtils', '$translate', function ($scope, $rootScope, zm, NVRDataModel, message, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $stateParams, $ionicHistory, $ionicScrollDelegate, $ionicPlatform, $ionicSlideBoxDelegate, $ionicPosition, $ionicPopover, $ionicPopup, EventServer, $sce, $cordovaBadge, $cordovaLocalNotification, $q, carouselUtils, $translate) {
+.controller('zmApp.EventCtrl', ['$scope', '$rootScope', 'zm', 'NVRDataModel', 'message', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$ionicPlatform', '$ionicSlideBoxDelegate', '$ionicPosition', '$ionicPopover', '$ionicPopup', 'EventServer', '$sce', '$cordovaBadge', '$cordovaLocalNotification', '$q', 'carouselUtils', '$translate', '$cordovaFileTransfer', '$cordovaFile', function($scope, $rootScope, zm, NVRDataModel, message, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $stateParams, $ionicHistory, $ionicScrollDelegate, $ionicPlatform, $ionicSlideBoxDelegate, $ionicPosition, $ionicPopover, $ionicPopup, EventServer, $sce, $cordovaBadge, $cordovaLocalNotification, $q, carouselUtils, $translate, $cordovaFileTransfer, $cordovaFile)
+{
// events in last 5 minutes
// TODO https://server/zm/api/events/consoleEvents/5%20minute.json
@@ -56,32 +61,34 @@ angular.module('zmApp.controllers')
var mycarouselWatcher;
var nolangFrom;
var nolangTo;
-
+
$scope.typeOfFrames = $translate.instant('kShowTimeDiffFrames');
var eventsListScrubHeight = eventsListScrubHeight;
var eventsListDetailsHeight = eventsListDetailsHeight;
-
//---------------------------------------------------
// initial code
//---------------------------------------------------
//we come here is TZ is updated after the view loads
- $rootScope.$on('tz-updated', function() {
+ $rootScope.$on('tz-updated', function()
+ {
$scope.tzAbbr = NVRDataModel.getTimeZoneNow();
- NVRDataModel.debug ("Timezone API updated timezone to " + NVRDataModel.getTimeZoneNow());
+ NVRDataModel.debug("Timezone API updated timezone to " + NVRDataModel.getTimeZoneNow());
});
-
- $rootScope.$on("language-changed", function () {
+
+ $rootScope.$on("language-changed", function()
+ {
NVRDataModel.log(">>>>>>>>>>>>>>> language changed");
doRefresh();
});
- $scope.$on('$ionicView.afterEnter', function () {
+ $scope.$on('$ionicView.afterEnter', function()
+ {
//console.log ("********* AFTER ENTER");
-
+
// see if we come from monitors, if so, don't filter events
- if ($ionicHistory.backTitle() =='Monitors')
+ if ($ionicHistory.backTitle() == 'Monitors')
{
showHiddenMonitors = true;
}
@@ -89,9 +96,7 @@ angular.module('zmApp.controllers')
{
showHiddenMonitors = false;
}
- // console.log (">>>>>>>>>>>>>>>>>SHOWHIDDEN IS " + showHiddenMonitors);
-
- // lets get the abbreviated version of TZ to display
+
if (NVRDataModel.getLogin().useLocalTimeZone)
{
$scope.tzAbbr = moment().tz(moment.tz.guess()).zoneAbbr();
@@ -100,41 +105,40 @@ angular.module('zmApp.controllers')
{
$scope.tzAbbr = moment().tz(NVRDataModel.getTimeZoneNow()).zoneAbbr();
}
-
+
$scope.events = [];
getInitialEvents();
setupWatchers();
footerExpand();
});
-
-
- $scope.$on('$ionicView.beforeEnter', function () {
+ $scope.$on('$ionicView.beforeEnter', function()
+ {
-
//console.log ("********* BEFORE ENTER");
+ //
+ $scope.gifshotSupported = true;
document.addEventListener("pause", onPause, false);
//console.log("I got STATE PARAM " + $stateParams.id);
$scope.id = parseInt($stateParams.id, 10);
$scope.showEvent = $stateParams.playEvent || false;
-
- console.log (">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
-
-
- NVRDataModel.log ("EventCtrl called with: EID=" + $scope.id + " playEvent = "+$scope.showEvent);
-
+
+ console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
+
+ NVRDataModel.log("EventCtrl called with: EID=" + $scope.id + " playEvent = " + $scope.showEvent);
+
// This is the only view that hardcodes row size due to
// collection repeat, so lets re-get the text size if it has changed
// note that there may be a delay as its a callback - so might involve
// a UI jiggle
-
+
if (window.cordova)
MobileAccessibility.getTextZoom(getTextZoomCallback);
-
+
eventsListDetailsHeight = parseInt(zm.eventsListDetailsHeight * $rootScope.textScaleFactor);
eventsListScrubHeight = parseInt(zm.eventsListScrubHeight * $rootScope.textScaleFactor);
-
- NVRDataModel.debug (">>>height of list/scrub set to " + eventsListDetailsHeight + " and " + eventsListScrubHeight);
+
+ NVRDataModel.debug(">>>height of list/scrub set to " + eventsListDetailsHeight + " and " + eventsListScrubHeight);
pageLoaded = false;
enableLoadMore = true;
@@ -153,7 +157,6 @@ angular.module('zmApp.controllers')
$scope.weeks = [];
$scope.months = [];
-
$scope.eventList = {
showDelete: false
};
@@ -167,13 +170,13 @@ angular.module('zmApp.controllers')
$scope.FrameArray = []; // will hold frame info from detailed Events API
loginData = NVRDataModel.getLogin();
NVRDataModel.getKeyConfigParams(0)
- .then(function (data) {
+ .then(function(data)
+ {
//console.log ("***GETKEY: " + JSON.stringify(data));
eventImageDigits = parseInt(data);
NVRDataModel.log("Image padding digits reported as " + eventImageDigits);
});
-
$scope.showSearch = false;
eventsPage = 1;
moreEvents = true;
@@ -191,64 +194,48 @@ angular.module('zmApp.controllers')
});
-
-
function getEventObject(eid)
{
-
- var apiurl = NVRDataModel.getLogin().apiurl + '/events/'+eid+'.json';
-
- $http.get (apiurl)
- .success (function (data) {
- })
- .error (function (err) {
- });
- /*
- myevents[i].Event.humanizeTime = humanizeTime(myevents[i].Event.StartTime);
- myevents[i].Event.MonitorName = NVRDataModel.getMonitorName(myevents[i].Event.MonitorId);
- // now construct base path
-
- myevents[i].Event.streamingURL = NVRDataModel.getStreamingURL(myevents[i].Event.MonitorId);
- myevents[i].Event.baseURL = NVRDataModel.getBaseURL(myevents[i].Event.MonitorId);
- myevents[i].Event.imageMode = NVRDataModel.getImageMode(myevents[i].Event.MonitorId);
- // console.log ("***** MULTISERVER STREAMING URL FOR EVENTS " + myevents[i].Event.streamingURL);
-
- // console.log ("***** MULTISERVER BASE URL FOR EVENTS " + myevents[i].Event.baseURL);
-
- myevents[i].Event.ShowScrub = false;
- myevents[i].Event.BasePath = computeBasePath(myevents[i]);
- myevents[i].Event.relativePath = computeRelativePath(myevents[i]);
- */
+
+ var apiurl = NVRDataModel.getLogin().apiurl + '/events/' + eid + '.json';
+
+ $http.get(apiurl)
+ .success(function(data) {})
+ .error(function(err) {});
+
}
-
function getTextZoomCallback(tz)
{
- $rootScope.textScaleFactor = parseFloat(tz+"%") / 100.0;
- NVRDataModel.debug ("text zoom factor is " + $rootScope.textScaleFactor);
+ $rootScope.textScaleFactor = parseFloat(tz + "%") / 100.0;
+ NVRDataModel.debug("text zoom factor is " + $rootScope.textScaleFactor);
}
-
-
-
// --------------------------------------------------------
// Handling of back button in case modal is open should
// close the modal
// --------------------------------------------------------
- $ionicPlatform.registerBackButtonAction(function (e) {
+ $ionicPlatform.registerBackButtonAction(function(e)
+ {
e.preventDefault();
- if ($scope.modal != undefined && $scope.modal.isShown()) {
+ if ($scope.modal != undefined && $scope.modal.isShown())
+ {
// switch off awake, as liveview is finished
NVRDataModel.debug("Modal is open, closing it");
NVRDataModel.setAwake(false);
$scope.modal.remove();
- } else {
+ }
+ else
+ {
NVRDataModel.debug("Modal is closed, so toggling or exiting");
- if (!$ionicSideMenuDelegate.isOpenLeft()) {
+ if (!$ionicSideMenuDelegate.isOpenLeft())
+ {
$ionicSideMenuDelegate.toggleLeft();
- } else {
+ }
+ else
+ {
navigator.app.exitApp();
}
@@ -256,30 +243,30 @@ angular.module('zmApp.controllers')
}, 1000);
-
//--------------------------------------
// monitor the slider for carousels
//--------------------------------------
- function setupWatchers() {
+ function setupWatchers()
+ {
NVRDataModel.debug("Setting up carousel watchers");
- ionRangeWatcher = $scope.$watch('ionRange.index', function () {
+ ionRangeWatcher = $scope.$watch('ionRange.index', function()
+ {
// console.log ("Watching index");
$scope.mycarousel.index = parseInt($scope.ionRange.index) - 1;
if (carouselUtils.getStop() == true)
return;
-
//console.log ("***ION RANGE CHANGED TO " + $scope.mycarousel.index);
});
+ mycarouselWatcher = $scope.$watch('mycarousel.index', function()
+ {
-
- mycarouselWatcher = $scope.$watch('mycarousel.index', function () {
-
-
- if ($scope.event && $scope.ionRange.index == parseInt($scope.event.Event.Frames) - 1) {
- if (!$scope.modal || $scope.modal.isShown() == false) {
+ if ($scope.event && $scope.ionRange.index == parseInt($scope.event.Event.Frames) - 1)
+ {
+ if (!$scope.modal || $scope.modal.isShown() == false)
+ {
// console.log("quick scrub playback over");
carouselUtils.setStop(true);
$scope.ionRange.index = 0;
@@ -292,10 +279,8 @@ angular.module('zmApp.controllers')
$scope.ionRange.index = ($scope.mycarousel.index + 1).toString();
// console.log ("***IONRANGE RANGE CHANGED TO " + $scope.ionRange.index);
-
});
-
}
// --------------------------------------------------------
@@ -303,30 +288,34 @@ angular.module('zmApp.controllers')
// close the modal
// --------------------------------------------------------
- function getInitialEvents() {
+ function getInitialEvents()
+ {
NVRDataModel.debug("getInitialEvents called");
var lData = NVRDataModel.getLogin();
-
-
// If you came from Monitors, disregard hidden monitors in montage
- /* if (lData.persistMontageOrder && stackState != "Monitors") {
- var tempMon = message;
- $scope.monitors = NVRDataModel.applyMontageMonitorPrefs(tempMon, 2)[0];
- } else*/
- $scope.monitors = message;
-
+ /* if (lData.persistMontageOrder && stackState != "Monitors") {
+ var tempMon = message;
+ $scope.monitors = NVRDataModel.applyMontageMonitorPrefs(tempMon, 2)[0];
+ } else*/
+ $scope.monitors = message;
- if ($scope.monitors.length == 0) {
+ if ($scope.monitors.length == 0)
+ {
var pTitle = $translate.instant('kNoMonitors');
- $ionicPopup.alert({
+ $ionicPopup.alert(
+ {
title: pTitle,
- template: "{{'kCheckCredentials' | translate }}"
+ template: "{{'kCheckCredentials' | translate }}",
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
});
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
- $state.go("login", {
+ $state.go("login",
+ {
"wizard": false
});
return;
@@ -345,7 +334,8 @@ angular.module('zmApp.controllers')
if ($rootScope.toString)
nolangTo = moment($rootScope.toString).locale('en').format("YYYY-MM-DD HH:mm:ss");
NVRDataModel.getEventsPages($scope.id, nolangFrom, nolangTo)
- .then(function (data) {
+ .then(function(data)
+ {
eventsPage = data.pageCount || 1;
NVRDataModel.debug("EventCtrl: found " + eventsPage + " pages of events");
@@ -361,18 +351,23 @@ angular.module('zmApp.controllers')
nolangTo = moment($rootScope.toString).locale('en').format("YYYY-MM-DD HH:mm:ss");
NVRDataModel.getEvents($scope.id, eventsPage, "", nolangFrom, nolangTo)
- .then(function (data) {
+ .then(function(data)
+ {
var myevents = data;
NVRDataModel.debug("EventCtrl: success, got " + myevents.length + " events");
var loginData = NVRDataModel.getLogin();
- for (var i = 0; i < myevents.length; i++) {
+ for (var i = 0; i < myevents.length; i++)
+ {
var idfound = true;
- if (loginData.persistMontageOrder) {
+ if (loginData.persistMontageOrder)
+ {
idfound = false;
- for (var ii = 0; ii < $scope.monitors.length; ii++) {
- if ($scope.monitors[ii].Monitor.Id == myevents[i].Event.MonitorId && (NVRDataModel.isNotHidden(myevents[i].Event.MonitorId) || showHiddenMonitors)) {
+ for (var ii = 0; ii < $scope.monitors.length; ii++)
+ {
+ if ($scope.monitors[ii].Monitor.Id == myevents[i].Event.MonitorId && (NVRDataModel.isNotHidden(myevents[i].Event.MonitorId) || showHiddenMonitors))
+ {
idfound = true;
break;
@@ -380,7 +375,6 @@ angular.module('zmApp.controllers')
}
}
-
myevents[i].Event.humanizeTime = humanizeTime(myevents[i].Event.StartTime);
myevents[i].Event.streamingURL = NVRDataModel.getStreamingURL(myevents[i].Event.MonitorId);
myevents[i].Event.baseURL = NVRDataModel.getBaseURL(myevents[i].Event.MonitorId);
@@ -397,26 +391,34 @@ angular.module('zmApp.controllers')
myevents[i].Event.BasePath = computeBasePath(myevents[i]);
myevents[i].Event.relativePath = computeRelativePath(myevents[i]);
-
// in multiserver BasePath is login url for frames
// http://login.url/index.php?view=frame&eid=19696772&fid=21
// console.log ("COMPARING "+NVRDataModel.getLogin().url+ " TO " +myevents[i].Event.baseURL);
- if (NVRDataModel.getLogin().url != myevents[i].Event.baseURL) {
+ if (NVRDataModel.getLogin().url != myevents[i].Event.baseURL)
+ {
//NVRDataModel.debug ("Multi server, changing base");
myevents[i].Event.baseURL = NVRDataModel.getLogin().url;
}
- if (idfound) {
+ if (myevents[i].Event.imageMode == 'path')
+ //if (1)
+ myevents[i].Event.videoPath = myevents[i].Event.baseURL + "/events/" + myevents[i].Event.relativePath + myevents[i].Event.DefaultVideo;
+ else
+ myevents[i].Event.videoPath = myevents[i].Event.baseURL + "/index.php?view=view_video&eid=" + myevents[i].Event.Id;
+
+ if (idfound)
+ {
$scope.events.push(myevents[i]);
- } else {
+ }
+ else
+ {
//console.log ("Skipping Event MID = " + myevents[i].Event.MonitorId);
}
} //for
-
//$scope.events = myevents;
// we only need to stop the template from loading when the list is empty
// so this can be false once we have _some_ content
@@ -425,7 +427,8 @@ angular.module('zmApp.controllers')
// to avoid only few events being displayed
// if last page has less events
//console.log("**Loading Next Page ***");
- if (myevents.length < 50) {
+ if (myevents.length < 50)
+ {
NVRDataModel.debug("EventCtrl:loading one more page just in case we don't have enough to display");
loadMore();
}
@@ -434,14 +437,14 @@ angular.module('zmApp.controllers')
});
}
-
-
//-------------------------------------------------------
// Tapping on a frame shows this image
//------------------------------------------------------
- function SaveSuccess() {
- $ionicLoading.show({
+ function SaveSuccess()
+ {
+ $ionicLoading.show(
+ {
template: $translate.instant('kDone'),
noBackdrop: true,
duration: 1000
@@ -449,8 +452,10 @@ angular.module('zmApp.controllers')
NVRDataModel.debug("ModalCtrl:Photo saved successfuly");
}
- function SaveError(e) {
- $ionicLoading.show({
+ function SaveError(e)
+ {
+ $ionicLoading.show(
+ {
template: $translate.instant('kErrorSave'),
noBackdrop: true,
duration: 2000
@@ -459,10 +464,11 @@ angular.module('zmApp.controllers')
//console.log("***ERROR");
}
+ function saveNow(imgsrc, r, f)
+ {
- function saveNow(imgsrc, r, f) {
-
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kSavingSnapshot') + "...",
noBackdrop: true,
duration: zm.httpTimeout
@@ -471,7 +477,8 @@ angular.module('zmApp.controllers')
NVRDataModel.log("saveNow: File path to grab is " + url);
var img = new Image();
- img.onload = function () {
+ img.onload = function()
+ {
// console.log("********* ONLOAD");
var canvas = document.createElement('canvas');
canvas.width = img.width;
@@ -482,8 +489,10 @@ angular.module('zmApp.controllers')
var imageDataUrl = canvas.toDataURL('image/jpeg', 1.0);
var imageData = imageDataUrl.replace(/data:image\/jpeg;base64,/, '');
- if ($rootScope.platformOS != "desktop") {
- try {
+ if ($rootScope.platformOS != "desktop")
+ {
+ try
+ {
cordova.exec(
SaveSuccess,
@@ -492,37 +501,279 @@ angular.module('zmApp.controllers')
'saveImageDataToLibrary', [imageData]
);
// carouselUtils.setStop(curState);
- } catch (e) {
+ }
+ catch (e)
+ {
SaveError(e.message);
// carouselUtils.setStop(curState);
}
- } else {
-
+ }
+ else
+ {
var fname = r + f + ".png";
fname = fname.replace(/\//, "-");
fname = fname.replace(/\.jpg/, '');
- canvas.toBlob(function (blob) {
+ canvas.toBlob(function(blob)
+ {
saveAs(blob, fname);
SaveSuccess();
});
}
};
- try {
+ try
+ {
img.src = url;
// console.log ("SAVING IMAGE SOURCE");
- } catch (e) {
+ }
+ catch (e)
+ {
SaveError(e.message);
}
}
- $scope.showImage = function (p, r, f, fid, e, imode, id, parray, ndx) {
- var img;
+
+ function writeFile2( path, file, blob, isAppend)
+ {
+ var csize = 4 * 1024 * 1024; // 4MB
+ var d = $q.defer();
+ NVRDataModel.debug ("Inside writeFile2 with blob size="+blob.size);
+
+ // nothing more to write, so all good?
+ if (!blob.size)
+ {
+ NVRDataModel.debug ("writeFile2 all done");
+ d.resolve(true);
+ return $q.resolve(true);
+ }
+
+
+ if (!isAppend)
+ {
+ // return the delegated promise, even if it fails
+ return $cordovaFile.writeFile(path, file, blob.slice(0,csize), true)
+ .then (function (succ) {
+ return writeFile2(path,file,blob.slice(csize),true);
+ });
+ }
+ else
+ {
+ // return the delegated promise, even if it fails
+ return $cordovaFile.writeExistingFile(path, file, blob.slice(0,csize))
+ .then (function (succ) {
+ return writeFile2(path,file,blob.slice(csize),true);
+ });
+ }
+
+
+ }
+
+ function writeFile(path, __filename, __data){
+ var d = $q.defer();
+ console.log ("inside write file");
+ window.requestFileSystem(LocalFileSystem.TEMPORARY, __data.size+5000, onFileSystemSuccess, fail);
+
+ function fail(e)
+ {
+ var msg = '';
+
+ switch (e.code) {
+ case FileError.QUOTA_EXCEEDED_ERR:
+ msg = 'QUOTA_EXCEEDED_ERR';
+ break;
+ case FileError.NOT_FOUND_ERR:
+ msg = 'NOT_FOUND_ERR';
+ break;
+ case FileError.SECURITY_ERR:
+ msg = 'SECURITY_ERR';
+ break;
+ case FileError.INVALID_MODIFICATION_ERR:
+ msg = 'INVALID_MODIFICATION_ERR';
+ break;
+ case FileError.INVALID_STATE_ERR:
+ msg = 'INVALID_STATE_ERR';
+ break;
+ default:
+ msg = 'Unknown Error';
+ break;
+ }
+
+ console.log('Error: ' + msg);
+ }
+ function onFileSystemSuccess()
+ {
+ console.log ("Got temporary FS");
+ window.resolveLocalFileSystemURL(path, function(dir){
+ dir.getFile(__filename, {create:true}, function(file){
+ file.createWriter(function(fileWriter){
+ //var blob = new Blob([__data], {type:'text/plain'});
+ console.log ("about to write "+__data.size+" bytes");
+ //var blob = new Blob([__data], {type:'text/plain'});
+ fileWriter.write(__data);
+ fileWriter.onwrite = function(e) {
+ NVRDataModel.debug ("write complete");
+ d.resolve();
+ return d.promise;
+ };
+
+ fileWriter.onerror = function(e) {
+ NVRDataModel.debug ("write error in filewriter:"+JSON.stringify(e));
+ d.reject();
+ return d.promise;
+ };
+
+ });
+ });
+
+ },
+ function (err) {
+ d.reject(err);
+ return d.promise;
+ });
+ }
+ return d.promise;
+ }
+
+
+ function moveImageToGallery(fname)
+ {
+ // this is https://github.com/terikon/cordova-plugin-photo-library
+
+ NVRDataModel.debug("moveImageToGallery called with " + fname);
+ cordova.plugins.photoLibrary.saveImage(fname, "zmNinja",onSuccess, onError);
+ //LibraryHelper.saveImageToLibrary(onSuccess, onError, fname, "zmNinja");
+
+ function onSuccess(results)
+ {
+
+ NVRDataModel.debug("Removing temp file");
+
+ if ($rootScope.platformOS == 'ios') {
+ $cordovaFile.removeFile(cordova.file.documentsDirectory, "temp-file.gif");
+ }
+ else
+ $cordovaFile.removeFile(cordova.file.dataDirectory, "temp-file.gif");
+ $ionicLoading.show(
+ {
+ template: $translate.instant('kDone'),
+ noBackdrop: true,
+ duration: 2000
+ });
+
+
+ }
+
+ function onError(error)
+ {
+ console.log("Error: " + error);
+
+ }
+ }
+
+ $scope.downloadFileToDevice = function(path, eid)
+ {
+
+ NVRDataModel.setAwake(true);
+ var tp;
+ if ($rootScope.platformOS == 'ios')
+ tp = cordova.file.documentsDirectory + "temp-video.mp4";
+ else
+ tp = cordova.file.dataDirectory + "temp-video.mp4";
+
+ var th = true;
+ var opt = {};
+ //path = "http://techslides.com/demos/sample-videos/small.mp4";
+
+ NVRDataModel.debug("Saving temporary video to: " + tp);
+ $cordovaFileTransfer.download(path, tp, opt, th)
+ .then(function(result)
+ {
+ NVRDataModel.debug("Moving to gallery...");
+ var ntp;
+ ntp = tp.indexOf('file://') === 0 ? tp.slice(7) : tp;
+
+ $timeout(function()
+ {
+ $ionicLoading.hide();
+ });
+ moveToGallery(ntp, eid + "-video");
+ NVRDataModel.setAwake(false);
+ // Success!
+ }, function(err)
+ {
+ NVRDataModel.setAwake(false);
+ NVRDataModel.log("Error=" + JSON.stringify(err));
+
+ $timeout(function()
+ {
+ $ionicLoading.show(
+ {
+
+ template: $translate.instant('kError'),
+ noBackdrop: true,
+ duration: 3000
+ });
+ });
+ // Error
+ }, function(progress)
+ {
+ var p = Math.round((progress.loaded / progress.total) * 100);
+
+ $ionicLoading.show(
+ {
+
+ template: $translate.instant('kPleaseWait') + "...(" + p + "%)",
+ noBackdrop: true
+ });
+
+ });
+
+ function moveToGallery(path, fname)
+ {
+
+ NVRDataModel.debug("moveToGallery called with " + path);
+ LibraryHelper.saveVideoToLibrary(onSuccess, onError, path, fname);
+
+ function onSuccess(results)
+ {
+ NVRDataModel.debug("Removing temp file");
+
+ if ($rootScope.platformOS == 'ios')
+ $cordovaFile.removeFile(cordova.file.documentsDirectory, "temp-video.mp4");
+ else
+ $cordovaFile.removeFile(cordova.file.dataDirectory, "temp-video.mp4");
+
+ }
+
+ function onError(error)
+ {
+ console.log("Error: " + error);
+
+ }
+ }
+
+ };
+
+ $scope.mp4warning = function()
+ {
+ $ionicPopup.alert(
+ {
+ title: $translate.instant('kNote'),
+ template: "{{'kVideoMp4Warning' | translate }}",
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
+ });
+ };
+
+ $scope.showImage = function(p, r, f, fid, e, imode, id, parray, ndx)
+ {
+ var img;
+
//console.log ("HERE");
$scope.kFrame = $translate.instant('kFrame');
$scope.kEvent = $translate.instant('kEvent');
@@ -534,42 +785,41 @@ angular.module('zmApp.controllers')
// at unique frames;
// NVRDataModel.debug("Hello");
- if ($scope.typeOfFrames == $translate.instant('kShowTimeDiffFrames')) {
+ if ($scope.typeOfFrames == $translate.instant('kShowTimeDiffFrames'))
+ {
var ic;
- for (ic = 0; ic < $scope.parray.length; ic++) {
+ for (ic = 0; ic < $scope.parray.length; ic++)
+ {
if ($scope.parray[ic].frameid == fid)
break;
}
-
NVRDataModel.debug("Readjusting selected frame ID from:" + $scope.ndx + " to actual frame ID of:" + ic);
$scope.ndx = ic;
- } else {
+ }
+ else
+ {
NVRDataModel.debug("No index adjustment necessary as we are using all frames");
}
-
-
-
// console.log ("Image Mode " + imode);
// console.log ("parray : " + JSON.stringify(parray));
// console.log ("index: " + ndx);
if ($scope.imode == 'path')
$scope.imgsrc = p + "/index.php?view=image&path=" + r + $scope.parray[$scope.ndx].fname;
- else {
+ else
+ {
$scope.imgsrc = p + "/index.php?view=image&fid=" + $scope.parray[$scope.ndx].id;
}
-
-
-
//$rootScope.zmPopup = $ionicPopup.alert({title: kFrame+':'+fid+'/'+kEvent+':'+e,template:img, cssClass:'popup80'});
- $rootScope.zmPopup = $ionicPopup.show({
+ $rootScope.zmPopup = $ionicPopup.show(
+ {
template: '<center>' + $translate.instant('kFrame') + ':{{parray[ndx].frameid}}@{{prettifyTimeSec(parray[ndx].time)}}</center><br/><img src="{{imgsrc}}" width="100%" />',
title: $translate.instant('kImages') + " (" + $translate.instant($scope.typeOfFrames) + ")",
subTitle: 'use left and right arrows to change',
@@ -580,7 +830,8 @@ angular.module('zmApp.controllers')
{
text: '',
type: 'button-assertive button-small ion-camera',
- onTap: function (e) {
+ onTap: function(e)
+ {
e.preventDefault();
saveNow($scope.imgsrc, r, parray[$scope.ndx].fname);
@@ -591,15 +842,18 @@ angular.module('zmApp.controllers')
// left 1
text: '',
type: 'button-small button-energized ion-chevron-left',
- onTap: function (e) {
+ onTap: function(e)
+ {
// look for next frame that matches the type of frame
// we are showing (all or diff timestamps);
// console.log ("TYPE OF FRAMES: " + $scope.typeOfFrames);
var nndx = null;
var alltype = $translate.instant('kShowAllFrames');
- for (var i = $scope.ndx - 1; i >= 0; i--) {
- if ($scope.parray[i].type == $scope.typeOfFrames || $scope.typeOfFrames == alltype) {
+ for (var i = $scope.ndx - 1; i >= 0; i--)
+ {
+ if ($scope.parray[i].type == $scope.typeOfFrames || $scope.typeOfFrames == alltype)
+ {
nndx = i;
break;
}
@@ -607,25 +861,26 @@ angular.module('zmApp.controllers')
if (nndx == null) nndx = $scope.ndx;
$scope.ndx = nndx;
- if ($scope.imode == 'path') {
+ if ($scope.imode == 'path')
+ {
$scope.imgsrc = p + "/index.php?view=image&path=" + r + $scope.parray[$scope.ndx].fname;
- } else {
+ }
+ else
+ {
$scope.imgsrc = p + "/index.php?view=image&fid=" + $scope.parray[$scope.ndx].id;
}
-
-
e.preventDefault();
-
}
},
{
// right 1
text: '',
type: 'button-small button-energized ion-chevron-right',
- onTap: function (e) {
+ onTap: function(e)
+ {
// look for next frame that matches the type of frame
// we are showing (all or diff timestamps);
@@ -633,9 +888,11 @@ angular.module('zmApp.controllers')
// console.log ("TYPE OF FRAMES: " + $scope.typeOfFrames);
var nndx = null;
var alltype = $translate.instant('kShowAllFrames');
- for (var i = $scope.ndx + 1; i < $scope.parray.length; i++) {
+ for (var i = $scope.ndx + 1; i < $scope.parray.length; i++)
+ {
//console.log ("Comparing: " +$scope.parray[i].type +" to " + $scope.typeOfFrames);
- if ($scope.parray[i].type == $scope.typeOfFrames || $scope.typeOfFrames == alltype) {
+ if ($scope.parray[i].type == $scope.typeOfFrames || $scope.typeOfFrames == alltype)
+ {
nndx = i;
break;
}
@@ -643,67 +900,63 @@ angular.module('zmApp.controllers')
if (nndx == null) nndx = $scope.ndx;
$scope.ndx = nndx;
- if ($scope.imode == 'path') {
+ if ($scope.imode == 'path')
+ {
$scope.imgsrc = p + "/index.php?view=image&path=" + r + $scope.parray[$scope.ndx].fname;
- } else {
+ }
+ else
+ {
$scope.imgsrc = p + "/index.php?view=image&fid=" + $scope.parray[$scope.ndx].id;
}
e.preventDefault();
-
}
},
-
-
{
text: '',
type: 'button-positive button-small ion-checkmark-round',
- onTap: function (e) {
-
+ onTap: function(e) {
}
- }]
+ }
+ ]
});
-
-
-
};
-
-
-
-
-
- $scope.toggleTypeOfAlarms = function () {
+ $scope.toggleTypeOfAlarms = function()
+ {
// "kShowAllFrames" : "all",
// "kShowTimeDiffFrames" : "different timestamps"
- if ($scope.typeOfFrames == $translate.instant('kShowAllFrames')) {
+ if ($scope.typeOfFrames == $translate.instant('kShowAllFrames'))
+ {
$scope.typeOfFrames = $translate.instant('kShowTimeDiffFrames');
- } else {
+ }
+ else
+ {
$scope.typeOfFrames = $translate.instant('kShowAllFrames');
}
};
-
// not explictly handling error --> I have a default "No events found" message
// displayed in the template if events list is null
//--------------------------------------------------------------------------
// This is what the pullup bar calls depending on what range is specified
//--------------------------------------------------------------------------
- $scope.showEvents = function (val, unit, monitorId) {
+ $scope.showEvents = function(val, unit, monitorId)
+ {
NVRDataModel.debug("ShowEvents called with val:" + val + " unit:" + unit + " for Monitor:" + monitorId);
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
-
// we have to convert from and to, to server time
var mToDate = moment().tz(NVRDataModel.getTimeZoneNow());
var mFromDate = moment().subtract(parseInt(val), unit).tz(NVRDataModel.getTimeZoneNow());
@@ -728,16 +981,17 @@ angular.module('zmApp.controllers')
.format("YYYY-MM-DD") + " " + mToDate
.format("HH:mm:ss");
-
// console.log("**************From String: " + $rootScope.fromString);
// console.log("**************To String: " + $rootScope.toString);
// reloading - may solve https://github.com/pliablepixels/zmNinja/issues/36
// if you are in the same mid event page $state.go won't work
- $state.go("events", {
+ $state.go("events",
+ {
"id": monitorId,
- "playEvent":false
- }, {
+ "playEvent": false
+ },
+ {
reload: true
});
};
@@ -745,27 +999,569 @@ angular.module('zmApp.controllers')
//----------------------------------------------------------------
// Alarm notification handling
//----------------------------------------------------------------
- $scope.handleAlarms = function () {
+ $scope.handleAlarms = function()
+ {
$rootScope.isAlarm = !$rootScope.isAlarm;
- if (!$rootScope.isAlarm) {
+ if (!$rootScope.isAlarm)
+ {
$rootScope.alarmCount = "0";
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
- $state.go("events", {
+ $state.go("events",
+ {
"id": 0,
- "playEvent":false
- }, {
+ "playEvent": false
+ },
+ {
reload: true
});
return;
}
};
+ // credit:http://stackoverflow.com/a/20151856/1361529
+ function base64toBlob(base64Data, contentType)
+ {
+ contentType = contentType || '';
+ var sliceSize = 1024;
+ var byteCharacters = atob(base64Data);
+ var bytesLength = byteCharacters.length;
+ var slicesCount = Math.ceil(bytesLength / sliceSize);
+ var byteArrays = new Array(slicesCount);
+
+ for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex)
+ {
+ var begin = sliceIndex * sliceSize;
+ var end = Math.min(begin + sliceSize, bytesLength);
+
+ var bytes = new Array(end - begin);
+ for (var offset = begin, i = 0; offset < end; ++i, ++offset)
+ {
+ bytes[i] = byteCharacters[offset].charCodeAt(0);
+ }
+ byteArrays[sliceIndex] = new Uint8Array(bytes);
+ }
+ return new Blob(byteArrays,
+ {
+ type: contentType
+ });
+ }
+
+ //----------------------------------------------------------
+ // create an array of images
+ // too keep memory manageable, we are only going to pick up alarmed frames
+ // and that too, max 1ps
+ // --------------------------------------------------------------
+ function prepareImages(e)
+ {
+ var d = $q.defer();
+ var imglist = [];
+ var myurl = loginData.apiurl + '/events/' + e.Event.Id + ".json";
+ $http.get(myurl)
+ .then(function(succ)
+ {
+ var data = succ.data;
+ var fps = 0;
+ var lastTime = "";
+
+ for (var i = 0; i < data.event.Frame.length; i++)
+ {
+ if (data.event.Frame[i].Type == "Alarm")
+ //if (1)
+ {
+ var fname;
+ //console.log ("PATH="+e.Event.imageMode);
+ if (e.Event.imageMode == 'path')
+ //if (1)
+ {
+ var rfp = padToN(data.event.Frame[i].FrameId, eventImageDigits) + "-capture.jpg";
+ fname = e.Event.baseURL + "/index.php?view=image&width=" + zm.maxGifWidth + "&path=" + e.Event.relativePath + rfp;
+ }
+ else
+ {
+ fname = e.Event.baseURL + "/index.php?view=image&width=" + zm.maxGifWidth + "&fid=" + data.event.Frame[i].Id;
+ }
+
+ if (data.event.Frame[i].TimeStamp != lastTime /*|| fps < 2*/ )
+
+ {
+ imglist.push(fname);
+ //fps = data.event.Frame[i].TimeStamp != lastTime ? 0 : fps+1;
+ lastTime = data.event.Frame[i].TimeStamp;
+ }
+
+ }
+
+ }
+
+ // next up make sure we are not processing more than 100 images
+
+ while (imglist.length > zm.maxGifCount2)
+ {
+ NVRDataModel.debug("Too many images: " + imglist.length + ", deleting alternate frames to keep it <=" + zm.maxGifCount2);
+
+ for (var l = 0; l < imglist.length; l++)
+ {
+ imglist.splice(l + 1, 2);
+ if (imglist.length <= zm.maxGifCount2) break;
+ }
+
+ }
+ NVRDataModel.debug("final image list length is:" + imglist.length);
+
+ d.resolve(imglist);
+ return d.promise;
+ },
+ function(err)
+ {
+ d.reject(err);
+ return d.promise;
+ });
+ return d.promise;
+ }
+
+ // force image to be of zm.maxGifWidth. TBD: rotated foo
+ function adjustAspect(e)
+ {
+
+ var w = zm.maxGifWidth;
+ var h = Math.round(e.Event.Height / e.Event.Width * zm.maxGifWidth);
+ return {
+ w: w,
+ h: h
+ };
+
+ }
+
+ // for devices - handle permission before you download
+ $scope.permissionsDownload = function(e)
+ {
+ if ($rootScope.platformOS == 'desktop')
+ {
+ gifAlert(e);
+ }
+ else
+ {
+
+ console.log("in perms");
+ cordova.plugins.photoLibrary.getLibrary(
+ function(library)
+ {
+ gifAlert(e);
+ },
+ function(err)
+ {
+ if (err.startsWith('Permission'))
+ {
+ // call requestAuthorization, and retry
+ cordova.plugins.photoLibrary.requestAuthorization(
+ function()
+ {
+ // User gave us permission to his library, retry reading it!
+ gifAlert(e);
+ },
+ function(err)
+ {
+ NVRDataModel.log("ERROR with saving permissions " + err);
+ // User denied the access
+ }, // if options not provided, defaults to {read: true}.
+ {
+ read: true,
+ write: true
+ }
+ );
+ }
+ // Handle error - it's not permission-related
+ }
+ );
+
+ }
+ };
+
+ // make sure the user knows the GIF is not full fps/all frames
+ function gifAlert(e)
+ {
+ if(navigator.userAgent.toLowerCase().indexOf('crosswalk') == -1) {
+ $ionicPopup.confirm(
+ {
+ title: $translate.instant('kNote'),
+ template: "{{'kGifWarning' | translate }}",
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
+ }).then(function(res)
+ {
+ if (res)
+ {
+ downloadAsGif2(e);
+ }
+ else
+ NVRDataModel.debug ("User cancelled GIF");
+
+ });
+ }
+ else
+ {
+ $ionicPopup.alert({
+ title:$translate.instant ('kNote'),
+ template:"{{'kGifNoCrosswalk' | translate}}"
+ });
+ }
+
+
+ }
+
+ // convert to base64 - devices need this to save to gallery
+ function blobToBase64(blob)
+ {
+ NVRDataModel.debug("converting blob to base64...");
+ var d = $q.defer();
+ var reader = new window.FileReader();
+ reader.readAsDataURL(blob);
+ reader.onloadend = function()
+ {
+ var base64data = reader.result;
+ //console.log(base64data );
+ d.resolve(base64data);
+ return d.promise;
+
+ };
+ return d.promise;
+ }
+
+ // part of neuquant conversion
+ function componentizedPaletteToArray(paletteRGB)
+ {
+ var paletteArray = [],
+ i, r, g, b;
+ for (i = 0; i < paletteRGB.length; i += 3)
+ {
+ r = paletteRGB[i];
+ g = paletteRGB[i + 1];
+ b = paletteRGB[i + 2];
+ paletteArray.push(r << 16 | g << 8 | b);
+ }
+ return paletteArray;
+ }
+
+ // part of neuquant conversion
+ function dataToRGB(data, width, height)
+ {
+ var i = 0,
+ length = width * height * 4,
+ rgb = [];
+ while (i < length)
+ {
+ rgb.push(data[i++]);
+ rgb.push(data[i++]);
+ rgb.push(data[i++]);
+ i++;
+ }
+ return rgb;
+ }
+
+ // credit Jimmy Warting
+ // https://github.com/jimmywarting/StreamSaver.js/issues/38
+ // he stream-ized and cleaned up the gif creation process
+ // using GifWriter.js
+ function createGif(files, w, h)
+ {
+
+ var cv = document.getElementById("canvas");
+ var ctx = cv.getContext("2d");
+ var pixels = new Uint8Array(w * h);
+ var totalImages = files.length;
+ var processedImages = 0;
+
+ cv.width = w;
+ cv.height = h;
+
+ var rs = new ReadableStream(
+ {
+ // Each time pull gets called you should get the pixel data and
+ // enqueue it as if it would be good old gif.addFrame()
+ pull: function pull(controller)
+ {
+ var frame = files.shift();
+ if (!frame) {controller.close(); return;}
+
+ return $http(
+ {
+ url: frame,
+ responseType: "blob"
+ })
+ .then(function(res)
+ {
+
+ return res.data.image();
+ })
+ .then(function(img)
+ {
+ processedImages++;
+
+ var p = Math.round(processedImages / totalImages * 100);
+ $ionicLoading.show(
+ {
+ template: $translate.instant('kPleaseWait') + "...(" + p + "%)",
+ noBackdrop: true
+ });
+
+ console.log("URL=" + frame);
+ URL.revokeObjectURL(img.src);
+ ctx.drawImage(img, 0, 0);
+
+ var data = ctx.getImageData(0, 0, w, h).data;
+ var rgbComponents = dataToRGB(data, w, h);
+ var nq = new NeuQuant(rgbComponents, rgbComponents.length, 15);
+ var paletteRGB = nq.process();
+ var paletteArray = new Uint32Array(componentizedPaletteToArray(paletteRGB));
+ var numberPixels = w * h;
+ var k = 0,
+ i, r, g, b;
+
+ for (i = 0; i < numberPixels; i++)
+ {
+ r = rgbComponents[k++];
+ g = rgbComponents[k++];
+ b = rgbComponents[k++];
+ pixels[i] = nq.map(r, g, b);
+ }
+
+ controller.enqueue([0, 0, w, h, pixels,
+ {
+ palette: paletteArray,
+ delay: 100, // 1 second
+ }]);
+ });
+ }
+ });
+
+ return new GifWriter(rs, w, h,
+ {
+ loop: null
+ });
+ }
+
+
+
+ function downloadAsGif2(e)
+ {
+ $rootScope.isDownloading = true;
+ $ionicLoading.show(
+ {
+ template: $translate.instant('kPleaseWait') + "...",
+ noBackdrop: true,
+ duration: 20000
+ });
+ NVRDataModel.setAwake(true);
+
+ prepareImages(e)
+ .then(function(files)
+ {
+ return $http(
+ {
+ url: files[0],
+ responseType: "blob"
+ })
+ .then(function(res)
+ {
+ return res.data.image();
+ })
+ .then(function(img)
+ {
+ URL.revokeObjectURL(img.src); // Revoke object URL to free memory
+ var stream = createGif(files, img.width, img.height);
+ //var fileStream = streamSaver.createWriteStream('image.gif');
+
+ var chunks = [];
+ var reader = stream.getReader();
+
+ function pull()
+ {
+ return reader.read().then(function(result)
+ {
+ chunks.push(result.value);
+ return result.done ? chunks : pull();
+ });
+ }
+
+ pull().then(function(chunks)
+ {
+ var blob = new Blob(chunks,
+ {
+ type: "image/gif"
+
+ });
+
+ //alert ("WE ARE DONE!");
+ if ($rootScope.platformOS == 'desktop')
+ {
+ saveAs(blob, e.Event.Id + "-video.gif");
+ $ionicLoading.hide();
+ }
+ else
+ {
+ // write blob to file
+ var tp;
+ if ($rootScope.platformOS == 'ios')
+ tp = cordova.file.documentsDirectory;
+ else
+ tp = cordova.file.dataDirectory;
+ var th = true, opt = {};
+
+ $ionicLoading.show(
+ {
+
+ template:"writing to file...",
+ noBackdrop: true,
+ });
+
+ //var bloburl = URL.createObjectURL(blob);
+ //NVRDataModel.debug ("blob-url is:"+bloburl);
+
+ writeFile2(tp,"temp-file.gif",blob,false)
+ .then (function (succ) {
+ NVRDataModel.debug ("write to file successful");
+ console.log( "write file successful");
+ $ionicLoading.hide();
+
+ var ntp = tp;
+ //ntp = tp.indexOf('file://') === 0 ? tp.slice(7) : tp;
+
+ ntp = ntp+"temp-file.gif";
+ console.log ("ntp="+ntp);
+
+ moveImageToGallery(ntp);
+ $rootScope.isDownloading = false;
+
+ }, function (err) {
+ $rootScope.isDownloading = false;
+ $ionicLoading.hide();
+ NVRDataModel.debug ("error writing to file "+JSON.stringify(err));
+
+
+ });
+ }
+
+ });
+ });
+
+ },
+ function(err)
+ {
+ $ionicLoading.hide();
+ NVRDataModel.setAwake(false);
+ NVRDataModel.log("Error getting frames");
+ $rootScope.isDownloading = false;
+ }
+
+ );
+
+ }
+
+ // NOT USED - WILL REMOVE AFTER TESTING OTHER METHOD MORE
+ function downloadAsGif(e)
+ {
+ $ionicLoading.show(
+ {
+ template: $translate.instant('kPleaseWait') + "...",
+ noBackdrop: true,
+ duration: 20000
+ });
+
+ prepareImages(e)
+ .then(function(imgs)
+ {
+
+ console.log("TOTAL IMAGES TO GIF=" + imgs.length);
+ //console.log(JSON.stringify(imgs));
+
+ var ad = adjustAspect(e);
+ //console.log("SAVING W=" + ad.w + " H=" + ad.h);
+ NVRDataModel.setAwake(true);
+ gifshot.createGIF(
+ {
+
+ 'gifWidth': ad.w,
+ 'gifHeight': ad.h,
+ 'images': imgs,
+ 'interval': 1,
+ //'loop':null,
+ 'sampleInterval': 20,
+ //'frameDur':5, // 1/2 a sec
+ 'text': 'zmNinja',
+ 'crossOrigin': 'use-credentials',
+ 'progressCallback': function(cp)
+ {
+ var p = Math.round(cp * 100);
+ $ionicLoading.show(
+ {
+ template: $translate.instant('kPleaseWait') + "...(" + p + "%)",
+ noBackdrop: true
+ });
+ }
+ }, function(obj)
+ {
+ NVRDataModel.setAwake(false);
+ if (!obj.error)
+ {
+ //console.log(obj.image);
+
+ var blob;
+
+ if ($rootScope.platformOS == 'desktop')
+ {
+
+ obj.image = obj.image.replace(/data:image\/gif;base64,/, '');
+ blob = base64toBlob(obj.image, "image/gif");
+ var f = NVRDataModel.getMonitorName(e.Event.MonitorId);
+ f = f + "-" + e.Event.Id + ".gif";
+ saveAs(blob, f);
+ $ionicLoading.hide();
+ }
+
+ else
+ {
+ NVRDataModel.debug("Saving blob to gallery...");
+ var album = "zmNinja";
+ cordova.plugins.photoLibrary.saveImage(obj.image, album,
+ function()
+ {
+ $ionicLoading.hide();
+ NVRDataModel.debug("Event saved");
+ },
+ function(err)
+ {
+ $ionicLoading.hide();
+ NVRDataModel.debug("Saving ERROR=" + err);
+ });
+
+ }
+
+ }
+ else
+ {
+ $ionicLoading.hide();
+ NVRDataModel.log("Error creating GIF");
+ }
+ });
+ },
+ function(err)
+ {
+ $ionicLoading.hide();
+ NVRDataModel.log("Error getting frames");
+ }
+
+ );
+ }
+
//--------------------------------------------------------------------------
// Takes care of deleting individual events
//--------------------------------------------------------------------------
- $scope.deleteEvent = function (id, itemid) {
+
+ $scope.deleteEvent = function(id, itemid)
+ {
//$scope.eventList.showDelete = false;
//curl -XDELETE http://server/zm/api/events/1.json
var loginData = NVRDataModel.getLogin();
@@ -773,14 +1569,16 @@ angular.module('zmApp.controllers')
NVRDataModel.debug("DeleteEvent: ID=" + id + " item=" + itemid);
NVRDataModel.log("Delete event " + apiDelete);
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: "{{'kDeletingEvent' | translate}}...",
noBackdrop: true,
duration: zm.httpTimeout
});
$http.delete(apiDelete)
- .success(function (data) {
+ .success(function(data)
+ {
$ionicLoading.hide();
NVRDataModel.debug("delete success: " + JSON.stringify(data));
NVRDataModel.displayBanner('info', [$translate.instant('kDeleteEventSuccess')], 2000, 2000);
@@ -789,30 +1587,36 @@ angular.module('zmApp.controllers')
//doRefresh();
})
- .error(function (data) {
+ .error(function(data)
+ {
$ionicLoading.hide();
NVRDataModel.debug("delete error: " + JSON.stringify(data));
NVRDataModel.displayBanner('error', [$translate.instant('kDeleteEventError1'), $translate.instant('kDeleteEventError2')]);
});
-
};
//------------------------------------------------
// Tapping on the filter sign lets you reset it
//-------------------------------------------------
- $scope.filterTapped = function () {
+ $scope.filterTapped = function()
+ {
//console.log("FILTER TAPPED");
var myFrom = moment($rootScope.fromString).format("MMM/DD/YYYY " + NVRDataModel.getTimeFormat()).toString();
var toString = moment($rootScope.toString).format("MMM/DD/YYYY " + NVRDataModel.getTimeFormat()).toString();
- $rootScope.zmPopup = $ionicPopup.confirm({
+ $rootScope.zmPopup = $ionicPopup.confirm(
+ {
title: $translate.instant('kFilterSettings'),
- template: $translate.instant('kFilterEventsBetween1') + ':<br/> <b>' + myFrom + "</b> " + $translate.instant('kTo') + " <b>" + toString + '</b><br/>' + $translate.instant('kFilterEventsBetween2')
+ template: $translate.instant('kFilterEventsBetween1') + ':<br/> <b>' + myFrom + "</b> " + $translate.instant('kTo') + " <b>" + toString + '</b><br/>' + $translate.instant('kFilterEventsBetween2'),
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
});
- $rootScope.zmPopup.then(function (res) {
- if (res) {
+ $rootScope.zmPopup.then(function(res)
+ {
+ if (res)
+ {
NVRDataModel.log("Filter reset requested in popup");
$rootScope.isEventFilterOn = false;
$rootScope.fromDate = "";
@@ -821,15 +1625,19 @@ angular.module('zmApp.controllers')
$rootScope.toTime = "";
$rootScope.fromString = "";
$rootScope.toString = "";
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
- $state.go("events", {
+ $state.go("events",
+ {
"id": 0,
- "playEvent":false
+ "playEvent": false
});
return;
- } else {
+ }
+ else
+ {
NVRDataModel.log("Filter reset cancelled in popup");
}
});
@@ -841,40 +1649,44 @@ angular.module('zmApp.controllers')
// data for events ranges summaries using the consolveEvents facility of ZM
//--------------------------------------------------------------------------
- $scope.footerExpand = function () {
- footerExpand();
+ $scope.footerExpand = function()
+ {
+ footerExpand();
};
function footerExpand()
{
- //https://server/zm/api/events/consoleEvents/5%20minute.json
+ //https://server/zm/api/events/consoleEvents/5%20minute.json
var ld = NVRDataModel.getLogin();
- var af = "/AlarmFrames >=:" + (ld.enableAlarmCount ? ld.minAlarmCount : 0);
+ var af = "/AlarmFrames >=:" + (ld.enableAlarmCount ? ld.minAlarmCount : 0);
var apiurl = ld.apiurl + "/events/consoleEvents/1%20hour" + af + ".json";
NVRDataModel.debug("consoleEvents API:" + apiurl);
-
$http.get(apiurl)
- .success(function (data) {
+ .success(function(data)
+ {
NVRDataModel.debug(JSON.stringify(data));
$scope.hours = [];
var p = data.results;
- for (var key in data.results) {
-
-
+ for (var key in data.results)
+ {
- if (p.hasOwnProperty(key)) {
+ if (p.hasOwnProperty(key))
+ {
var idfound = true;
//console.log ("PERSIST IS " + ld.persistMontageOrder);
- if (ld.persistMontageOrder) {
+ if (ld.persistMontageOrder)
+ {
idfound = false;
- for (var ii = 0; ii < $scope.monitors.length; ii++) {
- if ($scope.monitors[ii].Monitor.Id == key && (NVRDataModel.isNotHidden(key) || showHiddenMonitors) ) {
- // console.log ("Authorizing "+$scope.monitors[ii].Monitor.Name);
+ for (var ii = 0; ii < $scope.monitors.length; ii++)
+ {
+ if ($scope.monitors[ii].Monitor.Id == key && (NVRDataModel.isNotHidden(key) || showHiddenMonitors))
+ {
+ // console.log ("Authorizing "+$scope.monitors[ii].Monitor.Name);
idfound = true;
break;
}
@@ -882,7 +1694,8 @@ angular.module('zmApp.controllers')
}
//console.log(NVRDataModel.getMonitorName(key) + " -> " + p[key]);
if (idfound)
- $scope.hours.push({
+ $scope.hours.push(
+ {
monitor: NVRDataModel.getMonitorName(key),
events: p[key],
mid: key
@@ -892,21 +1705,26 @@ angular.module('zmApp.controllers')
}
});
-
apiurl = ld.apiurl + "/events/consoleEvents/1%20day" + af + ".json";
NVRDataModel.debug("consoleEvents API:" + apiurl);
$http.get(apiurl)
- .success(function (data) {
+ .success(function(data)
+ {
NVRDataModel.debug(JSON.stringify(data));
$scope.days = [];
var p = data.results;
- for (var key in data.results) {
- if (p.hasOwnProperty(key)) {
+ for (var key in data.results)
+ {
+ if (p.hasOwnProperty(key))
+ {
var idfound = true;
- if (ld.persistMontageOrder) {
+ if (ld.persistMontageOrder)
+ {
idfound = false;
- for (var ii = 0; ii < $scope.monitors.length; ii++) {
- if ($scope.monitors[ii].Monitor.Id == key && (NVRDataModel.isNotHidden(key) || showHiddenMonitors)) {
+ for (var ii = 0; ii < $scope.monitors.length; ii++)
+ {
+ if ($scope.monitors[ii].Monitor.Id == key && (NVRDataModel.isNotHidden(key) || showHiddenMonitors))
+ {
idfound = true;
break;
}
@@ -915,7 +1733,8 @@ angular.module('zmApp.controllers')
//console.log(NVRDataModel.getMonitorName(key) + " -> " + p[key]);
if (idfound)
//console.log(NVRDataModel.getMonitorName(key) + " -> " + p[key]);
- $scope.days.push({
+ $scope.days.push(
+ {
monitor: NVRDataModel.getMonitorName(key),
events: p[key],
mid: key
@@ -925,23 +1744,27 @@ angular.module('zmApp.controllers')
}
});
-
-
apiurl = ld.apiurl + "/events/consoleEvents/1%20week" + af + ".json";
NVRDataModel.debug("consoleEvents API:" + apiurl);
$http.get(apiurl)
- .success(function (data) {
+ .success(function(data)
+ {
NVRDataModel.debug(JSON.stringify(data));
$scope.weeks = [];
var p = data.results;
- for (var key in data.results) {
- if (p.hasOwnProperty(key)) {
+ for (var key in data.results)
+ {
+ if (p.hasOwnProperty(key))
+ {
var idfound = true;
- if (ld.persistMontageOrder) {
+ if (ld.persistMontageOrder)
+ {
idfound = false;
- for (var ii = 0; ii < $scope.monitors.length; ii++) {
- if ($scope.monitors[ii].Monitor.Id == key && (NVRDataModel.isNotHidden(key)|| showHiddenMonitors)) {
+ for (var ii = 0; ii < $scope.monitors.length; ii++)
+ {
+ if ($scope.monitors[ii].Monitor.Id == key && (NVRDataModel.isNotHidden(key) || showHiddenMonitors))
+ {
idfound = true;
break;
}
@@ -950,7 +1773,8 @@ angular.module('zmApp.controllers')
//console.log(NVRDataModel.getMonitorName(key) + " -> " + p[key]);
if (idfound)
//console.log(NVRDataModel.getMonitorName(key) + " -> " + p[key]);
- $scope.weeks.push({
+ $scope.weeks.push(
+ {
monitor: NVRDataModel.getMonitorName(key),
events: p[key],
mid: key
@@ -960,23 +1784,28 @@ angular.module('zmApp.controllers')
}
});
-
apiurl = ld.apiurl + "/events/consoleEvents/1%20month" + af + ".json";
NVRDataModel.debug("consoleEvents API:" + apiurl);
$http.get(apiurl)
- .success(function (data) {
+ .success(function(data)
+ {
NVRDataModel.debug(JSON.stringify(data));
$scope.months = [];
var p = data.results;
- for (var key in data.results) {
- if (p.hasOwnProperty(key)) {
+ for (var key in data.results)
+ {
+ if (p.hasOwnProperty(key))
+ {
var idfound = true;
var ld = NVRDataModel.getLogin();
- if (ld.persistMontageOrder) {
+ if (ld.persistMontageOrder)
+ {
idfound = false;
- for (var ii = 0; ii < $scope.monitors.length; ii++) {
- if ($scope.monitors[ii].Monitor.Id == key && (NVRDataModel.isNotHidden(key)|| showHiddenMonitors)) {
+ for (var ii = 0; ii < $scope.monitors.length; ii++)
+ {
+ if ($scope.monitors[ii].Monitor.Id == key && (NVRDataModel.isNotHidden(key) || showHiddenMonitors))
+ {
idfound = true;
break;
}
@@ -985,7 +1814,8 @@ angular.module('zmApp.controllers')
//console.log(NVRDataModel.getMonitorName(key) + " -> " + p[key]);
if (idfound)
//console.log(NVRDataModel.getMonitorName(key) + " -> " + p[key]);
- $scope.months.push({
+ $scope.months.push(
+ {
monitor: NVRDataModel.getMonitorName(key),
events: p[key],
mid: key
@@ -997,16 +1827,21 @@ angular.module('zmApp.controllers')
}
- $scope.openMenu = function () {
+ $scope.openMenu = function()
+ {
$ionicSideMenuDelegate.toggleLeft();
};
- $scope.scrollPosition = function () {
+ $scope.scrollPosition = function()
+ {
var scrl = parseFloat($ionicScrollDelegate.$getByHandle("mainScroll").getScrollPosition().top);
- var item = Math.round(scrl / eventsListDetailsHeight );
- if ($scope.events == undefined || !$scope.events.length || $scope.events[item] == undefined) {
+ var item = Math.round(scrl / eventsListDetailsHeight);
+ if ($scope.events == undefined || !$scope.events.length || $scope.events[item] == undefined)
+ {
return "";
- } else {
+ }
+ else
+ {
//return prettifyDate($scope.events[item].Event.StartTime);
return ($scope.events[item].Event.humanizeTime);
}
@@ -1016,7 +1851,8 @@ angular.module('zmApp.controllers')
//-------------------------------------------------------------------------
// called when user switches to background
//-------------------------------------------------------------------------
- function onPause() {
+ function onPause()
+ {
NVRDataModel.debug("EventCtrl:onpause called");
if ($scope.popover) $scope.popover.remove();
@@ -1024,66 +1860,70 @@ angular.module('zmApp.controllers')
//-------------------------------------------------------------------------
// Pads the filename with leading 0s, depending on ZM_IMAGE_DIGITS
//-------------------------------------------------------------------------
- function padToN(number, digits) {
+ function padToN(number, digits)
+ {
var i;
var stringMax = "";
var stringLeading = "";
- for (i = 1; i <= digits; i++) {
+ for (i = 1; i <= digits; i++)
+ {
stringMax = stringMax + "9";
if (i != digits) stringLeading = stringLeading + "0";
}
var numMax = parseInt(stringMax);
- if (number <= numMax) {
+ if (number <= numMax)
+ {
number = (stringLeading + number).slice(-digits);
}
//console.log ("PADTON: returning " + number);
return number;
}
-
//-------------------------------------------------------------------------
// FIXME: Are we using this?
//-------------------------------------------------------------------------
- $scope.disableSlide = function () {
+ $scope.disableSlide = function()
+ {
NVRDataModel.debug("EventCtrl:DisableSlide called");
$ionicSlideBoxDelegate.$getByHandle("eventSlideBox").enableSlide(false);
};
-
-
-
//-------------------------------------------------------------------------
// This function is called when a user enables or disables
// scrub view for an event.
//-------------------------------------------------------------------------
- $scope.toggleGroupScrub = function (event, ndx, frames) {
+ $scope.toggleGroupScrub = function(event, ndx, frames)
+ {
$scope.groupType = "scrub";
toggleGroup(event, ndx, frames, $scope.groupType);
};
- $scope.toggleGroupAlarms = function (event, ndx, frames) {
+ $scope.toggleGroupAlarms = function(event, ndx, frames)
+ {
$scope.groupType = "alarms";
toggleGroup(event, ndx, frames, $scope.groupType);
};
- function toggleGroup(event, ndx, frames, groupType) {
-
+ function toggleGroup(event, ndx, frames, groupType)
+ {
// If we are here and there is a record of a previous scroll
// then we need to scroll back to hide that view
- if (scrollbynumber) {
+ if (scrollbynumber)
+ {
$ionicScrollDelegate.$getByHandle("mainScroll").scrollBy(0, -1 * scrollbynumber, true);
scrollbynumber = 0;
}
- if (oldEvent && event != oldEvent) {
+ if (oldEvent && event != oldEvent)
+ {
NVRDataModel.debug("EventCtrl:Old event scrub will hide now");
oldEvent.Event.ShowScrub = false;
- oldEvent.Event.height = eventsListDetailsHeight ;
+ oldEvent.Event.height = eventsListDetailsHeight;
oldEvent = "";
}
@@ -1095,14 +1935,16 @@ angular.module('zmApp.controllers')
if (event.Event.ShowScrub == true) // turn on display now
{
- if (groupType == 'alarms') {
+ if (groupType == 'alarms')
+ {
$scope.alarm_images = [];
event.Event.height = (eventsListDetailsHeight + eventsListScrubHeight);
$ionicScrollDelegate.resize();
var myurl = loginData.apiurl + '/events/' + event.Event.Id + ".json";
NVRDataModel.log("API for event details" + myurl);
$http.get(myurl)
- .success(function (data) {
+ .success(function(data)
+ {
$scope.FrameArray = data.event.Frame;
// $scope.slider_options.scale=[];
@@ -1110,18 +1952,24 @@ angular.module('zmApp.controllers')
var i;
var timestamp = null;
- for (i = 0; i < data.event.Frame.length; i++) {
- if (data.event.Frame[i].Type == "Alarm") {
+ for (i = 0; i < data.event.Frame.length; i++)
+ {
+ if (data.event.Frame[i].Type == "Alarm")
+ {
//console.log ("**ONLY ALARM AT " + i + "of " + data.event.Frame.length);
var atype;
- if (timestamp != data.event.Frame[i].TimeStamp) {
+ if (timestamp != data.event.Frame[i].TimeStamp)
+ {
atype = $translate.instant('kShowTimeDiffFrames');
- } else {
+ }
+ else
+ {
atype = $translate.instant('kShowAllFrames');
}
- $scope.alarm_images.push({
+ $scope.alarm_images.push(
+ {
type: atype,
id: data.event.Frame[i].Id,
frameid: data.event.Frame[i].FrameId,
@@ -1137,7 +1985,8 @@ angular.module('zmApp.controllers')
//console.log (JSON.stringify(data));
})
- .error(function (err) {
+ .error(function(err)
+ {
NVRDataModel.log("Error retrieving detailed frame API " + JSON.stringify(err));
NVRDataModel.displayBanner('error', ['could not retrieve frame details', 'please try again']);
});
@@ -1157,31 +2006,38 @@ angular.module('zmApp.controllers')
realtime: true,
step: 1,
className: "mySliderClass",
- callback: function (value, released) {
+ callback: function(value, released)
+ {
//console.log("CALLBACK"+value+released);
$ionicScrollDelegate.freezeScroll(!released);
//NVRDataModel.debug("EventCtrl: freezeScroll called with " + !released);
-
},
//modelLabels:function(val) {return "";},
- css: {
- background: {
+ css:
+ {
+ background:
+ {
"background-color": "silver"
},
- before: {
+ before:
+ {
"background-color": "purple"
},
- default: {
+ default:
+ {
"background-color": "white"
}, // default value: 1px
- after: {
+ after:
+ {
"background-color": "green"
}, // zone after default value
- pointer: {
+ pointer:
+ {
"background-color": "red"
}, // circle pointer
- range: {
+ range:
+ {
"background-color": "red"
} // use it if double value
},
@@ -1197,58 +2053,58 @@ angular.module('zmApp.controllers')
$scope.slides = [];
var i;
- if (event.Event.imageMode == 'path') {
+ if (event.Event.imageMode == 'path')
+ {
NVRDataModel.debug("EventCtrl: found " + frames + " frames to scrub");
-
-
- for (i = 1; i <= frames; i++) {
+ for (i = 1; i <= frames; i++)
+ {
var fname = padToN(i, eventImageDigits) + "-capture.jpg";
-
-
- $scope.slides.push({
+ $scope.slides.push(
+ {
id: i,
img: fname
});
}
- } else // we need fids
+ }
+ else // we need fids
{
var myurl_frames = loginData.apiurl + '/events/' + event.Event.Id + ".json";
NVRDataModel.log("API for event details" + myurl_frames);
$http.get(myurl_frames)
- .success(function (data) {
+ .success(function(data)
+ {
$scope.FrameArray = data.event.Frame;
// $scope.slider_options.scale=[];
//$scope.slider_options.scale = [];
var i;
- for (i = 0; i < data.event.Frame.length; i++) {
-
+ for (i = 0; i < data.event.Frame.length; i++)
+ {
//console.log ("**ONLY ALARM AT " + i + "of " + data.event.Frame.length);
- $scope.slides.push({
+ $scope.slides.push(
+ {
id: data.event.Frame[i].Id,
frameid: data.event.Frame[i].FrameId,
});
-
}
//console.log (JSON.stringify(data));
})
- .error(function (err) {
+ .error(function(err)
+ {
NVRDataModel.log("Error retrieving detailed frame API " + JSON.stringify(err));
NVRDataModel.displayBanner('error', [$translate.instant('kErrorFrameBanner'), $translate.instant('kErrorPleaseTryAgain')]);
});
}
-
-
// now get event details to show alarm frames
loginData = NVRDataModel.getLogin();
@@ -1258,7 +2114,8 @@ angular.module('zmApp.controllers')
event.Event.video = {};
var videoURL;
- if (event.Event.imageMode == 'path')
+ //if (event.Event.imageMode == 'path')
+ if (1)
videoURL = event.Event.baseURL + "/events/" + event.Event.relativePath + event.Event.DefaultVideo;
else
videoURL = event.Event.baseURL + "/index.php?view=view_video&eid=" + event.Event.Id;
@@ -1270,7 +2127,7 @@ angular.module('zmApp.controllers')
{
src: $sce.trustAsResourceUrl(videoURL),
type: "video/mp4"
- }
+ }
],
@@ -1278,25 +2135,30 @@ angular.module('zmApp.controllers')
};
-
var myurl2 = loginData.apiurl + '/events/' + event.Event.Id + ".json";
NVRDataModel.log("API for event details" + myurl2);
$http.get(myurl2)
- .success(function (data) {
+ .success(function(data)
+ {
$scope.FrameArray = data.event.Frame;
// $scope.slider_options.scale=[];
$scope.slider_options.scale = [];
var i;
- for (i = 0; i < data.event.Frame.length; i++) {
- if (data.event.Frame[i].Type == "Alarm") {
+ for (i = 0; i < data.event.Frame.length; i++)
+ {
+ if (data.event.Frame[i].Type == "Alarm")
+ {
//console.log ("**ALARM AT " + i + "of " + data.event.Frame.length);
- $scope.slider_options.scale.push({
+ $scope.slider_options.scale.push(
+ {
val: data.event.Frame[i].FrameId,
label: ' '
});
- } else {
+ }
+ else
+ {
//$scope.slider_options.scale.push(' ');
}
@@ -1304,12 +2166,12 @@ angular.module('zmApp.controllers')
//console.log (JSON.stringify(data));
})
- .error(function (err) {
+ .error(function(err)
+ {
NVRDataModel.log("Error retrieving detailed frame API " + JSON.stringify(err));
NVRDataModel.displayBanner('error', [$translate.instant('kErrorFrameBanner'), $translate.instant('kErrorPleaseTryAgain')]);
});
-
oldEvent = event;
$rootScope.rand = Math.floor(Math.random() * (999999 - 111111 + 1)) + 111111;
var elem = angular.element(document.getElementById("item-" + ndx));
@@ -1331,13 +2193,15 @@ angular.module('zmApp.controllers')
} // end of groupType == scrub
} // end of ShowScrub == true
- else {
+ else
+ {
// $ionicScrollDelegate.freezeScroll(false);
$ionicSideMenuDelegate.canDragContent(true);
event.Event.height = eventsListDetailsHeight;
$ionicScrollDelegate.resize();
- if (scrollbynumber) {
+ if (scrollbynumber)
+ {
$ionicScrollDelegate.$getByHandle("mainScroll").scrollBy(0, -1 * scrollbynumber, true);
scrollbynumber = 0;
}
@@ -1346,15 +2210,18 @@ angular.module('zmApp.controllers')
}
- $scope.closeIfOpen = function (event) {
- if (event != undefined) {
+ $scope.closeIfOpen = function(event)
+ {
+ if (event != undefined)
+ {
if (event.Event.ShowScrub)
toggleGroup(event);
}
};
- $scope.isGroupShown = function (event) {
+ $scope.isGroupShown = function(event)
+ {
// console.log ("IS SHOW INDEX is " + ndx);
//console.log ("SHOW GROUP IS " + showGroup);
@@ -1365,7 +2232,8 @@ angular.module('zmApp.controllers')
//---------------------------------------------------
// reload view
//---------------------------------------------------
- $scope.reloadView = function () {
+ $scope.reloadView = function()
+ {
// All we really need to do here is change the random token
// in the image src and it will refresh. No need to reload the view
// and if you did reload the view, it would go back to events list
@@ -1373,7 +2241,8 @@ angular.module('zmApp.controllers')
//console.log("*** Refreshing Modal view ***");
//$state.go($state.current, {}, {reload: true});
$rootScope.rand = Math.floor(Math.random() * (999999 - 111111 + 1)) + 111111;
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kRefreshedView'),
noBackdrop: true,
duration: 3000
@@ -1384,17 +2253,20 @@ angular.module('zmApp.controllers')
//---------------------------------------------------
// when you tap a list entry - to break search loop
//---------------------------------------------------
- $scope.tapped = function () {
+ $scope.tapped = function()
+ {
// console.log("*** TAPPED ****");
// if he tapped, the we are not infinite loading on ion-infinite
- if (enableLoadMore == false) {
+ if (enableLoadMore == false)
+ {
moreEvents = true;
enableLoadMore = true;
// console.log("REMOVING ARTIFICAL LOAD MORE BLOCK");
}
};
- $scope.$on('$ionicView.loaded', function () {
+ $scope.$on('$ionicView.loaded', function()
+ {
// console.log("**VIEW ** Events Ctrl Loaded");
});
@@ -1405,28 +2277,33 @@ angular.module('zmApp.controllers')
// reset power state on exit as if it is called after we enter another
// state, that effectively overwrites current view power management needs
//------------------------------------------------------------------------
- $scope.$on('$ionicView.enter', function () {
+ $scope.$on('$ionicView.enter', function()
+ {
// console.log("**VIEW ** Events Ctrl Entered");
NVRDataModel.setAwake(false);
- EventServer.sendMessage('push', {
+ EventServer.sendMessage('push',
+ {
type: 'badge',
badge: 0,
});
- $ionicPopover.fromTemplateUrl('templates/events-popover.html', {
+ $ionicPopover.fromTemplateUrl('templates/events-popover.html',
+ {
scope: $scope,
- }).then(function (popover) {
+ }).then(function(popover)
+ {
$scope.popover = popover;
});
-
-
//reset badge count
- if (window.cordova && window.cordova.plugins.notification) {
- $cordovaBadge.set(0).then(function () {
+ if (window.cordova && window.cordova.plugins.notification)
+ {
+ $cordovaBadge.set(0).then(function()
+ {
// You have permission, badge set.
- }, function (err) {
+ }, function(err)
+ {
NVRDataModel.debug("app does not have badge permissions. Please check your phone notification settings");
// You do not have permission.
});
@@ -1436,14 +2313,17 @@ angular.module('zmApp.controllers')
});
- $scope.$on('$ionicView.leave', function () {
+ $scope.$on('$ionicView.leave', function()
+ {
//console.log("**VIEW ** Events Ctrl Left");
});
- $scope.$on('$ionicView.unloaded', function () {
+ $scope.$on('$ionicView.unloaded', function()
+ {
//console.log("**VIEW ** Events Ctrl Unloaded");
//console.log("*** MODAL ** Destroying modal too");
- if ($scope.modal !== undefined) {
+ if ($scope.modal !== undefined)
+ {
$scope.modal.remove();
}
@@ -1452,7 +2332,8 @@ angular.module('zmApp.controllers')
//---------------------------------------------------
// used to hide loading image toast
//---------------------------------------------------
- $scope.finishedLoadingImage = function (ndx) {
+ $scope.finishedLoadingImage = function(ndx)
+ {
// console.log("*** Events image FINISHED loading index: "+ndx+"***");
$ionicLoading.hide();
};
@@ -1460,33 +2341,36 @@ angular.module('zmApp.controllers')
//---------------------------------------------------
//
//---------------------------------------------------
- $scope.clearSearch = function () {
+ $scope.clearSearch = function()
+ {
$scope.search.text = "";
};
//---------------------------------------------------
// Called when user toggles search
//---------------------------------------------------
- $scope.searchClicked = function () {
+ $scope.searchClicked = function()
+ {
$scope.showSearch = !$scope.showSearch;
// this helps greatly in repeat scroll gets
if ($scope.showSearch == false)
$scope.search.text = "";
//console.log("**** Setting search view to " + $scope.showSearch + " ****");
- if (enableLoadMore == false && $scope.showSearch == false) {
+ if (enableLoadMore == false && $scope.showSearch == false)
+ {
moreEvents = true;
enableLoadMore = true;
//console.log("REMOVING ARTIFICAL LOAD MORE BLOCK");
}
};
-
//--------------------------------------------------------
// utility function
//--------------------------------------------------------
- function computeRelativePath(event) {
+ function computeRelativePath(event)
+ {
var relativePath = "";
var loginData = NVRDataModel.getLogin();
var str = event.Event.StartTime;
@@ -1511,7 +2395,8 @@ angular.module('zmApp.controllers')
// utility function
//--------------------------------------------------------
- function computeBasePath(event) {
+ function computeBasePath(event)
+ {
var basePath = "";
var loginData = NVRDataModel.getLogin();
var str = event.Event.StartTime;
@@ -1533,43 +2418,44 @@ angular.module('zmApp.controllers')
return basePath;
}
- $scope.modalGraph = function () {
- $ionicModal.fromTemplateUrl('templates/events-modalgraph.html', {
+ $scope.modalGraph = function()
+ {
+ $ionicModal.fromTemplateUrl('templates/events-modalgraph.html',
+ {
scope: $scope, // give ModalCtrl access to this scope
animation: 'slide-in-up',
id: 'modalgraph',
})
- .then(function (modal) {
+ .then(function(modal)
+ {
$scope.modal = modal;
-
-
-
$scope.modal.show();
});
};
- $scope.analyzeEvent = function (ev) {
+ $scope.analyzeEvent = function(ev)
+ {
$scope.event = ev;
- $ionicModal.fromTemplateUrl('templates/timeline-modal.html', {
+ $ionicModal.fromTemplateUrl('templates/timeline-modal.html',
+ {
scope: $scope, // give ModalCtrl access to this scope
animation: 'slide-in-up',
id: 'analyze',
})
- .then(function (modal) {
+ .then(function(modal)
+ {
$scope.modal = modal;
-
-
-
$scope.modal.show();
});
};
- $scope.$on('modal.removed', function (e, m) {
+ $scope.$on('modal.removed', function(e, m)
+ {
if (m.id != 'footage')
return;
@@ -1588,7 +2474,8 @@ angular.module('zmApp.controllers')
//earlier won't work
//--------------------------------------------------------
- $scope.openModal = function (event) {
+ $scope.openModal = function(event)
+ {
NVRDataModel.debug("unbinding eventCtrl watchers as modal has its own");
ionRangeWatcher();
@@ -1602,17 +2489,18 @@ angular.module('zmApp.controllers')
$scope.currentEvent = event;
$scope.followSameMonitor = ($stateParams.id == "0") ? "0" : "1";
-
-
- $ionicModal.fromTemplateUrl('templates/events-modal.html', {
+ $ionicModal.fromTemplateUrl('templates/events-modal.html',
+ {
scope: $scope,
animation: 'slide-in-up',
id: 'footage',
})
- .then(function (modal) {
+ .then(function(modal)
+ {
$scope.modal = modal;
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kPleaseWait') + "...",
noBackdrop: true,
duration: 10000
@@ -1622,8 +2510,6 @@ angular.module('zmApp.controllers')
var ld = NVRDataModel.getLogin();
-
-
});
};
@@ -1632,10 +2518,12 @@ angular.module('zmApp.controllers')
//We need to destroy because we are instantiating
// it on open
//--------------------------------------------------------
- $scope.closeModal = function () {
+ $scope.closeModal = function()
+ {
NVRDataModel.debug(">>>EventCtrl:Close & Destroy Modal");
NVRDataModel.setAwake(false);
- if ($scope.modal !== undefined) {
+ if ($scope.modal !== undefined)
+ {
$scope.modal.remove();
}
@@ -1645,9 +2533,11 @@ angular.module('zmApp.controllers')
//Cleanup the modal when we're done with it
// I Don't think it ever comes here
//--------------------------------------------------------
- $scope.$on('$destroy', function () {
+ $scope.$on('$destroy', function()
+ {
//console.log("Destroy Modal");
- if ($scope.modal !== undefined) {
+ if ($scope.modal !== undefined)
+ {
$scope.modal.remove();
}
if ($scope.popover !== undefined)
@@ -1658,18 +2548,21 @@ angular.module('zmApp.controllers')
// used by infinite scrolling to see if we can get more
//--------------------------------------------------------
- $scope.moreDataCanBeLoaded = function () {
+ $scope.moreDataCanBeLoaded = function()
+ {
return moreEvents;
};
//--------------------------------------------------------
// stop searching for more data
//--------------------------------------------------------
- $scope.cancelSearch = function () {
+ $scope.cancelSearch = function()
+ {
$ionicLoading.hide(); //Or whatever action you want to preform
enableLoadMore = false;
//console.log("**** CANCELLED ****");
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kSearchCancelled'),
animation: 'fade-in',
showBackdrop: true,
@@ -1678,27 +2571,28 @@ angular.module('zmApp.controllers')
showDelay: 0
});
-
};
//--------------------------------------------------------
// loads next page of events
//--------------------------------------------------------
-
- function loadMore() {
+ function loadMore()
+ {
// the events API does not return an error for anything
// except greater page limits than reported
// console.log("***** LOADING MORE INFINITE SCROLL ****");
eventsPage--;
- if ((eventsPage <= 0) && (pageLoaded)) {
+ if ((eventsPage <= 0) && (pageLoaded))
+ {
moreEvents = false;
//console.log("*** At Page " + eventsPage + ", not proceeding");
return;
}
- if (!enableLoadMore) {
+ if (!enableLoadMore)
+ {
moreEvents = false; // Don't ion-scroll till enableLoadMore is true;
$scope.$broadcast('scroll.infiniteScrollComplete');
@@ -1707,9 +2601,11 @@ angular.module('zmApp.controllers')
}
var loadingStr = "";
- if ($scope.search.text != "") {
+ if ($scope.search.text != "")
+ {
var toastStr = $translate.instant('kToastSearchingPage') + eventsPage;
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
maxwidth: 100,
scope: $scope,
template: '<button class="button button-clear icon-left ion-close-circled button-text-wrap" ng-click="cancelSearch()" >' + toastStr + '</button>'
@@ -1726,20 +2622,25 @@ angular.module('zmApp.controllers')
nolangTo = moment($rootScope.toString).locale('en').format("YYYY-MM-DD HH:mm:ss");
NVRDataModel.getEvents($scope.id, eventsPage, loadingStr, nolangFrom, nolangTo)
- .then(function (data) {
+ .then(function(data)
+ {
var loginData = NVRDataModel.getLogin();
// console.log("Got new page of events with Page=" + eventsPage);
var myevents = data;
- for (var i = 0; i < myevents.length; i++) {
+ for (var i = 0; i < myevents.length; i++)
+ {
var idfound = true;
var ld = NVRDataModel.getLogin();
- if (ld.persistMontageOrder) {
+ if (ld.persistMontageOrder)
+ {
idfound = false;
- for (var ii = 0; ii < $scope.monitors.length; ii++) {
- if ($scope.monitors[ii].Monitor.Id == myevents[i].Event.MonitorId && (NVRDataModel.isNotHidden(myevents[i].Event.MonitorId)|| showHiddenMonitors)) {
+ for (var ii = 0; ii < $scope.monitors.length; ii++)
+ {
+ if ($scope.monitors[ii].Monitor.Id == myevents[i].Event.MonitorId && (NVRDataModel.isNotHidden(myevents[i].Event.MonitorId) || showHiddenMonitors))
+ {
//console.log ( $scope.monitors[ii].Monitor.Id + " MATCHES " + myevents[i].Event.MonitorId);
idfound = true;
@@ -1749,7 +2650,6 @@ angular.module('zmApp.controllers')
}
}
-
myevents[i].Event.humanizeTime = humanizeTime(myevents[i].Event.StartTime);
myevents[i].Event.MonitorName = NVRDataModel.getMonitorName(myevents[i].Event.MonitorId);
// now construct base path
@@ -1765,6 +2665,13 @@ angular.module('zmApp.controllers')
myevents[i].Event.BasePath = computeBasePath(myevents[i]);
myevents[i].Event.relativePath = computeRelativePath(myevents[i]);
myevents[i].Event.height = eventsListDetailsHeight;
+
+ if (myevents[i].Event.imageMode == 'path')
+ //if (1)
+ myevents[i].Event.videoPath = myevents[i].Event.baseURL + "/events/" + myevents[i].Event.relativePath + myevents[i].Event.DefaultVideo;
+ else
+ myevents[i].Event.videoPath = myevents[i].Event.baseURL + "/index.php?view=view_video&eid=" + myevents[i].Event.Id;
+
if (idfound) $scope.events.push(myevents[i]);
}
@@ -1773,7 +2680,8 @@ angular.module('zmApp.controllers')
$scope.$broadcast('scroll.infiniteScrollComplete');
},
- function (error) {
+ function(error)
+ {
// console.log("*** No More Events to Load, Stop Infinite Scroll ****");
moreEvents = false;
$scope.$broadcast('scroll.infiniteScrollComplete');
@@ -1781,13 +2689,14 @@ angular.module('zmApp.controllers')
});
}
- $scope.loadMore = function () {
+ $scope.loadMore = function()
+ {
loadMore();
};
- $scope.toggleMinAlarmFrameCount = function () {
-
+ $scope.toggleMinAlarmFrameCount = function()
+ {
var ld = NVRDataModel.getLogin();
@@ -1798,54 +2707,57 @@ angular.module('zmApp.controllers')
doRefresh();
};
-
//--------------------------------------
// formats events dates in a nice way
//---------------------------------------
-
- function humanizeTime(str) {
+ function humanizeTime(str)
+ {
//console.log ("Time:"+str+" TO LOCAL " + moment(str).local().toString());
//if (NVRDataModel.getLogin().useLocalTimeZone)
- return moment.tz(str, NVRDataModel.getTimeZoneNow()).fromNow();
- // else
- // return moment(str).fromNow();
-
+ return moment.tz(str, NVRDataModel.getTimeZoneNow()).fromNow();
+ // else
+ // return moment(str).fromNow();
+
}
- $scope.prettifyDate = function (str) {
+ $scope.prettifyDate = function(str)
+ {
if (NVRDataModel.getLogin().useLocalTimeZone)
return moment.tz(str, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format('MMM Do');
else
- return moment(str).format('MMM Do');
+ return moment(str).format('MMM Do');
};
- function prettifyDate(str) {
+ function prettifyDate(str)
+ {
if (NVRDataModel.getLogin().useLocalTimeZone)
return moment.tz(str, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format('MMM Do');
else
return moment(str).format('MMM Do');
}
- $scope.prettifyTime = function (str) {
+ $scope.prettifyTime = function(str)
+ {
if (NVRDataModel.getLogin().useLocalTimeZone)
return moment.tz(str, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format(NVRDataModel.getTimeFormat());
- else
+ else
return moment(str).format(NVRDataModel.getTimeFormat());
};
- $scope.prettifyTimeSec = function (str) {
+ $scope.prettifyTimeSec = function(str)
+ {
if (NVRDataModel.getLogin().useLocalTimeZone)
return moment.tz(str, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format(NVRDataModel.getTimeFormatSec());
else
return moment(str).format(NVRDataModel.getTimeFormatSec());
};
-
- $scope.prettify = function (str) {
+ $scope.prettify = function(str)
+ {
if (NVRDataModel.getLogin().useLocalTimeZone)
return moment.tz(str, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format(NVRDataModel.getTimeFormat() + ', MMMM Do YYYY');
- else
+ else
return moment(str).format(NVRDataModel.getTimeFormat() + ', MMMM Do YYYY');
};
//--------------------------------------------------------
@@ -1856,31 +2768,33 @@ angular.module('zmApp.controllers')
// a large list
//--------------------------------------------------------
- $scope.dummyDoRefresh = function () {
+ $scope.dummyDoRefresh = function()
+ {
$scope.$broadcast('scroll.refreshComplete');
};
-
- $scope.doRefresh = function () {
+ $scope.doRefresh = function()
+ {
doRefresh();
}; //dorefresh
- function doRefresh() {
+ function doRefresh()
+ {
// console.log("***Pull to Refresh");
NVRDataModel.debug("Reloading monitors");
var refresh = NVRDataModel.getMonitors(1);
- refresh.then(function (data) {
+ refresh.then(function(data)
+ {
$scope.monitors = data;
-
- /* var ld = NVRDataModel.getLogin();
- if (ld.persistMontageOrder) {
- var tempMon = data;
- $scope.monitors = NVRDataModel.applyMontageMonitorPrefs(tempMon, 2)[0];
- } else {
- $scope.monitors = data;
- }*/
+ /* var ld = NVRDataModel.getLogin();
+ if (ld.persistMontageOrder) {
+ var tempMon = data;
+ $scope.monitors = NVRDataModel.applyMontageMonitorPrefs(tempMon, 2)[0];
+ } else {
+ $scope.monitors = data;
+ }*/
getInitialEvents();
moreEvents = true;
@@ -1888,4 +2802,4 @@ angular.module('zmApp.controllers')
});
}
-}]); \ No newline at end of file
+}]);
diff --git a/www/js/EventDateTimeFilterCtrl.js b/www/js/EventDateTimeFilterCtrl.js
index 24df1f52..772b16be 100644
--- a/www/js/EventDateTimeFilterCtrl.js
+++ b/www/js/EventDateTimeFilterCtrl.js
@@ -2,40 +2,46 @@
/* jslint browser: true*/
/* global cordova,StatusBar,angular,console,moment */
-
-
angular.module('zmApp.controllers')
- .controller('zmApp.EventDateTimeFilterCtrl', ['$scope', '$ionicSlideBoxDelegate', '$ionicSideMenuDelegate', '$rootScope', '$ionicHistory', 'NVRDataModel', '$state', function ($scope, $ionicScrollDelegate, $ionicSideMenuDelegate, $rootScope, $ionicHistory, NVRDataModel, $state) {
+ .controller('zmApp.EventDateTimeFilterCtrl', ['$scope', '$ionicSlideBoxDelegate', '$ionicSideMenuDelegate', '$rootScope', '$ionicHistory', 'NVRDataModel', '$state', function($scope, $ionicScrollDelegate, $ionicSideMenuDelegate, $rootScope, $ionicHistory, NVRDataModel, $state)
+ {
//----------------------------------------------------------------
// Alarm notification handling
//----------------------------------------------------------------
- $scope.handleAlarms = function () {
+ $scope.handleAlarms = function()
+ {
$rootScope.isAlarm = !$rootScope.isAlarm;
- if (!$rootScope.isAlarm) {
+ if (!$rootScope.isAlarm)
+ {
$rootScope.alarmCount = "0";
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
- $state.go("events", {
+ $state.go("events",
+ {
"id": 0,
- "playEvent":false
- }, {
+ "playEvent": false
+ },
+ {
reload: true
});
return;
}
};
- $scope.$on('$ionicView.beforeEnter', function () {
+ $scope.$on('$ionicView.beforeEnter', function()
+ {
$scope.today = moment().format("YYYY-MM-DD");
});
-
+
//--------------------------------------------------------------------------
// Clears filters
//--------------------------------------------------------------------------
- $scope.removeFilters = function () {
+ $scope.removeFilters = function()
+ {
$rootScope.isEventFilterOn = false;
$rootScope.fromDate = "";
$rootScope.fromTime = "";
@@ -57,16 +63,17 @@ angular.module('zmApp.controllers')
}
else // in events, backview is undefined?
{
- $ionicHistory.nextViewOptions({
- disableBack: true
- });
- $state.go("events", {
- "id": 0,
- "playEvent":false
- });
- return;
+ $ionicHistory.nextViewOptions(
+ {
+ disableBack: true
+ });
+ $state.go("events",
+ {
+ "id": 0,
+ "playEvent": false
+ });
+ return;
}
-
//$ionicHistory.goBack();
};
@@ -75,51 +82,50 @@ angular.module('zmApp.controllers')
// Saves filters in root variables so EventFilter can access it. I know:
// don't root.
//--------------------------------------------------------------------------
- $scope.saveFilters = function () {
- if (!$rootScope.fromDate) {
+ $scope.saveFilters = function()
+ {
+ if (!$rootScope.fromDate)
+ {
//console.log("RESET fromDate");
$rootScope.fromDate = new Date();
NVRDataModel.debug("DateTimeFilter: resetting from date");
}
- if (!$rootScope.toDate) {
+ if (!$rootScope.toDate)
+ {
// console.log("RESET toDate");
$rootScope.toDate = new Date();
NVRDataModel.debug("DateTimeFilter: resetting to date");
}
- if (!$rootScope.fromTime) {
+ if (!$rootScope.fromTime)
+ {
// console.log("RESET fromTime");
$rootScope.fromTime = new Date(99, 5, 24, 0, 0, 0, 0); //moment().format("hh:mm:ss");
NVRDataModel.debug("DateTimeFilter: resetting from time");
}
-
- if (!$rootScope.toTime) {
+ if (!$rootScope.toTime)
+ {
//console.log("RESET toTime");
$rootScope.toTime = new Date(99, 5, 24, 23, 59, 59, 0);
//$rootScope.toTime = "01:01:02"; //moment().format("hh:mm:ss");
NVRDataModel.debug("DateTimeFilter: resetting to time");
}
-
if ($rootScope.fromDate > $rootScope.toDate)
{
- NVRDataModel.log ("From date > To Date, swapping");
+ NVRDataModel.log("From date > To Date, swapping");
var t = $rootScope.fromDate;
$rootScope.fromDate = $rootScope.toDate;
$rootScope.toDate = t;
}
-
+
$rootScope.isEventFilterOn = true;
$rootScope.fromString = moment($rootScope.fromDate).format("YYYY-MM-DD") + " " + moment($rootScope.fromTime).format("HH:mm:ss");
$rootScope.toString = moment($rootScope.toDate).format("YYYY-MM-DD") + " " + moment($rootScope.toTime).format("HH:mm:ss");
-
-
-
-
//console.log("CONCAT DATES " + temp);
//
// var startDate = moment(temp).format("YYYY-MM-DD hh:mm:ss");
@@ -127,7 +133,6 @@ angular.module('zmApp.controllers')
$ionicHistory.goBack();
};
+ }
-}
-
-]); \ No newline at end of file
+ ]);
diff --git a/www/js/EventModalCtrl.js b/www/js/EventModalCtrl.js
index abdf5052..c1633d1f 100644
--- a/www/js/EventModalCtrl.js
+++ b/www/js/EventModalCtrl.js
@@ -3,11 +3,8 @@
/* jslint browser: true*/
/* global saveAs, cordova,StatusBar,angular,console,ionic, moment, Chart */
-
-
-
-angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$rootScope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$q', '$sce', 'carouselUtils', '$ionicPopup', '$translate', function ($scope, $rootScope, zm, NVRDataModel, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $stateParams, $ionicHistory, $ionicScrollDelegate, $q, $sce, carouselUtils, $ionicPopup, $translate) {
-
+angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$rootScope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$q', '$sce', 'carouselUtils', '$ionicPopup', '$translate', '$filter', 'SecuredPopups', function($scope, $rootScope, zm, NVRDataModel, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $stateParams, $ionicHistory, $ionicScrollDelegate, $q, $sce, carouselUtils, $ionicPopup, $translate, $filter, SecuredPopups)
+{
// from parent scope
var currentEvent = $scope.currentEvent;
@@ -16,22 +13,24 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
$scope.loginData = NVRDataModel.getLogin();
$scope.currentRate = '-';
var timeFormat = 'MM/DD/YYYY HH:mm:ss';
-
+ var event;
+ var gEvent;
+ var handle;
var framearray = {
labels: [],
- datasets: [{
+ datasets: [
+ {
//label: '# of Votes',
backgroundColor: 'rgba(242, 12, 12, 0.5)',
borderColor: 'rgba(242, 12, 12, 0.5)',
data: [],
- }]
+ }]
};
var frameoptions = [];
-
var eventImageDigits = 5; // failsafe
$scope.currentProgress = {
progress: 0
@@ -40,7 +39,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
progress: 0
};
NVRDataModel.getKeyConfigParams(0)
- .then(function (data) {
+ .then(function(data)
+ {
//console.log ("***GETKEY: " + JSON.stringify(data));
eventImageDigits = parseInt(data);
NVRDataModel.log("Image padding digits reported as " + eventImageDigits);
@@ -54,8 +54,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
$scope.showModalRangeSider = false;
$scope.isModalActive = true;
-
- $timeout(function () {
+ $timeout(function()
+ {
$scope.showModalRangeSider = true;
}, 2000);
@@ -64,7 +64,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
document.addEventListener("resume", onResume, false);
$rootScope.authSession = "undefined";
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kNegotiatingStreamAuth'),
animation: 'fade-in',
showBackdrop: true,
@@ -74,116 +75,157 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
});
var ld = NVRDataModel.getLogin();
-
$scope.currentStreamMode = ld.gapless ? 'gapless' : 'single';
NVRDataModel.log("Using stream mode " + $scope.currentStreamMode);
NVRDataModel.debug("EventModalCtrl called from " + $ionicHistory.currentStateName());
// This is not needed for event mode
-
-
NVRDataModel.debug("Setting playback to " + $scope.streamMode);
-
$rootScope.validMonitorId = $scope.monitors[0].Monitor.Id;
NVRDataModel.getAuthKey($rootScope.validMonitorId, (Math.floor((Math.random() * 999999) + 1)).toString())
- .then(function (success) {
+ .then(function(success)
+ {
$ionicLoading.hide();
$rootScope.authSession = success;
NVRDataModel.log("Modal: Stream authentication construction: " + $rootScope.authSession);
},
- function (error) {
+ function(error)
+ {
$ionicLoading.hide();
NVRDataModel.debug("ModalCtrl: Error details of stream auth:" + error);
//$rootScope.authSession="";
NVRDataModel.log("Modal: Error returned Stream authentication construction. Retaining old value of: " + $rootScope.authSession);
});
-
-
-
+
//--------------------------------------------------------------------------------------
// Handles bandwidth change, if required
//
//--------------------------------------------------------------------------------------
- $rootScope.$on("bandwidth-change", function (e,data) {
- // not called for offline, I'm only interested in BW switches
+ $rootScope.$on("bandwidth-change", function(e, data)
+ {
+ // not called for offline, I'm only interested in BW switches
NVRDataModel.debug("Got network change:" + data);
var ds;
- if (data == 'lowbw') {
+ if (data == 'lowbw')
+ {
ds = $translate.instant('kLowBWDisplay');
- } else {
+ }
+ else
+ {
ds = $translate.instant('kHighBWDisplay');
}
NVRDataModel.displayBanner('net', [ds]);
var ld = NVRDataModel.getLogin();
-
- $scope.singleImageQuality = (NVRDataModel.getBandwidth()=="lowbw" )? zm.eventSingleImageQualityLowBW: ld.singleImageQuality;
- });
-
-
+ $scope.singleImageQuality = (NVRDataModel.getBandwidth() == "lowbw") ? zm.eventSingleImageQualityLowBW : ld.singleImageQuality;
+ });
//-------------------------------------------------------
// we use this to reload the connkey if authkey changed
//------------------------------------------------------
-
- $rootScope.$on("auth-success", function () {
+ $rootScope.$on("auth-success", function()
+ {
NVRDataModel.debug("EventModalCtrl: Re-login detected, resetting everything & re-generating connkey");
NVRDataModel.stopNetwork("Auth-Success inside EventModalCtrl");
$scope.connKey = (Math.floor((Math.random() * 999999) + 1)).toString();
//console.log ("********* OFFSET FROM AUTH SUCC");
- $timeout(function () {
+ $timeout(function()
+ {
sendCommand('14', $scope.connKey, '&offset=' + $scope.currentProgress.progress);
}, 500);
//$timeout.cancel(eventQueryHandle);
//eventQueryHandle = $timeout (function(){checkEvent();}, zm.eventPlaybackQuery);
-
-
});
//-------------------------------------------------------
// tap to pause
//------------------------------------------------------
- $scope.togglePause = function () {
+ $scope.togglePause = function()
+ {
$scope.isPaused = !$scope.isPaused;
NVRDataModel.debug("Paused is " + $scope.isPaused);
-
-
sendCommand($scope.isPaused ? '1' : '2', $scope.connKey);
};
-
- $scope.onPlayerReady = function(handle)
+ $scope.onPlayerReady = function(api)
{
// we need this timeout to avoid load interrupting
// play -- I suppose its an angular digest foo thing
- NVRDataModel.debug ("Player is ready");
- $timeout (function() {handle.play();},400);
-
- // window.stop();
+ console.log ("*********** ON PLAY READY");
+ handle = api;
+
+ $ionicLoading.show(
+ {
+ template: "<ion-spinner icon='ripple' class='spinner-energized'></ion-spinner><br/>" + $translate.instant('kVideoLoading')+"...",
+
+ });
+ NVRDataModel.debug("Player is ready");
+ $timeout(function()
+ {
+ handle.pause();
+ handle.setPlayback(2);
+ handle.play();
+
+ }, 300);
+
+ // window.stop();
};
- $scope.onCanPlay = function ()
+ $scope.onCanPlay = function()
{
- NVRDataModel.debug ("This video can be played");
+
+ console.log ("*********** CAN PLAY");
+ $ionicLoading.hide();
+ NVRDataModel.debug("This video can be played");
+ $scope.videoObject.config.cuepoints.points = [];
+ // now set up cue points
+ NVRDataModel.debug("Setting cue points..");
+ NVRDataModel.debug ("API-Total length:"+currentEvent.Event.Length);
+ NVRDataModel.debug ("Player-Total length:"+handle.totalTime/1000);
+
+ for (var l=0; l<currentEvent.Frame.length; l++ )
+ {
+ if (currentEvent.Frame[l].Type=='Alarm')
+ {
+ // var ft = moment(currentEvent.Frame[l].TimeStamp);
+ //var s = factor*Math.abs(st.diff(ft,'seconds'));
+
+ var s = currentEvent.Frame[l].Delta;
+
+ //console.log("START="+currentEvent.Event.StartTime);
+ //console.log("END="+currentEvent.Frame[l].TimeStamp);
+ NVRDataModel.debug ("alarm cue at:"+s+"s");
+ $scope.videoObject.config.cuepoints.points.push({time:s});
+ }
+ }
};
- $scope.onVideoError = function (event)
+ $scope.onVideoError = function(event)
{
- NVRDataModel.debug ("player reported a video error:"+JSON.stringify(event));
- };
+ $ionicLoading.hide();
+ if (!$scope.isModalActive) return;
+ NVRDataModel.debug("player reported a video error:" + JSON.stringify(event));
+ $rootScope.zmPopup = SecuredPopups.show('alert',
+ {
+ title: $translate.instant('kError'),
+ template: $rootScope.platformOS == 'desktop' ? $translate.instant('kVideoError') : $translate.instant('kVideoErrorMobile'),
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
+ });
+ };
//-------------------------------------------------------
// This is what we call every zm.EventQueryInterval
@@ -191,57 +233,64 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
// zms takes care of the display
//------------------------------------------------------
- function checkEvent() {
-
+ function checkEvent()
+ {
+
if ($scope.modalFromTimelineIsOpen == false)
{
- NVRDataModel.log ("Modal was closed in timeline, cancelling timer");
+ NVRDataModel.log("Modal was closed in timeline, cancelling timer");
$interval.cancel(eventQueryHandle);
return;
}
-
+
//console.log ("Event timer");
//console.log ("Event timer");
$scope.checkEventOn = true;
- if ($scope.defaultVideo !== undefined && $scope.defaultVideo != '') {
+ if ($scope.defaultVideo !== undefined && $scope.defaultVideo != '')
+ {
//console.log("playing video, not using zms, skipping event commands");
- } else {
+ }
+ else
+ {
processEvent('99', $scope.connKey);
}
}
-
- function sendCommand(cmd, connkey, extras, rq) {
+ function sendCommand(cmd, connkey, extras, rq)
+ {
var d = $q.defer();
- if ($scope.defaultVideo !== undefined && $scope.defaultVideo != '') {
+ if ($scope.defaultVideo !== undefined && $scope.defaultVideo != '')
+ {
// console.log("playing video, not using zms, skipping event commands");
d.resolve(true);
return (d.promise);
}
-
-
var loginData = NVRDataModel.getLogin();
//console.log("Sending CGI command to " + loginData.url);
var rqtoken = rq ? rq : "stream";
var myauthtoken = $rootScope.authSession.replace("&auth=", "");
//&auth=
- $http({
+ $http(
+ {
method: 'POST',
/*timeout: 15000,*/
url: loginData.url + '/index.php',
- headers: {
+ headers:
+ {
'Content-Type': 'application/x-www-form-urlencoded',
//'Accept': '*/*',
},
- transformRequest: function (obj) {
+ transformRequest: function(obj)
+ {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" +
encodeURIComponent(obj[p]));
var foo = str.join("&");
- if (extras) {
+ if (extras)
+ {
foo = foo + extras;
//console.log("EXTRAS****SUB RETURNING " + foo);
}
@@ -250,7 +299,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
},
- data: {
+ data:
+ {
view: "request",
request: rqtoken,
connkey: connkey,
@@ -260,14 +310,15 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
// pass: loginData.password
}
})
- .then(function (resp) {
+ .then(function(resp)
+ {
NVRDataModel.debug("sendCmd response:" + JSON.stringify(resp));
d.resolve(resp);
return (d.promise);
-
},
- function (resp) {
+ function(resp)
+ {
NVRDataModel.debug("sendCmd error:" + JSON.stringify(resp));
d.reject(resp);
return (d.promise);
@@ -276,10 +327,11 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
return (d.promise);
}
+ function processEvent(cmd, connkey)
+ {
- function processEvent(cmd, connkey) {
-
- if ($scope.blockSlider) {
+ if ($scope.blockSlider)
+ {
//console.log("Not doing ZMS Command as slider is depressed...");
return;
}
@@ -288,15 +340,18 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
//console.log("sending process Event command to " + loginData.url);
var myauthtoken = $rootScope.authSession.replace("&auth=", "");
//&auth=
- var req = $http({
+ var req = $http(
+ {
method: 'POST',
/*timeout: 15000,*/
url: loginData.url + '/index.php',
- headers: {
+ headers:
+ {
'Content-Type': 'application/x-www-form-urlencoded',
//'Accept': '*/*',
},
- transformRequest: function (obj) {
+ transformRequest: function(obj)
+ {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" +
@@ -306,7 +361,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
return foo;
},
- data: {
+ data:
+ {
view: "request",
request: "stream",
connkey: connkey,
@@ -317,17 +373,18 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
}
});
- req.success(function (resp) {
+ req.success(function(resp)
+ {
// NVRDataModel.debug ("processEvent success:"+JSON.stringify(resp));
- if (resp.result == "Ok") {
+ if (resp.result == "Ok")
+ {
$scope.currentProgress.progress = resp.status.progress;
$scope.eventId = resp.status.event;
$scope.d_eventId = $scope.eventId;
$scope.currentRate = resp.status.rate;
-
if ($scope.currentProgress.progress > $scope.currentEventDuration) $scope.currentProgress.progress = $scope.currentEventDuration;
$scope.progressText = "At " + $scope.currentProgress.progress + "s of " + $scope.currentEventDuration + "s";
@@ -337,12 +394,11 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
// as this code conflicts with fast rev etc
//if (Math.floor(resp.status.progress) >=$scope.currentEventDuration)
-
-
//$timeout (checkEvent(), zm.eventPlaybackQuery);
//eventQueryHandle = $timeout (function(){checkEvent();}, zm.eventPlaybackQuery);
- } else // resp.result was messed up
+ }
+ else // resp.result was messed up
{
NVRDataModel.debug("Hmm I found an error " + JSON.stringify(resp));
@@ -350,7 +406,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
$scope.connKey = (Math.floor((Math.random() * 999999) + 1)).toString();
// console.log (JSON.stringify(resp));
- $timeout(function () {
+ $timeout(function()
+ {
sendCommand('14', $scope.connKey, '&offset=' + $scope.currentProgress.progress);
}, 500);
NVRDataModel.debug("so I'm regenerating Connkey to " + $scope.connKey);
@@ -358,8 +415,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
}
});
-
- req.error(function (resp) {
+ req.error(function(resp)
+ {
NVRDataModel.debug("processEvent error:" + JSON.stringify(resp));
//eventQueryHandle = $timeout (function(){checkEvent();}, zm.eventPlaybackQuery);
@@ -367,9 +424,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
}
-
-
- function onPause() {
+ function onPause()
+ {
// $interval.cancel(modalIntervalHandle);
@@ -379,53 +435,50 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
}
-
- function onResume() {
+ function onResume()
+ {
NVRDataModel.debug("EventModalCtrl: Modal resume called");
-
-
$rootScope.modalRand = Math.floor((Math.random() * 100000) + 1);
}
-
-
-
-
-
-
-
- $scope.finishedLoadingImage = function () {
+ $scope.finishedLoadingImage = function()
+ {
// console.log("***Monitor image FINISHED Loading***");
$ionicLoading.hide();
};
- $scope.enableSliderBlock = function () {
+ $scope.enableSliderBlock = function()
+ {
$scope.blockSlider = true;
};
- $scope.youChangedSlider = function () {
+ $scope.youChangedSlider = function()
+ {
//console.log("YOU changed " + $scope.sliderProgress.progress);
$scope.currentProgress.progress = $scope.sliderProgress.progress;
sendCommand('14', $scope.connKey, '&offset=' + $scope.currentProgress.progress)
- .then (function (s) { $scope.blockSlider = false; }, function (e) {$scope.blockSlider = false;});
-
-
- };
-
-
-
+ .then(function(s)
+ {
+ $scope.blockSlider = false;
+ }, function(e)
+ {
+ $scope.blockSlider = false;
+ });
+ };
//-----------------------------------------------------------------------
// Sucess/Error handlers for saving a snapshot of the
// monitor image to phone storage
//-----------------------------------------------------------------------
- function SaveSuccess() {
- $ionicLoading.show({
+ function SaveSuccess()
+ {
+ $ionicLoading.show(
+ {
template: $translate.instant('kDone'),
noBackdrop: true,
duration: 1000
@@ -433,8 +486,10 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
NVRDataModel.debug("ModalCtrl:Photo saved successfuly");
}
- function SaveError(e) {
- $ionicLoading.show({
+ function SaveError(e)
+ {
+ $ionicLoading.show(
+ {
template: $translate.instant('kErrorSave'),
noBackdrop: true,
duration: 2000
@@ -443,79 +498,82 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
//console.log("***ERROR");
}
-
- $scope.jumpToOffsetInEvent = function () {
+ $scope.jumpToOffsetInEvent = function()
+ {
// streamReq.send( streamParms+"&command="+CMD_SEEK+"&offset="+offset );
};
-
-
//-----------------------------------------------------------------------
// Saves a snapshot of the monitor image to phone storage
//-----------------------------------------------------------------------
-
- $scope.saveEventImageToPhoneWithPerms = function (onlyAlarms)
+ $scope.saveEventImageToPhoneWithPerms = function(onlyAlarms)
{
-
+
if ($rootScope.platformOS != 'android')
{
processSaveEventImageToPhone(onlyAlarms);
return;
}
-
+
// if we are on android do the 6.x+ hasPermissions flow
NVRDataModel.debug("EventModalCtrl: Permission checking for write");
var permissions = cordova.plugins.permissions;
permissions.hasPermission(permissions.WRITE_EXTERNAL_STORAGE, checkPermissionCallback, null);
-
- function checkPermissionCallback(status) {
+
+ function checkPermissionCallback(status)
+ {
if (!status.hasPermission)
{
SaveError("No permission to write to external storage");
}
- permissions.requestPermission(permissions.WRITE_EXTERNAL_STORAGE, succ,err);
+ permissions.requestPermission(permissions.WRITE_EXTERNAL_STORAGE, succ, err);
}
-
+
function succ(s)
{
processSaveEventImageToPhone(onlyAlarms);
}
+
function err(e)
{
- SaveError ("Error in requestPermission");
+ SaveError("Error in requestPermission");
}
};
-
-
- function processSaveEventImageToPhone (onlyAlarms) {
- if ($scope.loginData.useNphZmsForEvents) {
+ function processSaveEventImageToPhone(onlyAlarms)
+ {
+
+ if ($scope.loginData.useNphZmsForEvents)
+ {
NVRDataModel.log("Use ZMS stream to save to phone");
saveEventImageToPhoneZms(onlyAlarms);
-
- } else {
+ }
+ else
+ {
saveEventImageToPhone(onlyAlarms);
}
-
}
- function saveEventImageToPhoneZms(onlyAlarms) {
+ function saveEventImageToPhoneZms(onlyAlarms)
+ {
// The strategy here is to build the array now so we can grab frames
// $scope.currentProgress.progress is the seconds where we are
// $scope.eventId is the event Id
$scope.isPaused = true;
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kPleaseWait'),
noBackdrop: true,
duration: zm.httpTimeout
});
sendCommand('1', $scope.connKey).
- then(function (resp) {
+ then(function(resp)
+ {
// console.log ("PAUSE ANSWER IS " + JSON.stringify(resp));
$scope.currentProgress.progress = resp.data.status.progress;
@@ -525,9 +583,10 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
var apiurl = $scope.loginData.apiurl + "/events/" + $scope.eventId + ".json";
NVRDataModel.debug("prepared to get frame details using " + apiurl);
$http.get(apiurl)
- .then(function (success) {
+ .then(function(success)
+ {
- var event = success.data.event;
+ event = success.data.event;
event.Event.BasePath = computeBasePath(event);
event.Event.relativePath = computeRelativePath(event);
@@ -551,41 +610,48 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
$scope.mycarousel.index = myFrame;
// console.log ("STEP 1 : Computed index as "+ $scope.mycarousel.index);
var i;
- for (i = 1; i <= event.Frame.length; i++) {
- var fname = padToN(event.Frame[i-1].FrameId, eventImageDigits) + "-capture.jpg";
+ for (i = 1; i <= event.Frame.length; i++)
+ {
+ var fname = padToN(event.Frame[i - 1].FrameId, eventImageDigits) + "-capture.jpg";
// console.log ("Building " + fname);
// console.log ("DUMPING ONE " + JSON.stringify(event.Frame[i-1]));
// onlyAlarms means only copy alarmed frames
- if (onlyAlarms) {
- if (event.Frame[i - 1] && event.Frame[i - 1].Type == 'Alarm') {
- $scope.slides.push({
- id: event.Frame[i-1].FrameId,
+ if (onlyAlarms)
+ {
+ if (event.Frame[i - 1] && event.Frame[i - 1].Type == 'Alarm')
+ {
+ $scope.slides.push(
+ {
+ id: event.Frame[i - 1].FrameId,
img: fname,
});
//console.log ("ALARM PUSHED " + fname);
}
- } else // push all frames
+ }
+ else // push all frames
{
- $scope.slides.push({
- id: event.Frame[i-1].FrameId,
+ $scope.slides.push(
+ {
+ id: event.Frame[i - 1].FrameId,
img: fname,
});
//console.log ("PUSHED " + fname);
}
-
+
}
// console.log ("STEP 2 : calling Save Event To Phone");
$ionicLoading.hide();
saveEventImageToPhone(onlyAlarms);
-
},
- function (err) {
+ function(err)
+ {
$ionicLoading.hide();
NVRDataModel.log("snapshot API Error: Could not get frames " + JSON.stringify(err));
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kErrorRetrievingFrames'),
noBackdrop: true,
duration: 4000
@@ -593,18 +659,18 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
});
},
- function (err) {
+ function(err)
+ {
NVRDataModel.debug("Error pausing stream before snapshot " + JSON.stringify(err));
$ionicLoading.hide();
}
); // then
-
}
-
- function saveEventImageToPhone(onlyAlarms) {
+ function saveEventImageToPhone(onlyAlarms)
+ {
var curState = carouselUtils.getStop();
carouselUtils.setStop(true);
@@ -615,20 +681,18 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
var canvas, context, imageDataUrl, imageData;
var loginData = NVRDataModel.getLogin();
-
// for alarms only
if (onlyAlarms) $scope.mycarousel.index = 0;
var url = $scope.playbackURL + '/index.php?view=image&rand=' + $rootScope.rand + "&path=" + $scope.relativePath + $scope.slides[$scope.mycarousel.index].img;
-
$scope.selectEventUrl = url;
$scope.slideIndex = $scope.mycarousel.index;
$scope.slideLastIndex = $scope.slides.length - 1;
- // console.log ("URL TO DISPLAY " + url);
-
+ // console.log ("URL TO DISPLAY " + url);
- $rootScope.zmPopup = $ionicPopup.show({
+ $rootScope.zmPopup = $ionicPopup.show(
+ {
template: '<center>Frame: {{slideIndex+1}} / {{slideLastIndex+1}}</center><br/><img src="{{selectEventUrl}}" width="100%" />',
title: 'Select ' + (onlyAlarms ? 'Alarmed ' : '') + 'frame to save',
subTitle: 'use left and right arrows to change',
@@ -639,13 +703,14 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
// left 1
text: '',
type: 'button-small button-energized ion-chevron-left',
- onTap: function (e) {
+ onTap: function(e)
+ {
if ($scope.slideIndex > 0) $scope.slideIndex--;
$scope.selectEventUrl = $scope.playbackURL + '/index.php?view=image&rand=' + $rootScope.rand + "&path=" + $scope.relativePath + $scope.slides[$scope.slideIndex].img;
//NVRDataModel.log("selected frame is " + $scope.slideIndex);
- console.log ("URL TO DISPLAY " + $scope.slides[$scope.slideIndex].img);
-
+ console.log("URL TO DISPLAY " + $scope.slides[$scope.slideIndex].img);
+
e.preventDefault();
}
},
@@ -653,11 +718,12 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
// right 1
text: '',
type: 'button-small button-energized ion-chevron-right',
- onTap: function (e) {
+ onTap: function(e)
+ {
if ($scope.slideIndex < $scope.slideLastIndex) $scope.slideIndex++;
$scope.selectEventUrl = $scope.playbackURL + '/index.php?view=image&rand=' + $rootScope.rand + "&path=" + $scope.relativePath + $scope.slides[$scope.slideIndex].img;
//NVRDataModel.log("selected frame is " + $scope.slideIndex);
- console.log ("URL TO DISPLAY " + $scope.slides[$scope.slideIndex].img);
+ console.log("URL TO DISPLAY " + $scope.slides[$scope.slideIndex].img);
e.preventDefault();
}
},
@@ -665,7 +731,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
// left 10
text: '',
type: 'button-small button-energized ion-skip-backward',
- onTap: function (e) {
+ onTap: function(e)
+ {
var tempVar = $scope.slideIndex;
tempVar -= 10;
if (tempVar < 0) tempVar = 0;
@@ -681,7 +748,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
// right 10
text: '',
type: 'button-small button-energized ion-skip-forward',
- onTap: function (e) {
+ onTap: function(e)
+ {
var tempVar = $scope.slideIndex;
tempVar += 10;
if (tempVar > $scope.slideLastIndex) tempVar = $scope.slideLastIndex;
@@ -700,15 +768,19 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
{
text: '',
type: 'button-positive button-small ion-checkmark-round',
- onTap: function (e) {
+ onTap: function(e)
+ {
saveNow();
}
- }]
+ }
+ ]
});
- function saveNow() {
- $ionicLoading.show({
+ function saveNow()
+ {
+ $ionicLoading.show(
+ {
template: $translate.instant('kSavingSnapshot') + "...",
noBackdrop: true,
duration: zm.httpTimeout
@@ -717,7 +789,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
NVRDataModel.log("saveNow: File path to grab is " + url);
var img = new Image();
- img.onload = function () {
+ img.onload = function()
+ {
// console.log("********* ONLOAD");
canvas = document.createElement('canvas');
canvas.width = img.width;
@@ -728,8 +801,10 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
imageDataUrl = canvas.toDataURL('image/jpeg', 1.0);
imageData = imageDataUrl.replace(/data:image\/jpeg;base64,/, '');
- if ($rootScope.platformOS != "desktop") {
- try {
+ if ($rootScope.platformOS != "desktop")
+ {
+ try
+ {
cordova.exec(
SaveSuccess,
@@ -738,70 +813,77 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
'saveImageDataToLibrary', [imageData]
);
// carouselUtils.setStop(curState);
- } catch (e) {
+ }
+ catch (e)
+ {
SaveError(e.message);
// carouselUtils.setStop(curState);
}
- } else {
-
+ }
+ else
+ {
var fname = $scope.relativePath + $scope.slides[$scope.slideIndex].img + ".png";
fname = fname.replace(/\//, "-");
fname = fname.replace(/\.jpg/, '');
- canvas.toBlob(function (blob) {
+ canvas.toBlob(function(blob)
+ {
saveAs(blob, fname);
SaveSuccess();
});
}
};
- try {
+ try
+ {
img.src = url;
// console.log ("SAVING IMAGE SOURCE");
- } catch (e) {
+ }
+ catch (e)
+ {
SaveError(e.message);
}
}
}
-
- $scope.reloadView = function () {
+ $scope.reloadView = function()
+ {
NVRDataModel.log("Reloading view for modal view, recomputing rand");
$rootScope.modalRand = Math.floor((Math.random() * 100000) + 1);
$scope.isModalActive = true;
};
- $scope.scaleImage = function () {
+ $scope.scaleImage = function()
+ {
$scope.imageFit = !$scope.imageFit;
- // console.log("Switching image style to " + $scope.imageFit);
+ console.log("Switching image style to " + $scope.imageFit);
};
- $scope.$on('$ionicView.enter', function () {
+ $scope.$on('$ionicView.enter', function()
+ {
//console.log (">>>>>>>>>>>>>>>>>>>> MODAL VIEW ENTER");
-
-
});
- $scope.$on('modal.shown', function (e, m) {
+ $scope.$on('modal.shown', function(e, m)
+ {
if (m.id != 'footage')
return;
+ $scope.videoDynamicTime = "";
$scope.videoIsReady = false;
var ld = NVRDataModel.getLogin();
$scope.loginData = NVRDataModel.getLogin();
- $scope.singleImageQuality = (NVRDataModel.getBandwidth()=="lowbw" )? zm.eventSingleImageQualityLowBW: ld.singleImageQuality;
+ $scope.singleImageQuality = (NVRDataModel.getBandwidth() == "lowbw") ? zm.eventSingleImageQualityLowBW : ld.singleImageQuality;
$scope.blockSlider = false;
$scope.checkEventOn = false;
//$scope.singleImageQuality = 100;
-
-
//$scope.commandURL = $scope.currentEvent.Event.baseURL+"/index.php";
// NVRDataModel.log (">>>>>>>>>>>>>>>>>>ZMS url command is " + $scope.commandURL);
@@ -813,26 +895,36 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
$scope.currentFrame = 1;
$scope.isPaused = false;
+
+ gEvent = $scope.currentEvent;
//console.log ("CURRENT EVENT " + JSON.stringify($scope.currentEvent));
+ //
$scope.currentEventDuration = Math.floor($scope.currentEvent.Event.Length);
//console.log ($scope.event.Event.Frames);
- if (currentEvent && currentEvent.Event) {
+ if (currentEvent && currentEvent.Event)
+ {
//console.log ("************ CALLING PREPARE MODAL ***********");
prepareModalEvent(currentEvent.Event.Id);
- if (ld.useNphZmsForEvents) {
- $timeout(function () {
+ if (ld.useNphZmsForEvents)
+ {
+ $timeout(function()
+ {
- if ($scope.modal != undefined && $scope.modal.isShown()) {
+ if ($scope.modal != undefined && $scope.modal.isShown())
+ {
NVRDataModel.log(">>>Starting checkAllEvents interval...");
//eventQueryHandle = $timeout (checkEvent(), zm.eventPlaybackQuery);
$interval.cancel(eventQueryHandle);
- eventQueryHandle = $interval(function () {
+ eventQueryHandle = $interval(function()
+ {
checkEvent();
// console.log ("Refreshing Image...");
- }.bind(this), (NVRDataModel.getBandwidth()=="lowbw")? zm.eventPlaybackQueryLowBW: zm.eventPlaybackQuery);
- } else {
+ }.bind(this), (NVRDataModel.getBandwidth() == "lowbw") ? zm.eventPlaybackQueryLowBW : zm.eventPlaybackQuery);
+ }
+ else
+ {
NVRDataModel.log(">>>Modal was exited, not starting checkAllEvents");
}
@@ -841,51 +933,53 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
}
-
-
-
-
-
});
//var current_data;
- function drawGraph() {
-
+ function drawGraph()
+ {
var cv = document.getElementById("eventchart");
var ctx = cv.getContext("2d");
-
frameoptions = {
responsive: true,
legend: false,
- title: {
+ title:
+ {
display: false,
text: ""
},
- scales: {
- yAxes: [{
+ scales:
+ {
+ yAxes: [
+ {
display: false,
- scaleLabel: {
+ scaleLabel:
+ {
display: false,
labelString: 'value',
}
- }],
- xAxes: [{
+ }],
+ xAxes: [
+ {
type: 'time',
display: false,
- time: {
+ time:
+ {
format: timeFormat,
tooltipFormat: 'll HH:mm',
min: framearray.datasets[0].data[0].x,
max: framearray.datasets[0].data[framearray.datasets[0].data.length - 1].x,
- displayFormats: {
+ displayFormats:
+ {
}
},
- scaleLabel: {
+ scaleLabel:
+ {
display: false,
labelString: ''
}
@@ -894,11 +988,11 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
}
};
+ $timeout(function()
+ {
- $timeout(function () {
-
-
- var myChart = new Chart(ctx, {
+ var myChart = new Chart(ctx,
+ {
type: 'line',
data: framearray,
options: frameoptions,
@@ -907,14 +1001,33 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
});
}
+ $scope.videoTime = function(s, c)
+ {
+ var a, o;
+ if (NVRDataModel.getLogin().useLocalTimeZone)
+ {
+ a = moment.tz(s, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess());
+
+ }
+ else
+ {
+ a = moment(s);
+ }
+ a.add(c);
+
+ o = a.format("MMM Do " + NVRDataModel.getTimeFormatSec());
+ $scope.videoDynamicTime = o;
+ //return a.format("MMM Do "+o);
+
+ };
- $scope.$on('modal.removed', function (e, m) {
- console.log ("************* REMOVE CALLED");
+ $scope.$on('modal.removed', function(e, m)
+ {
+ console.log("************* REMOVE CALLED");
$interval.cancel(eventQueryHandle);
if (m.id != 'footage')
return;
-
$scope.isModalActive = false;
NVRDataModel.debug("Modal removed - killing connkey");
@@ -924,13 +1037,15 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
// Execute action
});
-
// Playback speed adjuster
- $scope.adjustSpeed = function (val) {
+ $scope.adjustSpeed = function(val)
+ {
- if ($scope.defaultVideo !== undefined && $scope.defaultVideo != '') {
+ if ($scope.defaultVideo !== undefined && $scope.defaultVideo != '')
+ {
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kUseVideoControls'),
noBackdrop: true,
duration: 3000
@@ -940,11 +1055,13 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
var ld = NVRDataModel.getLogin();
- if (ld.useNphZmsForEvents) {
+ if (ld.useNphZmsForEvents)
+ {
var cmd;
$scope.isPaused = false;
- switch (val) {
+ switch (val)
+ {
case 'ff':
cmd = 4;
break;
@@ -962,28 +1079,32 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
cmd = 0;
}
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kPleaseWait') + "...",
noBackdrop: true,
duration: zm.httpTimeout
});
sendCommand(cmd, $scope.connKey)
- .then(function (success) {
+ .then(function(success)
+ {
$ionicLoading.hide();
},
- function (err) {
+ function(err)
+ {
$ionicLoading.hide();
NVRDataModel.debug("Error in adjust speed: " + JSON.stringify(err));
}
);
-
- } else // not using nph
+ }
+ else // not using nph
{
- switch (val) {
+ switch (val)
+ {
case "super":
$scope.eventSpeed = 20 / $scope.event.Event.Frames;
@@ -1011,7 +1132,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
}
NVRDataModel.debug("Set playback speed to " + $scope.eventSpeed);
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kPlaybackInterval') + ': ' + $scope.eventSpeed.toFixed(3) + "ms",
animation: 'fade-in',
showBackdrop: false,
@@ -1021,12 +1143,10 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
});
}
-
-
};
-
- $scope.toggleGapless = function () {
+ $scope.toggleGapless = function()
+ {
// console.log(">>>>>>>>>>>>>>GAPLESS TOGGLE INSIDE MODAL");
$scope.loginData.gapless = !$scope.loginData.gapless;
NVRDataModel.setLogin($scope.loginData);
@@ -1036,18 +1156,18 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
NVRDataModel.debug("Regenerating connkey as gapless has changed");
// console.log ("********* OFFSET FROM TOGGLE GAPLESS");
$scope.connKey = (Math.floor((Math.random() * 999999) + 1)).toString();
- $timeout(function () {
+ $timeout(function()
+ {
sendCommand('14', $scope.connKey, '&offset=' + $scope.currentProgress.progress);
}, 500);
//$timeout.cancel(eventQueryHandle);
//eventQueryHandle = $timeout (function(){checkEvent();}, zm.eventPlaybackQuery);
-
};
-
// This function returns neighbor events if applicable
- function neighborEvents(eid) {
+ function neighborEvents(eid)
+ {
var d = $q.defer();
// now get event details to show alarm frames
var loginData = NVRDataModel.getLogin();
@@ -1057,7 +1177,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
next: ""
};
$http.get(myurl)
- .success(function (data) {
+ .success(function(data)
+ {
// In Timeline view, gapless should stick to the same monitor
if ($scope.followSameMonitor == "1") // we are viewing only one monitor
@@ -1065,90 +1186,101 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
NVRDataModel.debug("Getting next event for same monitor Id ");
neighbors.prev = data.event.Event.PrevOfMonitor ? data.event.Event.PrevOfMonitor : "";
neighbors.next = data.event.Event.NextOfMonitor ? data.event.Event.NextOfMonitor : "";
- } else {
+ }
+ else
+ {
neighbors.prev = data.event.Event.Prev ? data.event.Event.Prev : "";
neighbors.next = data.event.Event.Next ? data.event.Event.Next : "";
}
NVRDataModel.debug("Neighbor events of " + eid + "are Prev:" +
neighbors.prev + " and Next:" + neighbors.next);
-
d.resolve(neighbors);
return (d.promise);
})
- .error(function (err) {
+ .error(function(err)
+ {
NVRDataModel.log("Error retrieving neighbors" + JSON.stringify(err));
d.reject(neighbors);
return (d.promise);
-
});
return (d.promise);
}
-
- $scope.zoomImage = function (val) {
+ $scope.zoomImage = function(val)
+ {
var zl = parseInt($ionicScrollDelegate.$getByHandle("imgscroll").getScrollPosition().zoom);
- if (zl == 1 && val == -1) {
+ if (zl == 1 && val == -1)
+ {
NVRDataModel.debug("Already zoomed out max");
return;
}
-
zl += val;
NVRDataModel.debug("Zoom level is " + zl);
$ionicScrollDelegate.$getByHandle("imgscroll").zoomTo(zl, true);
};
-
//--------------------------------------------------------
//Navigate to next/prev event in full screen mode
//--------------------------------------------------------
- $scope.onSwipeEvent = function (eid, dirn) {
+ $scope.onSwipeEvent = function(eid, dirn)
+ {
//console.log("HERE");
var ld = NVRDataModel.getLogin();
if (!ld.canSwipeMonitors) return;
- if ($ionicScrollDelegate.$getByHandle("imgscroll").getScrollPosition().zoom != 1) {
+ if ($ionicScrollDelegate.$getByHandle("imgscroll").getScrollPosition().zoom != 1)
+ {
//console.log("Image is zoomed in - not honoring swipe");
return;
}
- if (ld.useNphZmsForEvents) {
+ if (ld.useNphZmsForEvents)
+ {
NVRDataModel.log("using zms to move ");
jumpToEventZms($scope.connKey, dirn);
// sendCommand ( dirn==1?'13':'12',$scope.connKey);
- } else {
+ }
+ else
+ {
jumpToEvent(eid, dirn);
}
//console.log("JUMPING");
-
};
- $scope.jumpToEvent = function (eid, dirn) {
+ $scope.jumpToEvent = function(eid, dirn)
+ {
// console.log("jumptoevent");
var ld = NVRDataModel.getLogin();
- if (ld.useNphZmsForEvents) {
+ if (ld.useNphZmsForEvents)
+ {
NVRDataModel.log("using zms to move ");
jumpToEventZms($scope.connKey, dirn);
// sendCommand ( dirn==1?'13':'12',$scope.connKey);
- } else {
+ }
+ else
+ {
jumpToEvent(eid, dirn);
}
};
- function jumpToEvent(eid, dirn) {
+ function jumpToEvent(eid, dirn)
+ {
NVRDataModel.log("Event jump called with:" + eid);
- if (eid == "") {
- $ionicLoading.show({
+ if (eid == "")
+ {
+ $ionicLoading.show(
+ {
template: $translate.instant('kNoMoreEvents'),
noBackdrop: true,
duration: 2000
@@ -1159,26 +1291,29 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
var slidein;
var slideout;
- if (dirn == 1) {
+ if (dirn == 1)
+ {
slideout = "animated slideOutLeft";
slidein = "animated slideInRight";
- } else {
+ }
+ else
+ {
slideout = "animated slideOutRight";
slidein = "animated slideInLeft";
}
var element = angular.element(document.getElementById("full-screen-event"));
element.addClass(slideout).one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', outWithOld);
-
-
- function outWithOld() {
+ function outWithOld()
+ {
NVRDataModel.log("ModalCtrl:Stopping network pull...");
NVRDataModel.stopNetwork("EventModalCtrl-out with old");
$scope.animationInProgress = true;
// give digest time for image to swap
// 100 should be enough
- $timeout(function () {
+ $timeout(function()
+ {
element.removeClass(slideout);
element.addClass(slidein)
.one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', inWithNew);
@@ -1186,7 +1321,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
}, 200);
}
- function inWithNew() {
+ function inWithNew()
+ {
element.removeClass(slidein);
$scope.animationInProgress = false;
carouselUtils.setStop(false);
@@ -1197,18 +1333,20 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
function humanizeTime(str)
{
// if (NVRDataModel.getLogin().useLocalTimeZone)
- return moment.tz(str, NVRDataModel.getTimeZoneNow()).fromNow();
- // else
+ return moment.tz(str, NVRDataModel.getTimeZoneNow()).fromNow();
+ // else
// return moment(str).fromNow();
-
- }
- function jumpToEventZms(connkey, dirn) {
+ }
+ function jumpToEventZms(connkey, dirn)
+ {
- if ($scope.defaultVideo !== undefined && $scope.defaultVideo != '') {
+ if ($scope.defaultVideo !== undefined && $scope.defaultVideo != '')
+ {
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kEventNavVidFeeds'),
noBackdrop: true,
duration: 3000
@@ -1220,7 +1358,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
$scope.d_eventId = "...";
NVRDataModel.debug("Sending " + cmd + " to " + connkey);
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kSwitchingEvents') + "...",
noBackdrop: true,
duration: zm.httpTimeout
@@ -1229,17 +1368,20 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
//console.log("Send command connkey: " + connkey);
sendCommand(cmd, connkey)
.then(
- function (success) {
+ function(success)
+ {
//console.log ("jump success " + JSON.stringify(success));
$ionicLoading.hide();
},
- function (error) {
+ function(error)
+ {
NVRDataModel.debug("Hmm jump error " + JSON.stringify(error));
NVRDataModel.stopNetwork("EventModalCtrl-jumptoEventZms error");
$scope.connKey = (Math.floor((Math.random() * 999999) + 1)).toString();
// console.log ("********* OFFSET FROM JUMPTOEVENTZMS ERROR");
- $timeout(function () {
+ $timeout(function()
+ {
sendCommand('14', $scope.connKey, '&offset=' + $scope.currentProgress.progress);
}, 500);
NVRDataModel.debug("so I'm regenerating Connkey to " + $scope.connKey);
@@ -1249,23 +1391,24 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
});
var slidein;
var slideout;
- if (dirn == 1) {
+ if (dirn == 1)
+ {
slideout = "animated slideOutLeft";
slidein = "animated slideInRight";
- } else {
+ }
+ else
+ {
slideout = "animated slideOutRight";
slidein = "animated slideInLeft";
}
var element = angular.element(document.getElementById("full-screen-event"));
element.addClass(slideout).one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', outWithOld);
+ function outWithOld()
+ {
-
- function outWithOld() {
-
-
-
- $timeout(function () {
+ $timeout(function()
+ {
element.removeClass(slideout);
element.addClass(slidein)
.one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', inWithNew);
@@ -1273,20 +1416,20 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
}, 200);
}
- function inWithNew() {
+ function inWithNew()
+ {
element.removeClass(slidein);
-
}
}
-
//--------------------------------------------------------
// utility function
//--------------------------------------------------------
- function computeRelativePath(event) {
+ function computeRelativePath(event)
+ {
var relativePath = "";
var loginData = NVRDataModel.getLogin();
var str = event.Event.StartTime;
@@ -1311,7 +1454,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
// utility function
//--------------------------------------------------------
- function computeBasePath(event) {
+ function computeBasePath(event)
+ {
var basePath = "";
var loginData = NVRDataModel.getLogin();
var str = event.Event.StartTime;
@@ -1333,42 +1477,48 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
return basePath;
}
-
//-------------------------------------------------------------------------
// Called when rncarousel or video player finished playing event
//-------------------------------------------------------------------------
- $scope.playbackFinished = function () {
+ $scope.playbackFinished = function()
+ {
playbackFinished();
};
- function playbackFinished() {
+ function playbackFinished()
+ {
// currentEvent is updated with the currently playing event in prepareModalEvent()
NVRDataModel.log("Playback of event " + currentEvent.Event.Id + " is finished");
- if ($scope.loginData.gapless) {
+ if ($scope.loginData.gapless)
+ {
neighborEvents(currentEvent.Event.Id)
- .then(function (success) {
+ .then(function(success)
+ {
// lets give a second before gapless transition to the next event
- $timeout(function () {
+ $timeout(function()
+ {
$scope.nextId = success.next;
$scope.prevId = success.prev;
NVRDataModel.debug("Gapless move to event " + $scope.nextId);
jumpToEvent($scope.nextId, 1);
}, 1000);
},
- function (error) {
+ function(error)
+ {
NVRDataModel.debug("Error in neighbor call " +
JSON.stringify(error));
});
- } else {
+ }
+ else
+ {
NVRDataModel.debug("not going to next event, gapless is off");
}
}
-
//--------------------------------------------------------
// Called by openModal as well as jump to event
// what it basically does is get a detailed event API
@@ -1378,10 +1528,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
// API. Some parameters are repeated across both
//--------------------------------------------------------
-
-
-
- function prepareModalEvent(eid) {
+ function prepareModalEvent(eid)
+ {
// Lets get the detailed event API
var loginData = NVRDataModel.getLogin();
@@ -1390,8 +1538,8 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
$scope.humanizeTime = "...";
$scope.mName = "...";
$http.get(myurl)
- .then(function (success) {
-
+ .then(function(success)
+ {
// console.log ("DUCCESS::"+JSON.stringify(success));
var event = success.data.event;
@@ -1426,36 +1574,34 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
//console.log("Event ID is " + $scope.eventId);
//console.log("video is " + $scope.defaultVideo);
-
neighborEvents(event.Event.Id)
- .then(function (success) {
+ .then(function(success)
+ {
$scope.nextId = success.next;
$scope.prevId = success.prev;
},
- function (error) {
+ function(error)
+ {
//console.log(JSON.stringify(error));
});
$scope.nextId = "...";
$scope.prevId = "...";
-
-
-
event.Event.video = {};
var videoURL;
- if ((event.Event.imageMode == 'path') || NVRDataModel.getLogin().forceImageModePath )
+ if ((event.Event.imageMode == 'path') || NVRDataModel.getLogin().forceImageModePath)
videoURL = event.Event.baseURL + "/events/" + event.Event.relativePath + event.Event.DefaultVideo;
else
videoURL = event.Event.baseURL + "/index.php?view=view_video&eid=" + event.Event.Id;
// hack
- //videoURL = "http://static.videogular.com/assets/videos/videogular.mp4";
- //videoURL = "http://arjunrc.ddns.net:8888/foo2.mp4";
+ //videoURL = "http://static.videogular.com/assets/videos/videogular.mp4";
+ //videoURL = "http://arjunrc.ddns.net:8888/foo2.mp4";
$scope.video_url = videoURL;
- // console.log("************** VIDEO IS " + videoURL);
+ console.log("************** VIDEO IS " + videoURL);
NVRDataModel.debug("Video url passed to player is: " + videoURL);
@@ -1463,12 +1609,15 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
//console.log("************** VIDEO IS " + videoURL);
-
$scope.videoObject = {
- config: {
+ config:
+ {
autoPlay: true,
-
- playsInline:true,
+ responsive: false,
+ nativeControls: false,
+ nativeFullScreen:false,
+
+ playsInline: true,
sources: [
{
src: $sce.trustAsResourceUrl(videoURL),
@@ -1478,6 +1627,12 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
],
theme: "lib/videogular-themes-default/videogular.css",
+ cuepoints: {
+ theme: {
+ url:"lib/videogular-cuepoints/cuepoints.css"
+ },
+ points: [],
+ }
}
};
@@ -1502,31 +1657,38 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
realtime: true,
step: 1,
className: "mySliderClass",
- callback: function (value, released) {
+ callback: function(value, released)
+ {
//console.log("CALLBACK"+value+released);
$ionicScrollDelegate.freezeScroll(!released);
-
},
//modelLabels:function(val) {return "";},
smooth: false,
- css: {
- background: {
+ css:
+ {
+ background:
+ {
"background-color": "silver"
},
- before: {
+ before:
+ {
"background-color": "purple"
},
- default: {
+ default:
+ {
"background-color": "white"
}, // default value: 1px
- after: {
+ after:
+ {
"background-color": "green"
}, // zone after default value
- pointer: {
+ pointer:
+ {
"background-color": "red"
}, // circle pointer
- range: {
+ range:
+ {
"background-color": "red"
} // use it if double value
},
@@ -1534,8 +1696,6 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
};
-
-
$scope.mycarousel.index = 0;
$scope.ionRange.index = 1;
$scope.eventSpeed = $scope.event.Event.Length / $scope.event.Event.Frames;
@@ -1543,16 +1703,17 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
//console.log("**Resetting range");
$scope.slides = [];
var i;
- for (i = 1; i <= event.Event.Frames; i++) {
+ for (i = 1; i <= event.Event.Frames; i++)
+ {
var fname = padToN(i, eventImageDigits) + "-capture.jpg";
// console.log ("Building " + fname);
- $scope.slides.push({
+ $scope.slides.push(
+ {
id: i,
img: fname
});
}
-
// now get event details to show alarm frames
//$scope.FrameArray = event.Frame;
@@ -1561,60 +1722,72 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
// lets
framearray.datasets[0].data = [];
- for (i = 0; i < event.Frame.length; i++) {
+ for (i = 0; i < event.Frame.length; i++)
+ {
var ts = moment(event.Frame[i].TimeStamp).format(timeFormat);
//console.log ("pushing s:" + event.Frame[i].Score+" t:"+ts);
- framearray.datasets[0].data.push({
+ framearray.datasets[0].data.push(
+ {
x: ts,
y: event.Frame[i].Score
});
framearray.labels.push("");
-
}
$scope.totalEventTime = Math.round(parseFloat(event.Event.Length)) - 1;
$scope.currentEventTime = 0;
- $timeout(function () {
- drawGraph();
- }, 500);
+ // video mode doesn't need this graph - it won't really work
+ if ($scope.defaultVideo == undefined || $scope.defaultVideo == '')
+ {
+ $timeout(function()
+ {
+ drawGraph();
+ }, 500);
+ }
+
},
- function (err) {
+ function(err)
+ {
NVRDataModel.log("Error retrieving detailed frame API " + JSON.stringify(err));
NVRDataModel.displayBanner('error', ['could not retrieve frame details', 'please try again']);
});
-
}
-
- if (typeof $scope.ionRange !== 'undefined') {
- $scope.$watch('ionRange.index', function () {
+ if (typeof $scope.ionRange !== 'undefined')
+ {
+ $scope.$watch('ionRange.index', function()
+ {
//
$scope.mycarousel.index = parseInt($scope.ionRange.index) - 1;
if (carouselUtils.getStop() == true)
return;
-
//console.log ("***ION RANGE CHANGED TO " + $scope.mycarousel.index);
});
}
- if (typeof $scope.mycarousel !== 'undefined') {
- $scope.$watch('mycarousel.index', function () {
+ if (typeof $scope.mycarousel !== 'undefined')
+ {
+ $scope.$watch('mycarousel.index', function()
+ {
- if (currentEvent && $scope.ionRange.index == parseInt(currentEvent.Event.Frames - 1)) {
+ if (currentEvent && $scope.ionRange.index == parseInt(currentEvent.Event.Frames - 1))
+ {
playbackFinished();
}
// end of playback from quick scrub
// so ignore gapless
- if ($scope.event && $scope.ionRange.index == parseInt($scope.event.Event.Frames) - 1) {
- if (!$scope.modal || $scope.modal.isShown() == false) {
+ if ($scope.event && $scope.ionRange.index == parseInt($scope.event.Event.Frames) - 1)
+ {
+ if (!$scope.modal || $scope.modal.isShown() == false)
+ {
// console.log("quick scrub playback over");
carouselUtils.setStop(true);
$scope.ionRange.index = 0;
@@ -1627,28 +1800,28 @@ angular.module('zmApp.controllers').controller('EventModalCtrl', ['$scope', '$ro
$scope.ionRange.index = ($scope.mycarousel.index + 1).toString();
// console.log ("***IONRANGE RANGE CHANGED TO " + $scope.ionRange.index);
-
});
}
- function padToN(number, digits) {
+ function padToN(number, digits)
+ {
var i;
var stringMax = "";
var stringLeading = "";
- for (i = 1; i <= digits; i++) {
+ for (i = 1; i <= digits; i++)
+ {
stringMax = stringMax + "9";
if (i != digits) stringLeading = stringLeading + "0";
}
var numMax = parseInt(stringMax);
- if (number <= numMax) {
+ if (number <= numMax)
+ {
number = (stringLeading + number).slice(-digits);
}
//console.log ("PADTON: returning " + number);
return number;
}
-
-
-}]); \ No newline at end of file
+}]);
diff --git a/www/js/EventServer.js b/www/js/EventServer.js
index 8ea63531..b0d0c83f 100644
--- a/www/js/EventServer.js
+++ b/www/js/EventServer.js
@@ -1,6 +1,5 @@
/* jshint -W041 */
-
/* jslint browser: true*/
/* global cordova,StatusBar,angular,console ,PushNotification*/
@@ -10,8 +9,8 @@
//--------------------------------------------------------------------------
angular.module('zmApp.controllers')
-.factory('EventServer', ['NVRDataModel', '$rootScope', '$websocket', '$ionicPopup', '$timeout', '$q', 'zm', '$ionicPlatform', '$cordovaMedia', '$translate', function
- (NVRDataModel, $rootScope, $websocket, $ionicPopup, $timeout, $q, zm, $ionicPlatform, $cordovaMedia, $translate) {
+ .factory('EventServer', ['NVRDataModel', '$rootScope', '$websocket', '$ionicPopup', '$timeout', '$q', 'zm', '$ionicPlatform', '$cordovaMedia', '$translate', function(NVRDataModel, $rootScope, $websocket, $ionicPopup, $timeout, $q, zm, $ionicPlatform, $cordovaMedia, $translate)
+ {
var lastEventServerCheck = Date.now();
var ws;
@@ -19,24 +18,27 @@ angular.module('zmApp.controllers')
var localNotificationId = 0;
var firstError = true;
-
//--------------------------------------------------------------------------
// called when the websocket is opened
//--------------------------------------------------------------------------
- function openHandshake() {
+ function openHandshake()
+ {
var loginData = NVRDataModel.getLogin();
- if (loginData.isUseEventServer == false || loginData.eventServer == "") {
+ if (loginData.isUseEventServer == false || loginData.eventServer == "")
+ {
NVRDataModel.log("openHandShake: no event server");
return;
}
NVRDataModel.log("openHandshake: Websocket open");
- ws.$emit('auth', {
+ ws.$emit('auth',
+ {
user: loginData.username,
password: loginData.password
});
- if ($rootScope.apnsToken != '') {
+ if ($rootScope.apnsToken != '')
+ {
var plat = $ionicPlatform.is('ios') ? 'ios' : 'android';
var ld = NVRDataModel.getLogin();
var pushstate = "enabled";
@@ -44,25 +46,32 @@ angular.module('zmApp.controllers')
pushstate = "disabled";
NVRDataModel.debug("openHandShake: state of push is " + pushstate);
- ws.$emit('push', {
- type: 'token',
- platform: plat,
- token: $rootScope.apnsToken,
- state: pushstate
- });
+ // let's do this only if disabled. If enabled, I suppose registration
+ // will be called?
+ //if (ld.disablePush)
+ if (1)
+ {
+ //console.log ("HANDSHAKE MESSAGE WITH "+$rootScope.monstring);
+ ws.$emit('push',
+ {
+ type: 'token',
+ platform: plat,
+ token: $rootScope.apnsToken,
+ monlist:$rootScope.monstring,
+ intlist:$rootScope.intstring,
+ state: pushstate
+ });
+ }
}
}
-
-
//--------------------------------------------------------------------------
// Called once at app start. Does a lazy definition of websockets open
//--------------------------------------------------------------------------
- function init() {
-
-
-
+ function init()
+ {
+
$rootScope.isAlarm = 0;
$rootScope.alarmCount = "0";
@@ -72,7 +81,8 @@ angular.module('zmApp.controllers')
//console.log ("INIT GOT " + JSON.stringify(loginData));
- if (loginData.isUseEventServer == false || !loginData.eventServer) {
+ if (loginData.isUseEventServer == false || !loginData.eventServer)
+ {
NVRDataModel.log("No Event Server present. Not initializing");
d.reject("false");
return d.promise;
@@ -81,63 +91,65 @@ angular.module('zmApp.controllers')
//if (!$rootScope.apnsToken)
pushInit();
-
-
- if (typeof ws !== 'undefined') {
+ if (typeof ws !== 'undefined')
+ {
NVRDataModel.debug("Event server already initialized");
d.resolve("true");
return d.promise;
}
-
NVRDataModel.log("Initializing Websocket with URL " +
loginData.eventServer + " , will connect later...");
- ws = $websocket.$new({
+ ws = $websocket.$new(
+ {
url: loginData.eventServer,
reconnect: true,
reconnectInterval: 60000,
lazy: true
});
-
-
// Transmit auth information to server
ws.$on('$open', openHandshake);
- NVRDataModel.debug ("Setting up websocket error handler" );
- ws.$on('$error', function (e){
-
+ NVRDataModel.debug("Setting up websocket error handler");
+ ws.$on('$error', function(e)
+ {
+
// we don't need this check as I changed reconnect interval to 60s
//if ((Date.now() - lastEventServerCheck > 30000.0) || firstError)
if (1)
{
- NVRDataModel.debug ("Websocket Errorhandler called");
- $timeout( function(){
- NVRDataModel.displayBanner('error',['Event Server connection error']);
- },3000); // leave 3 seconds for transitions
+ NVRDataModel.debug("Websocket Errorhandler called");
+ $timeout(function()
+ {
+ NVRDataModel.displayBanner('error', ['Event Server connection error']);
+ }, 3000); // leave 3 seconds for transitions
firstError = false;
lastEventServerCheck = Date.now();
}
//console.log ("VALUE TIME " + lastEventServerCheck);
//console.log ("NOW TIME " + Date.now());
- });
+ });
- ws.$on('$close', function () {
+ ws.$on('$close', function()
+ {
NVRDataModel.log("Websocket closed");
});
// Handles responses back from ZM ES
- ws.$on('$message', function (str) {
+ ws.$on('$message', function(str)
+ {
NVRDataModel.log("Real-time event: " + JSON.stringify(str));
-
// Error messages
- if (str.status != 'Success') {
+ if (str.status != 'Success')
+ {
NVRDataModel.log("Event Error: " + JSON.stringify(str));
- if (str.reason == 'APNSDISABLED') {
+ if (str.reason == 'APNSDISABLED')
+ {
ws.$close();
NVRDataModel.displayBanner('error', ['Event Server: APNS disabled'], 2000, 6000);
$rootScope.apnsToken = "";
@@ -145,73 +157,87 @@ angular.module('zmApp.controllers')
}
- if (str.status == 'Success' && (str.event == 'auth')) {
+ if (str.status == 'Success' && (str.event == 'auth'))
+ {
if (str.version == undefined)
str.version = "0.1";
- if (NVRDataModel.versionCompare(str.version, zm.minEventServerVersion) == -1) {
- $rootScope.zmPopup = $ionicPopup.alert({
+ if (NVRDataModel.versionCompare(str.version, zm.minEventServerVersion) == -1)
+ {
+ $rootScope.zmPopup = $ionicPopup.alert(
+ {
title: $translate.instant('kEventServerVersionTitle'),
template: $translate.instant('kEventServerVersionBody1') + " " + str.version + ". " + $translate.instant('kEventServerVersionBody2') +
- zm.minEventServerVersion
+ zm.minEventServerVersion,
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
});
}
}
-
-
-
if (str.status == 'Success' && str.event == 'alarm') // new events
{
var localNotText;
// ZMN specific hack for Event Server
- if (str.supplementary != 'true') {
+ if (str.supplementary != 'true')
+ {
new Audio('sounds/blop.mp3').play();
localNotText = "Latest Alarms: ";
$rootScope.isAlarm = 1;
// Show upto a max of 99 when it comes to display
// so aesthetics are maintained
- if ($rootScope.alarmCount == "99") {
+ if ($rootScope.alarmCount == "99")
+ {
$rootScope.alarmCount = "99+";
}
- if ($rootScope.alarmCount != "99+") {
+ if ($rootScope.alarmCount != "99+")
+ {
$rootScope.alarmCount = (parseInt($rootScope.alarmCount) + 1).toString();
}
- } else {
+ }
+ else
+ {
NVRDataModel.debug("received supplementary event information over websockets");
}
var eventsToDisplay = [];
var listOfMonitors = [];
- for (var iter = 0; iter < str.events.length; iter++) {
+ for (var iter = 0; iter < str.events.length; iter++)
+ {
// lets stack the display so they don't overwrite
eventsToDisplay.push(str.events[iter].Name + ": latest new alarm (" + str.events[iter].EventId + ")");
localNotText = localNotText + str.events[iter].Name + ",";
listOfMonitors.push(str.events[iter].MonitorId);
-
}
localNotText = localNotText.substring(0, localNotText.length - 1);
// if we are in background, do a local notification, else do an in app display
- if (!NVRDataModel.isBackground()) {
+ if (!NVRDataModel.isBackground())
+ {
//emit alarm details - this is when received over websockets
- $rootScope.$emit('alarm', {
+ $rootScope.$emit('alarm',
+ {
message: listOfMonitors
});
- if (str.supplementary != 'true') {
+ if (str.supplementary != 'true')
+ {
NVRDataModel.debug("App is in foreground, displaying banner");
- if (eventsToDisplay.length > 0) {
+ if (eventsToDisplay.length > 0)
+ {
- if (eventsToDisplay.length == 1) {
+ if (eventsToDisplay.length == 1)
+ {
//console.log("Single Display: " + eventsToDisplay[0]);
NVRDataModel.displayBanner('alarm', [eventsToDisplay[0]], 5000, 5000);
- } else {
+ }
+ else
+ {
NVRDataModel.displayBanner('alarm', eventsToDisplay,
5000, 5000 * eventsToDisplay.length);
}
@@ -220,21 +246,16 @@ angular.module('zmApp.controllers')
}
}
-
-
} //end of success handler
-
-
-
-
});
d.resolve("true");
return (d.promise);
}
- function disconnect() {
+ function disconnect()
+ {
NVRDataModel.log("Disconnecting and deleting Event Server socket...");
if (typeof ws === 'undefined')
@@ -256,63 +277,68 @@ angular.module('zmApp.controllers')
// you turn off ES and then we need sendMessage to
// let ZMES know not to send us messages
//--------------------------------------------------------------------------
- function sendMessage(type, obj, isForce) {
+ function sendMessage(type, obj, isForce)
+ {
var ld = NVRDataModel.getLogin();
- if (ld.isUseEventServer == false && isForce != 1) {
+ if (ld.isUseEventServer == false && isForce != 1)
+ {
NVRDataModel.debug("Not sending WSS message as event server is off");
return;
}
-
- if (typeof ws === 'undefined') {
+ if (typeof ws === 'undefined')
+ {
NVRDataModel.debug("Event server not initalized, not sending message");
return;
}
-
- if (ws.$status() == ws.$CLOSED) {
+ if (ws.$status() == ws.$CLOSED)
+ {
NVRDataModel.log("Websocket was closed, trying to re-open");
ws.$un('$open');
//ws.$on ('$open', openHandshake);
ws.$open();
-
- ws.$on('$open', openHandshake, function () {
+ ws.$on('$open', openHandshake, function()
+ {
//console.log(" sending " + type + " " +
// JSON.stringify(obj));
+ //console.log("sending " + type + " " + JSON.stringify(obj));
ws.$emit(type, obj);
ws.$un('$open');
ws.$on('$open', openHandshake);
-
});
-
- } else {
+ }
+ else
+ {
ws.$emit(type, obj);
- //console.log("sending " + type + " " + JSON.stringify(obj));
+ // console.log("sending " + type + " " + JSON.stringify(obj));
}
-
-
}
//--------------------------------------------------------------------------
// Called each time we resume
//--------------------------------------------------------------------------
- function refresh() {
+ function refresh()
+ {
var loginData = NVRDataModel.getLogin();
- if ((!loginData.eventServer) || (loginData.isUseEventServer == false)) {
+ if ((!loginData.eventServer) || (loginData.isUseEventServer == false))
+ {
NVRDataModel.log("No Event Server configured, skipping refresh");
// Let's also make sure that if the socket was open
// we close it - this may happen if you disable it after using it
- if (typeof ws !== 'undefined') {
- if (ws.$status() != ws.$CLOSED) {
+ if (typeof ws !== 'undefined')
+ {
+ if (ws.$status() != ws.$CLOSED)
+ {
NVRDataModel.debug("Closing open websocket as event server was disabled");
ws.$close();
}
@@ -321,7 +347,8 @@ angular.module('zmApp.controllers')
return;
}
- if (typeof ws === 'undefined') {
+ if (typeof ws === 'undefined')
+ {
NVRDataModel.debug("Calling websocket init");
init();
}
@@ -333,16 +360,16 @@ angular.module('zmApp.controllers')
// c) The network died
// Seems to me in all cases we should give re-open a shot
-
- if (ws.$status() == ws.$CLOSED) {
+ if (ws.$status() == ws.$CLOSED)
+ {
NVRDataModel.log("Websocket was closed, trying to re-open");
ws.$open();
}
-
}
- function pushInit() {
+ function pushInit()
+ {
NVRDataModel.log(">>>Setting up push registration");
var push;
var mediasrc;
@@ -351,20 +378,20 @@ angular.module('zmApp.controllers')
var plat = $ionicPlatform.is('ios') ? 'ios' : 'android';
-
- if ($rootScope.platformOS == 'desktop') {
+ if ($rootScope.platformOS == 'desktop')
+ {
NVRDataModel.log("Desktop instance, not setting up push. Websockets only, I hope");
return;
}
-
-
- if (plat == 'ios') {
+ if (plat == 'ios')
+ {
mediasrc = "sounds/blop.mp3";
push = PushNotification.init(
{
- "ios": {
+ "ios":
+ {
"alert": true,
"badge": true,
"sound": ld.soundOnPush,
@@ -374,17 +401,17 @@ angular.module('zmApp.controllers')
);
- } else {
+ }
+ else
+ {
mediasrc = "/android_asset/www/sounds/blop.mp3";
var android_media_file = "blop";
-
-
-
push = PushNotification.init(
{
- "android": {
+ "android":
+ {
"senderID": zm.gcmSenderId,
"icon": "ic_stat_notification",
sound: ld.soundOnPush,
@@ -395,13 +422,13 @@ angular.module('zmApp.controllers')
);
-
}
// console.log("*********** MEDIA BLOG IS " + mediasrc);
media = $cordovaMedia.newMedia(mediasrc);
- push.on('registration', function (data) {
+ push.on('registration', function(data)
+ {
NVRDataModel.debug("Push Notification registration ID received: " + JSON.stringify(data));
$rootScope.apnsToken = data.registrationId;
@@ -411,29 +438,64 @@ angular.module('zmApp.controllers')
if (ld.disablePush == true)
pushstate = "disabled";
- sendMessage('push', {
- type: 'token',
- platform: plat,
- token: $rootScope.apnsToken,
- state: pushstate
- }, 1);
+ // now at this stage, if this is a first registration
+ // zmeventserver will have no record of this token
+ // so we need to make sure we send it a legit list of
+ // monitors otherwise users will get notifications for monitors
+ // their login is not supposed to see. Refer #391
+
+ var monstring='';
+ var intstring='';
+ NVRDataModel.getMonitors()
+ .then (function(succ) {
+ var mon = succ;
+ for (var i = 0; i < mon.length; i++) {
+ monstring = monstring + mon[i].Monitor.Id + ",";
+ intstring = intstring + '0,';
+ }
+ if (monstring.charAt(monstring.length - 1) == ',')
+ monstring = monstring.substr(0, monstring.length - 1);
+ if (intstring.charAt(intstring.length - 1) == ',')
+ intstring = intstring.substr(0, intstring.length - 1);
- });
+ //console.log ("WUTPUT SENDING REG WITH "+monstring);
+ $rootScope.monstring = monstring;
+ $rootScope.intstring = intstring;
- push.on('notification', function (data) {
+ sendMessage('push',
+ {
+ type: 'token',
+ platform: plat,
+ token: $rootScope.apnsToken,
+ monlist: monstring,
+ intlist: intstring,
+ state: pushstate
+ }, 1);
+
+ },
+ function (err)
+ {
+ NVRDataModel.log ("Could not get monitors, can't send push reg");
+ });
+
+ });
+
+ push.on('notification', function(data)
+ {
NVRDataModel.debug("received push notification");
var ld = NVRDataModel.getLogin();
- if (ld.isUseEventServer == false) {
+ if (ld.isUseEventServer == false)
+ {
NVRDataModel.debug("received push notification, but event server disabled. Not acting on it");
return;
}
-
- if (data.additionalData.foreground == false) {
+ if (data.additionalData.foreground == false)
+ {
// This means push notification tap in background
NVRDataModel.debug("*** PUSH NOTFN.>>>>" + JSON.stringify(data));
@@ -449,7 +511,8 @@ angular.module('zmApp.controllers')
// if Multiple mids, take the first one
var mi = mid.indexOf(',');
- if (mi > 0) {
+ if (mi > 0)
+ {
mid = mid.slice(0, mi);
}
mid = parseInt(mid);
@@ -457,23 +520,26 @@ angular.module('zmApp.controllers')
$rootScope.tappedMid = mid;
NVRDataModel.log("Push notification: Tapped Monitor taken as:" + $rootScope.tappedMid);
-
-
- if ($rootScope.platformOS == 'ios') {
-
+ if ($rootScope.platformOS == 'ios')
+ {
NVRDataModel.debug("iOS only: clearing background push");
- push.finish(function () {
+ push.finish(function()
+ {
NVRDataModel.debug("processing of push data is finished");
});
}
- } else {
+ }
+ else
+ {
// this flag honors the HW mute button. Go figure
// http://ilee.co.uk/phonegap-plays-sound-on-mute/
- if (ld.soundOnPush) {
- media.play({
+ if (ld.soundOnPush)
+ {
+ media.play(
+ {
playAudioWhenScreenIsLocked: false
});
}
@@ -482,25 +548,25 @@ angular.module('zmApp.controllers')
// console.log ("***STRING: " + str + " " +str.status);
var eventsToDisplay = [];
-
-
NVRDataModel.displayBanner('alarm', [str], 0, 5000 * eventsToDisplay.length);
-
$rootScope.isAlarm = 1;
// Show upto a max of 99 when it comes to display
// so aesthetics are maintained
- if ($rootScope.alarmCount == "99") {
+ if ($rootScope.alarmCount == "99")
+ {
$rootScope.alarmCount = "99+";
}
- if ($rootScope.alarmCount != "99+") {
+ if ($rootScope.alarmCount != "99+")
+ {
$rootScope.alarmCount = (parseInt($rootScope.alarmCount) + 1).toString();
}
}
});
- push.on('error', function (e) {
+ push.on('error', function(e)
+ {
NVRDataModel.debug("Push error: " + JSON.stringify(e));
// console.log("************* PUSH ERROR ******************");
});
@@ -515,5 +581,4 @@ angular.module('zmApp.controllers')
};
-
-}]); \ No newline at end of file
+ }]);
diff --git a/www/js/EventServerSettingsCtrl.js b/www/js/EventServerSettingsCtrl.js
index 7bee989d..efa868ea 100644
--- a/www/js/EventServerSettingsCtrl.js
+++ b/www/js/EventServerSettingsCtrl.js
@@ -2,46 +2,51 @@
/* jslint browser: true*/
/* global cordova,StatusBar,angular,console */
- angular.module('zmApp.controllers').controller('zmApp.EventServerSettingsCtrl', ['$scope', '$ionicSideMenuDelegate', 'zm', '$stateParams', 'EventServer', '$ionicHistory', '$rootScope', '$state', 'message', 'NVRDataModel', '$ionicPlatform', '$ionicPopup', '$timeout', '$translate', function ($scope, $ionicSideMenuDelegate, zm, $stateParams, EventServer, $ionicHistory, $rootScope, $state, message, NVRDataModel, $ionicPlatform, $ionicPopup, $timeout, $translate) {
- $scope.openMenu = function () {
+ angular.module('zmApp.controllers').controller('zmApp.EventServerSettingsCtrl', ['$scope', '$ionicSideMenuDelegate', 'zm', '$stateParams', 'EventServer', '$ionicHistory', '$rootScope', '$state', 'message', 'NVRDataModel', '$ionicPlatform', '$ionicPopup', '$timeout', '$translate', function($scope, $ionicSideMenuDelegate, zm, $stateParams, EventServer, $ionicHistory, $rootScope, $state, message, NVRDataModel, $ionicPlatform, $ionicPopup, $timeout, $translate)
+ {
+ $scope.openMenu = function()
+ {
$ionicSideMenuDelegate.toggleLeft();
};
-
- $scope.openMenu = function () {
+ $scope.openMenu = function()
+ {
$ionicSideMenuDelegate.toggleLeft();
};
-
//----------------------------------------------------------------
// Alarm notification handling
//----------------------------------------------------------------
- $scope.handleAlarms = function () {
+ $scope.handleAlarms = function()
+ {
$rootScope.isAlarm = !$rootScope.isAlarm;
- if (!$rootScope.isAlarm) {
+ if (!$rootScope.isAlarm)
+ {
$rootScope.alarmCount = "0";
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
-
- $state.go("events", {
+ $state.go("events",
+ {
"id": 0,
- "playEvent":false
- }, {
+ "playEvent": false
+ },
+ {
reload: true
});
return;
}
};
-
// we need this to dynamically get title
// name as ion-view is set in stone and
// we don't get title till beforeEnter
// which is odd - I'd expect beforeEnter to load
// before View is loaded
- $scope.getTitle = function () {
+ $scope.getTitle = function()
+ {
return $scope.loginData.serverName;
};
@@ -49,54 +54,68 @@
// Save anyway when you exit
//----------------------------------------------------------------
- $scope.$on('$ionicView.beforeLeave', function () {
+ $scope.$on('$ionicView.beforeLeave', function()
+ {
saveItems();
-
});
-
- $scope.$on('$ionicView.beforeEnter', function () {
+ $scope.$on('$ionicView.beforeEnter', function()
+ {
$scope.loginData = NVRDataModel.getLogin();
//console.log ("Event server - before Enter, loginData is " + JSON.stringify($scope.loginData));
$scope.defScreen = $scope.loginData.onTapScreen;
- if ($scope.loginData.eventServer == "") {
+ if ($scope.loginData.eventServer == "")
+ {
$scope.loginData.eventServer = "wss://" + extractDomain($scope.loginData.url) + ":9000";
}
-
-
res = $scope.loginData.eventServerMonitors.split(",");
minterval = $scope.loginData.eventServerInterval.split(",");
+ var monchecked = false;
+ for (var i = 0; i < $scope.monitors.length; i++)
+ {
- for (var i = 0; i < $scope.monitors.length; i++) {
-
-
- if (!isEnabled($scope.monitors[i].Monitor.Id)) {
+ if (!isEnabled($scope.monitors[i].Monitor.Id))
+ {
// if the filter list has IDs and this is not part of it, uncheck it
$scope.monitors[i].Monitor.isChecked = false;
//console.log("Marking false");
$scope.monitors[i].Monitor.reportingInterval = 0;
- } else {
+ }
+ else
+ {
// console.log("Marking true");
$scope.monitors[i].Monitor.isChecked = true;
$scope.monitors[i].Monitor.reportingInterval = getInterval($scope.monitors[i].Monitor.Id);
+ monchecked = true;
}
}
- });
+ // now if none are checked, assume it means all checked. This is related to the
+ // fact that ES will start sending all monitors, even ones you don't have access to
+ if (!monchecked)
+ {
+ NVRDataModel.debug ("Enabling all monitors for event server");
+ for (var j = 0; j < $scope.monitors.length; j++)
+ {
+ $scope.monitors[i].Monitor.isChecked = true;
+ $scope.monitors[i].Monitor.reportingInterval = 0;
+ }
+
+ }
+ });
//--------------------------------------------------
// notification tap action
//--------------------------------------------------
-
-
- $scope.selectScreen = function () {
+ $scope.selectScreen = function()
+ {
var ld = NVRDataModel.getLogin();
@@ -104,62 +123,62 @@
selectedState: ld.onTapScreen
};
-
var options = '<ion-radio-fix ng-model="myopt.selectedState" ng-value="\'' + $translate.instant('kTapEvents') + '\'">' + $translate.instant('kTapEvents') + '</ion-radio-fix>';
options += '<ion-radio-fix ng-model="myopt.selectedState" ng-value="\'' + $translate.instant('kTapMontage') + '\'">' + $translate.instant('kTapMontage') + '</ion-radio-fix>';
options += '<ion-radio-fix ng-model="myopt.selectedState" ng-value="\'' + $translate.instant('kTapLiveMonitor') + '\'">' + $translate.instant('kTapLiveMonitor') + '</ion-radio-fix>';
-
-
- $rootScope.zmPopup = $ionicPopup.show({
+ $rootScope.zmPopup = $ionicPopup.show(
+ {
scope: $scope,
template: options,
-
title: 'View to navigate to:',
subTitle: 'currently set to: ' + ld.onTapScreen,
buttons: [
- {
- text: $translate.instant('kButtonCancel'),
-
+ {
+ text: $translate.instant('kButtonCancel'),
- },
+ },
+ {
+ text: $translate.instant('kButtonOk'),
+ onTap: function(e)
{
- text: $translate.instant('kButtonOk'),
- onTap: function (e) {
-
- ld.onTapScreen = $scope.myopt.selectedState;
- NVRDataModel.log("Setting new onTap State:" + ld.onTapScreen);
- NVRDataModel.setLogin(ld);
- $scope.defScreen = $scope.myopt.selectedState;
- $scope.loginData = ld;
+ ld.onTapScreen = $scope.myopt.selectedState;
+ NVRDataModel.log("Setting new onTap State:" + ld.onTapScreen);
+ NVRDataModel.setLogin(ld);
+ $scope.defScreen = $scope.myopt.selectedState;
+ $scope.loginData = ld;
- }
- }
- ]
+ }
+ }]
});
-
};
//----------------------------------------------------------------
// Accordion list show/hide
//----------------------------------------------------------------
- $scope.toggleGroup = function (group) {
- if ($scope.isGroupShown(group)) {
+ $scope.toggleGroup = function(group)
+ {
+ if ($scope.isGroupShown(group))
+ {
$scope.shownGroup = null;
- } else {
+ }
+ else
+ {
$scope.shownGroup = group;
}
};
- $scope.isGroupShown = function (group) {
+ $scope.isGroupShown = function(group)
+ {
return $scope.shownGroup === group;
};
- $scope.saveItems = function () {
+ $scope.saveItems = function()
+ {
saveItems();
};
@@ -167,16 +186,20 @@
// Saves ES data
//----------------------------------------------------------------
- function saveItems() {
+ function saveItems()
+ {
NVRDataModel.debug("Saving Event Server data");
var monstring = "";
var intervalstring = "";
var plat = $ionicPlatform.is('ios') ? 'ios' : 'android';
- for (var i = 0; i < $scope.monitors.length; i++) {
- if (isNaN($scope.monitors[i].Monitor.reportingInterval)) {
+ for (var i = 0; i < $scope.monitors.length; i++)
+ {
+ if (isNaN($scope.monitors[i].Monitor.reportingInterval))
+ {
$scope.monitors[i].Monitor.reportingInterval = 0;
}
- if ($scope.monitors[i].Monitor.isChecked) {
+ if ($scope.monitors[i].Monitor.isChecked)
+ {
monstring = monstring + $scope.monitors[i].Monitor.Id + ",";
var tint = parseInt($scope.monitors[i].Monitor.reportingInterval);
if (isNaN(tint)) tint = 0;
@@ -194,7 +217,6 @@
$scope.loginData.eventServerMonitors = monstring;
$scope.loginData.eventServerInterval = intervalstring;
-
//console.log ("SAVED: " + JSON.stringify($scope.loginData));
NVRDataModel.setLogin($scope.loginData);
@@ -202,12 +224,15 @@
if ($scope.loginData.disablePush == true || $scope.loginData.isUseEventServer == false)
pushstate = "disabled";
- if ($scope.loginData.isUseEventServer == true) {
+ if ($scope.loginData.isUseEventServer == true)
+ {
EventServer.init()
- .then(function (data) {
+ .then(function(data)
+ {
// console.log("Sending control filter");
NVRDataModel.debug("Sending Control message 'filter' with monlist=" + monstring + " and interval=" + intervalstring);
- EventServer.sendMessage("control", {
+ EventServer.sendMessage("control",
+ {
type: 'filter',
monlist: monstring,
intlist: intervalstring
@@ -220,7 +245,8 @@
{
// we need to disable the token
NVRDataModel.debug("Sending token state " + pushstate);
- EventServer.sendMessage('push', {
+ EventServer.sendMessage('push',
+ {
type: 'token',
platform: plat,
token: $rootScope.apnsToken,
@@ -229,12 +255,11 @@
}
-
});
-
-
- } else {
+ }
+ else
+ {
if ($rootScope.apnsToken != "")
// if its defined then this is post init work
// so lets transmit state here
@@ -242,7 +267,8 @@
{
// we need to disable the token
NVRDataModel.debug("Sending token state " + pushstate);
- EventServer.sendMessage('push', {
+ EventServer.sendMessage('push',
+ {
type: 'token',
platform: plat,
token: $rootScope.apnsToken,
@@ -254,11 +280,8 @@
EventServer.disconnect();
-
}
-
-
NVRDataModel.displayBanner('info', ['settings saved']);
}
@@ -266,12 +289,16 @@
// returns domain name in string -
// http://stackoverflow.com/questions/8498592/extract-root-domain-name-from-string
//----------------------------------------------------------------
- function extractDomain(url) {
+ function extractDomain(url)
+ {
var domain;
//find & remove protocol (http, ftp, etc.) and get domain
- if (url.indexOf("://") > -1) {
+ if (url.indexOf("://") > -1)
+ {
domain = url.split('/')[2];
- } else {
+ }
+ else
+ {
domain = url.split('/')[0];
}
@@ -281,18 +308,20 @@
return domain;
}
-
//----------------------------------------------------------------
// returns reporting interval for monitor ID
//----------------------------------------------------------------
- function getInterval(id) {
+ function getInterval(id)
+ {
// means no interval, should only happen one time
// till we save
if ($scope.loginData.eventServerInterval == "")
return 0;
var retval = 0;
- for (var i = 0; i < res.length; i++) {
- if (res[i] == id) {
+ for (var i = 0; i < res.length; i++)
+ {
+ if (res[i] == id)
+ {
retval = parseInt(minterval[i]);
break;
}
@@ -303,13 +332,16 @@
//----------------------------------------------------------------
// Returns true/false if monitor ID is in event monitor list
//----------------------------------------------------------------
- function isEnabled(id) {
+ function isEnabled(id)
+ {
if ($scope.loginData.eventServerMonitors == "")
return true;
var isThere = false;
- for (var i = 0; i < res.length; i++) {
- if (res[i] == id) {
+ for (var i = 0; i < res.length; i++)
+ {
+ if (res[i] == id)
+ {
isThere = true;
//console.log("isRes found: " + id);
break;
@@ -325,11 +357,4 @@
$scope.monitors = message;
var res, minterval;
-
-
-
-
-
-
-
-}]); \ No newline at end of file
+ }]);
diff --git a/www/js/EventsGraphsCtrl.js b/www/js/EventsGraphsCtrl.js
index 19d963ea..5f21b09b 100644
--- a/www/js/EventsGraphsCtrl.js
+++ b/www/js/EventsGraphsCtrl.js
@@ -8,31 +8,38 @@
// the main function is generateChart. I call generate chart with required parameters
// from the template file
-angular.module('zmApp.controllers').controller('zmApp.EventsGraphsCtrl', ['$ionicPlatform', '$scope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$rootScope', '$http', '$ionicHistory', '$state', function ($ionicPlatform, $scope, zm, NVRDataModel, $ionicSideMenuDelegate, $rootScope, $http, $ionicHistory, $state) {
+angular.module('zmApp.controllers').controller('zmApp.EventsGraphsCtrl', ['$ionicPlatform', '$scope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$rootScope', '$http', '$ionicHistory', '$state', function($ionicPlatform, $scope, zm, NVRDataModel, $ionicSideMenuDelegate, $rootScope, $http, $ionicHistory, $state)
+{
//console.log("Inside Graphs controller");
- $scope.openMenu = function () {
+ $scope.openMenu = function()
+ {
$ionicSideMenuDelegate.toggleLeft();
};
- $scope.$on('$ionicView.loaded', function () {
+ $scope.$on('$ionicView.loaded', function()
+ {
// console.log("**VIEW ** Graph Ctrl Loaded");
});
-
//----------------------------------------------------------------
// Alarm notification handling
//----------------------------------------------------------------
- $scope.handleAlarms = function () {
+ $scope.handleAlarms = function()
+ {
$rootScope.isAlarm = !$rootScope.isAlarm;
- if (!$rootScope.isAlarm) {
+ if (!$rootScope.isAlarm)
+ {
$rootScope.alarmCount = "0";
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
- $state.go("events", {
+ $state.go("events",
+ {
"id": 0,
- "playEvent":false
- }, {
+ "playEvent": false
+ },
+ {
reload: true
});
return;
@@ -46,20 +53,22 @@ angular.module('zmApp.controllers').controller('zmApp.EventsGraphsCtrl', ['$ioni
// reset power state on exit as if it is called after we enter another
// state, that effectively overwrites current view power management needs
//------------------------------------------------------------------------
- $scope.$on('$ionicView.enter', function () {
+ $scope.$on('$ionicView.enter', function()
+ {
// console.log("**VIEW ** EventsGraphs Ctrl Entered");
NVRDataModel.setAwake(false);
});
- $scope.$on('$ionicView.leave', function () {
+ $scope.$on('$ionicView.leave', function()
+ {
// console.log("**VIEW ** Graph Ctrl Left");
});
- $scope.$on('$ionicView.unloaded', function () {
+ $scope.$on('$ionicView.unloaded', function()
+ {
// console.log("**VIEW ** Graph Ctrl Unloaded");
});
-
//-------------------------------------------------
// Controller main
//-------------------------------------------------
@@ -67,56 +76,57 @@ angular.module('zmApp.controllers').controller('zmApp.EventsGraphsCtrl', ['$ioni
// $scope.chart = "";
$scope.navTitle = 'Tab Page';
// $scope.chart="";
- $scope.leftButtons = [{
+ $scope.leftButtons = [
+ {
type: 'button-icon icon ion-navicon',
- tap: function (e) {
+ tap: function(e)
+ {
$scope.toggleMenu();
}
- }];
-
+ }];
var container = angular.element(document.getElementById('visualization'));
//console.log(JSON.stringify(container));
var data = [
- {
- id: 1,
- content: 'item 1',
- start: '2013-04-20'
- },
- {
- id: 2,
- content: 'item 2',
- start: '2013-04-14'
- },
- {
- id: 3,
- content: 'item 3',
- start: '2013-04-18'
- },
- {
- id: 4,
- content: 'item 4',
- start: '2013-04-16',
- end: '2013-04-19'
- },
- {
- id: 5,
- content: 'item 5',
- start: '2013-04-25'
- },
- {
- id: 6,
- content: 'item 6',
- start: '2013-04-27'
- }
- ];
+ {
+ id: 1,
+ content: 'item 1',
+ start: '2013-04-20'
+ },
+ {
+ id: 2,
+ content: 'item 2',
+ start: '2013-04-14'
+ },
+ {
+ id: 3,
+ content: 'item 3',
+ start: '2013-04-18'
+ },
+ {
+ id: 4,
+ content: 'item 4',
+ start: '2013-04-16',
+ end: '2013-04-19'
+ },
+ {
+ id: 5,
+ content: 'item 5',
+ start: '2013-04-25'
+ },
+ {
+ id: 6,
+ content: 'item 6',
+ start: '2013-04-27'
+ }];
var options = {};
//var timeline = new vis.Timeline(container[0], data, options);
// -------------------------------------------------
// Called when user taps on a bar
//---------------------------------------------------
- $scope.handleChartClick = function (event) {
+ $scope.handleChartClick = function(event)
+ {
//console.log(JSON.stringify($scope.chartwithbars.getBarsAtEvent(event)));
//console.log(angular.element[0].getContext('2d'));
@@ -127,7 +137,8 @@ angular.module('zmApp.controllers').controller('zmApp.EventsGraphsCtrl', ['$ioni
//-------------------------------------------------
// Generates a bar graph with data provided
//-------------------------------------------------
- $scope.generateTCChart = function (id, chartTitle, hrs) {
+ $scope.generateTCChart = function(id, chartTitle, hrs)
+ {
var monitors = [];
var dateRange = "";
var startDate = "";
@@ -140,11 +151,10 @@ angular.module('zmApp.controllers').controller('zmApp.EventsGraphsCtrl', ['$ioni
};
-
$scope.chart.barHeight = $rootScope.devHeight;
-
- if (hrs) {
+ if (hrs)
+ {
// Apply a time based filter if I am not watching all events
var cur = moment();
endDate = cur.format("YYYY-MM-DD " + NVRDataModel.getTimeFormat());
@@ -159,27 +169,30 @@ angular.module('zmApp.controllers').controller('zmApp.EventsGraphsCtrl', ['$ioni
$scope.chart.data = {
labels: [],
datasets: [
- {
- label: '',
- fillColor: zm.graphFillColor,
- strokeColor: zm.graphStrokeColor,
- highlightFill: zm.graphHighlightFill,
- data: []
- },
- ]
+ {
+ label: '',
+ fillColor: zm.graphFillColor,
+ strokeColor: zm.graphStrokeColor,
+ highlightFill: zm.graphHighlightFill,
+ data: []
+ }, ]
};
- NVRDataModel.getMonitors(0).then(function (data) {
+ NVRDataModel.getMonitors(0).then(function(data)
+ {
monitors = data;
var adjustedHeight = monitors.length * 30;
- if (adjustedHeight > $rootScope.devHeight) {
+ if (adjustedHeight > $rootScope.devHeight)
+ {
$scope.chart.barHeight = adjustedHeight;
//console.log("********* BAR HEIGHT TO " + $scope.chart.barHeight);
}
- for (var i = 0; i < monitors.length; i++) {
- (function (j) { // loop closure - http is async, so success returns after i goes out of scope
+ for (var i = 0; i < monitors.length; i++)
+ {
+ (function(j)
+ { // loop closure - http is async, so success returns after i goes out of scope
// so we need to bind j to i when http returns so its not out of scope. Gak.
// I much prefer the old days of passing context data from request to response
@@ -189,7 +202,8 @@ angular.module('zmApp.controllers').controller('zmApp.EventsGraphsCtrl', ['$ioni
// $scope.chartObject.data[j+1]=([monitors[j].Monitor.Name,'100','color:#76A7FA','0']);
var dateString = "";
- if (hrs) {
+ if (hrs)
+ {
dateString = "/StartTime >=:" + startDate + "/EndTime <=:" + endDate;
}
var url = loginData.apiurl +
@@ -198,12 +212,14 @@ angular.module('zmApp.controllers').controller('zmApp.EventsGraphsCtrl', ['$ioni
// console.log("Monitor event URL:" + url);
NVRDataModel.log("EventGraph: composed url is " + url);
$http.get(url /*,{timeout:15000}*/ )
- .success(function (data) {
+ .success(function(data)
+ {
NVRDataModel.debug("Event count for monitor" +
monitors[j].Monitor.Id + " is " + data.pagination.count);
$scope.chart.data.datasets[0].data[j] = data.pagination.count;
})
- .error(function (data) {
+ .error(function(data)
+ {
// ideally I should be treating it as an error
// but what I am really doing now is treating it like no events
// works but TBD: make this into a proper error handler
@@ -231,4 +247,4 @@ angular.module('zmApp.controllers').controller('zmApp.EventsGraphsCtrl', ['$ioni
// legendTemplate : '<ul class="tc-chart-js-legend"><% for (var i=0; i<datasets.length; i++){%><li><span style="background-color:<%=datasets[i].fillColor%>"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>'
};
}; //generateTCChart
-}]); \ No newline at end of file
+}]);
diff --git a/www/js/EventsModalGraphCtrl.js b/www/js/EventsModalGraphCtrl.js
index 0874e4f7..fc9a9c42 100644
--- a/www/js/EventsModalGraphCtrl.js
+++ b/www/js/EventsModalGraphCtrl.js
@@ -3,13 +3,8 @@
/* jslint browser: true*/
/* global saveAs, cordova,StatusBar,angular,console,ionic, moment, vis , Chart, DJS*/
-
-
-
-angular.module('zmApp.controllers').controller('EventsModalGraphCtrl', ['$scope', '$rootScope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$q', '$sce', 'carouselUtils', '$ionicPopup', '$translate', function ($scope, $rootScope, zm, NVRDataModel, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $stateParams, $ionicHistory, $ionicScrollDelegate, $q, $sce, carouselUtils, $ionicPopup, $translate) {
-
-
-
+angular.module('zmApp.controllers').controller('EventsModalGraphCtrl', ['$scope', '$rootScope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$q', '$sce', 'carouselUtils', '$ionicPopup', '$translate', function($scope, $rootScope, zm, NVRDataModel, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $stateParams, $ionicHistory, $ionicScrollDelegate, $q, $sce, carouselUtils, $ionicPopup, $translate)
+{
var Graph2d;
var tcGraph;
@@ -26,52 +21,50 @@ angular.module('zmApp.controllers').controller('EventsModalGraphCtrl', ['$scope'
var btype;
var data, options;
-
-
-
-
-
- $scope.$on('modal.shown', function (e, m) {
+ $scope.$on('modal.shown', function(e, m)
+ {
if (m.id != 'modalgraph')
return;
-
//console.log ("INSIDE MODAL GRAPH>>>>>>>>>>>>>>>>>");
data = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
- {
- label: "My First dataset",
- fillColor: "rgba(220,220,220,0.5)",
- strokeColor: "rgba(220,220,220,0.8)",
- highlightFill: "rgba(220,220,220,0.75)",
- highlightStroke: "rgba(220,220,220,1)",
- data: [65, 59, 80, 81, 56, 55, 40]
- },
- {
- label: "My Second dataset",
- fillColor: "rgba(151,187,205,0.5)",
- strokeColor: "rgba(151,187,205,0.8)",
- highlightFill: "rgba(151,187,205,0.75)",
- highlightStroke: "rgba(151,187,205,1)",
- data: [28, 48, 40, 19, 86, 27, 90]
- }
- ]
+ {
+ label: "My First dataset",
+ fillColor: "rgba(220,220,220,0.5)",
+ strokeColor: "rgba(220,220,220,0.8)",
+ highlightFill: "rgba(220,220,220,0.75)",
+ highlightStroke: "rgba(220,220,220,1)",
+ data: [65, 59, 80, 81, 56, 55, 40]
+ },
+ {
+ label: "My Second dataset",
+ fillColor: "rgba(151,187,205,0.5)",
+ strokeColor: "rgba(151,187,205,0.8)",
+ highlightFill: "rgba(151,187,205,0.75)",
+ highlightStroke: "rgba(151,187,205,1)",
+ data: [28, 48, 40, 19, 86, 27, 90]
+ }]
};
options = {
- scales: {
- yAxes: [{
- ticks: {
+ scales:
+ {
+ yAxes: [
+ {
+ ticks:
+ {
// beginAtZero:true,
min: -1,
},
- }],
- xAxes: [{
+ }],
+ xAxes: [
+ {
display: false
- }]
+ }]
},
responsive: true,
@@ -80,10 +73,11 @@ angular.module('zmApp.controllers').controller('EventsModalGraphCtrl', ['$scope'
scaleGridLineColor: "rgba(0,0,0,.05)",
scaleGridLineWidth: 1,
-
- hover: {
+ hover:
+ {
mode: 'single',
- onHover: function (obj) {
+ onHover: function(obj)
+ {
if (obj.length > 0)
tapOrHover(obj[0]._index);
}
@@ -95,8 +89,10 @@ angular.module('zmApp.controllers').controller('EventsModalGraphCtrl', ['$scope'
cv = document.getElementById("eventchart");
ctx = cv.getContext("2d");
- $timeout(function () {
- var tcGraph2 = new Chart(ctx, {
+ $timeout(function()
+ {
+ var tcGraph2 = new Chart(ctx,
+ {
type: 'bar',
data: data,
options: options
@@ -108,24 +104,20 @@ angular.module('zmApp.controllers').controller('EventsModalGraphCtrl', ['$scope'
// we use this to reload the connkey if authkey changed
//------------------------------------------------------
-
- $rootScope.$on("auth-success", function () {
+ $rootScope.$on("auth-success", function()
+ {
NVRDataModel.debug("EventModalCtrl: Re-login detected, resetting everything & re-generating connkey");
-
});
-
-
-
-
//-------------------------------------------------------
// I was kidding, this is where it really is drawn
// scout's promise
//------------------------------------------------------
- function drawGraphTC(event) {
+ function drawGraphTC(event)
+ {
$scope.eid = event.event.Event.Id;
@@ -155,7 +147,6 @@ angular.module('zmApp.controllers').controller('EventsModalGraphCtrl', ['$scope'
]
};*/
-
data = {
labels: [],
datasets: [
@@ -176,9 +167,9 @@ angular.module('zmApp.controllers').controller('EventsModalGraphCtrl', ['$scope'
data: [],
frames: []
- },
+ },
- ]
+ ]
};
onlyalarm_data = {
@@ -192,24 +183,28 @@ angular.module('zmApp.controllers').controller('EventsModalGraphCtrl', ['$scope'
hoverBorderColor: 'rgba(248, 148, 6,1.0)',
data: [],
frames: []
- },
+ },
- ]
+ ]
};
// Chart.js Options
options = {
- scales: {
- yAxes: [{
- ticks: {
+ scales:
+ {
+ yAxes: [
+ {
+ ticks:
+ {
// beginAtZero:true,
min: -1,
},
- }],
- xAxes: [{
+ }],
+ xAxes: [
+ {
display: false
- }]
+ }]
},
responsive: true,
@@ -218,10 +213,11 @@ angular.module('zmApp.controllers').controller('EventsModalGraphCtrl', ['$scope'
scaleGridLineColor: "rgba(0,0,0,.05)",
scaleGridLineWidth: 1,
-
- hover: {
+ hover:
+ {
mode: 'single',
- onHover: function (obj) {
+ onHover: function(obj)
+ {
if (obj.length > 0)
tapOrHover(obj[0]._index);
}
@@ -237,13 +233,14 @@ angular.module('zmApp.controllers').controller('EventsModalGraphCtrl', ['$scope'
// NVRDataModel.log ("Changing graph width to " + $scope.graphWidth);
- for (var i = 0; i < event.event.Frame.length; i++) {
-
+ for (var i = 0; i < event.event.Frame.length; i++)
+ {
data.labels.push(event.event.Frame[i].TimeStamp);
//data.labels.push(' ');
data.datasets[0].data.push(event.event.Frame[i].Score);
- data.datasets[0].frames.push({
+ data.datasets[0].frames.push(
+ {
x: event.event.Frame[i].TimeStamp,
y: event.event.Frame[i].Score,
eid: event.event.Event.Id,
@@ -255,12 +252,14 @@ angular.module('zmApp.controllers').controller('EventsModalGraphCtrl', ['$scope'
});
- if (event.event.Frame[i].Type == "Alarm") {
+ if (event.event.Frame[i].Type == "Alarm")
+ {
onlyalarm_data.labels.push(event.event.Frame[i].TimeStamp);
//data.labels.push(' ');
onlyalarm_data.datasets[0].data.push(event.event.Frame[i].Score);
- onlyalarm_data.datasets[0].frames.push({
+ onlyalarm_data.datasets[0].frames.push(
+ {
x: event.event.Frame[i].TimeStamp,
y: event.event.Frame[i].Score,
eid: event.event.Event.Id,
@@ -280,39 +279,48 @@ angular.module('zmApp.controllers').controller('EventsModalGraphCtrl', ['$scope'
cv = document.getElementById("tcchart");
ctx = cv.getContext("2d");
- if (NVRDataModel.getLogin().timelineModalGraphType == 'all') {
+ if (NVRDataModel.getLogin().timelineModalGraphType == 'all')
+ {
btype = 'line';
current_data = data;
- } else {
+ }
+ else
+ {
btype = 'bar';
current_data = onlyalarm_data;
}
- $timeout(function () {
- tcGraph = new Chart(ctx, {
+ $timeout(function()
+ {
+ tcGraph = new Chart(ctx,
+ {
type: btype,
data: current_data,
options: options
});
});
- cv.onclick = function (e) {
+ cv.onclick = function(e)
+ {
var b = tcGraph.getElementAtEvent(e);
- if (b.length > 0) {
+ if (b.length > 0)
+ {
tapOrHover(b[0]._index);
}
};
}
- function tapOrHover(ndx) {
-
- $timeout(function () {
+ function tapOrHover(ndx)
+ {
+ $timeout(function()
+ {
//console.log ("You tapped " + ndx);
$scope.alarm_images = [];
$scope.playbackURL = NVRDataModel.getLogin().url;
var items = current_data.datasets[0].frames[ndx];
- $scope.alarm_images.push({
+ $scope.alarm_images.push(
+ {
relativePath: items.relativePath,
fid: items.fid,
fname: items.fname,
@@ -324,13 +332,12 @@ angular.module('zmApp.controllers').controller('EventsModalGraphCtrl', ['$scope'
}
-
-
//--------------------------------------------------------
// utility function
//--------------------------------------------------------
- function computeRelativePath(event) {
+ function computeRelativePath(event)
+ {
var relativePath = "";
var loginData = NVRDataModel.getLogin();
var str = event.Event.StartTime;
@@ -355,7 +362,8 @@ angular.module('zmApp.controllers').controller('EventsModalGraphCtrl', ['$scope'
// utility function
//--------------------------------------------------------
- function computeBasePath(event) {
+ function computeBasePath(event)
+ {
var basePath = "";
var loginData = NVRDataModel.getLogin();
var str = event.Event.StartTime;
@@ -377,26 +385,25 @@ angular.module('zmApp.controllers').controller('EventsModalGraphCtrl', ['$scope'
return basePath;
}
-
-
- function padToN(number, digits) {
+ function padToN(number, digits)
+ {
var i;
var stringMax = "";
var stringLeading = "";
- for (i = 1; i <= digits; i++) {
+ for (i = 1; i <= digits; i++)
+ {
stringMax = stringMax + "9";
if (i != digits) stringLeading = stringLeading + "0";
}
var numMax = parseInt(stringMax);
- if (number <= numMax) {
+ if (number <= numMax)
+ {
number = (stringLeading + number).slice(-digits);
}
//console.log ("PADTON: returning " + number);
return number;
}
-
-
-}]); \ No newline at end of file
+}]);
diff --git a/www/js/FirstUseCtrl.js b/www/js/FirstUseCtrl.js
index 8df81f82..e5692188 100644
--- a/www/js/FirstUseCtrl.js
+++ b/www/js/FirstUseCtrl.js
@@ -2,74 +2,79 @@
/* jslint browser: true*/
/* global cordova,StatusBar,angular,console */
-angular.module('zmApp.controllers').controller('zmApp.FirstUseCtrl', ['$scope', '$ionicSideMenuDelegate', 'zm', '$stateParams', '$ionicHistory', '$state', 'NVRDataModel', '$rootScope', '$ionicPopup', '$translate', function ($scope, $ionicSideMenuDelegate, zm, $stateParams, $ionicHistory, $state, NVRDataModel, $rootScope, $ionicPopup, $translate) {
- $scope.openMenu = function () {
+angular.module('zmApp.controllers').controller('zmApp.FirstUseCtrl', ['$scope', '$ionicSideMenuDelegate', 'zm', '$stateParams', '$ionicHistory', '$state', 'NVRDataModel', '$rootScope', '$ionicPopup', '$translate', function($scope, $ionicSideMenuDelegate, zm, $stateParams, $ionicHistory, $state, NVRDataModel, $rootScope, $ionicPopup, $translate)
+{
+ $scope.openMenu = function()
+ {
$ionicSideMenuDelegate.toggleLeft();
};
-
-
//-------------------------------------------------------------------------
// Controller Main
//------------------------------------------------------------------------
- $scope.$on('$ionicView.enter', function () {
+ $scope.$on('$ionicView.enter', function()
+ {
//console.log("**VIEW ** FirstUse Ctrl Entered");
$ionicSideMenuDelegate.canDragContent(true);
-
});
- $scope.switchLang = function () {
+ $scope.switchLang = function()
+ {
$scope.lang = NVRDataModel.getLanguages();
$scope.myopt = {
lang: ""
};
- $rootScope.zmPopup = $ionicPopup.show({
+ $rootScope.zmPopup = $ionicPopup.show(
+ {
scope: $scope,
template: '<ion-radio-fix ng-repeat="item in lang" ng-value="item.value" ng-model="myopt.lang"> {{item.text}} </ion-radio-fix>',
-
title: $translate.instant('kSelectLanguage'),
buttons: [
+ {
+ text: $translate.instant('kButtonCancel'),
+ onTap: function(e)
{
- text: $translate.instant('kButtonCancel'),
- onTap: function (e) {
- //return "CANCEL";
- }
+ //return "CANCEL";
+ }
- },
+ },
+ {
+ text: $translate.instant('kButtonOk'),
+ onTap: function(e)
{
- text: $translate.instant('kButtonOk'),
- onTap: function (e) {
- NVRDataModel.log("Language selected:" + $scope.myopt.lang);
- NVRDataModel.setDefaultLanguage($scope.myopt.lang, true);
+ NVRDataModel.log("Language selected:" + $scope.myopt.lang);
+ NVRDataModel.setDefaultLanguage($scope.myopt.lang, true);
+ //return "OK";
- //return "OK";
-
- }
- }
- ]
+ }
+ }]
});
-
};
- $scope.goToLogin = function () {
- $ionicHistory.nextViewOptions({
+ $scope.goToLogin = function()
+ {
+ $ionicHistory.nextViewOptions(
+ {
disableAnimate: false,
disableBack: true
});
- $state.go("login", {
+ $state.go("login",
+ {
"wizard": false
});
return;
};
- $scope.goToWizard = function () {
- $ionicHistory.nextViewOptions({
+ $scope.goToWizard = function()
+ {
+ $ionicHistory.nextViewOptions(
+ {
disableAnimate: false,
disableBack: true
});
@@ -77,6 +82,4 @@ angular.module('zmApp.controllers').controller('zmApp.FirstUseCtrl', ['$scope',
return;
};
-
-
-}]); \ No newline at end of file
+}]);
diff --git a/www/js/HelpCtrl.js b/www/js/HelpCtrl.js
index ce4ec761..51da877b 100644
--- a/www/js/HelpCtrl.js
+++ b/www/js/HelpCtrl.js
@@ -2,26 +2,32 @@
/* jslint browser: true*/
/* global cordova,StatusBar,angular,console, Masonry */
-angular.module('zmApp.controllers').controller('zmApp.HelpCtrl', ['$scope', '$rootScope', '$ionicModal', 'NVRDataModel', '$ionicSideMenuDelegate', '$ionicHistory', '$state', '$translate', '$q', '$templateRequest', '$sce', '$compile', function ($scope, $rootScope, $ionicModal, NVRDataModel, $ionicSideMenuDelegate, $ionicHistory, $state, $translate, $q, $templateRequest, $sce, $compile) {
- $scope.openMenu = function () {
+angular.module('zmApp.controllers').controller('zmApp.HelpCtrl', ['$scope', '$rootScope', '$ionicModal', 'NVRDataModel', '$ionicSideMenuDelegate', '$ionicHistory', '$state', '$translate', '$q', '$templateRequest', '$sce', '$compile', function($scope, $rootScope, $ionicModal, NVRDataModel, $ionicSideMenuDelegate, $ionicHistory, $state, $translate, $q, $templateRequest, $sce, $compile)
+{
+ $scope.openMenu = function()
+ {
$ionicSideMenuDelegate.toggleLeft();
};
-
//----------------------------------------------------------------
// Alarm notification handling
//----------------------------------------------------------------
- $scope.handleAlarms = function () {
+ $scope.handleAlarms = function()
+ {
$rootScope.isAlarm = !$rootScope.isAlarm;
- if (!$rootScope.isAlarm) {
+ if (!$rootScope.isAlarm)
+ {
$rootScope.alarmCount = "0";
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
- $state.go("events", {
+ $state.go("events",
+ {
"id": 0,
- "playEvent":false
- }, {
+ "playEvent": false
+ },
+ {
reload: true
});
return;
@@ -33,7 +39,8 @@ angular.module('zmApp.controllers').controller('zmApp.HelpCtrl', ['$scope', '$ro
// based on selected language
//----------------------------------------------------------------
- function insertHelp() {
+ function insertHelp()
+ {
var l = NVRDataModel.getDefaultLanguage() || 'en';
var lang = "lang/help/help-" + l + ".html";
@@ -43,25 +50,27 @@ angular.module('zmApp.controllers').controller('zmApp.HelpCtrl', ['$scope', '$ro
var templateUrlFB = $sce.getTrustedResourceUrl(lang_fb);
$templateRequest(lang)
- .then(function (template) {
+ .then(function(template)
+ {
var elem = angular.element(document.getElementById('insertHelp'));
$compile(elem.html(template).contents())($scope);
},
- function (error) {
+ function(error)
+ {
NVRDataModel.log("Language file " + lang + " not found, falling back");
$templateRequest(templateUrlFB)
- .then(function (template) {
+ .then(function(template)
+ {
var elem = angular.element(document.getElementById('insertHelp'));
$compile(elem.html(template).contents())($scope);
},
- function (error) {
+ function(error)
+ {
NVRDataModel.log("fallback help not found");
});
}
);
-
-
}
//-------------------------------------------------------------------------
@@ -71,16 +80,13 @@ angular.module('zmApp.controllers').controller('zmApp.HelpCtrl', ['$scope', '$ro
// reset power state on exit as if it is called after we enter another
// state, that effectively overwrites current view power management needs
//------------------------------------------------------------------------
- $scope.$on('$ionicView.enter', function () {
+ $scope.$on('$ionicView.enter', function()
+ {
//console.log("**VIEW ** Help Ctrl Entered");
NVRDataModel.setAwake(false);
$scope.zmAppVersion = NVRDataModel.getAppVersion();
insertHelp();
-
-
-
-
});
-}]); \ No newline at end of file
+}]);
diff --git a/www/js/ImportantMessageCtrl.js b/www/js/ImportantMessageCtrl.js
index 29e27d2f..0f948d7f 100644
--- a/www/js/ImportantMessageCtrl.js
+++ b/www/js/ImportantMessageCtrl.js
@@ -2,18 +2,18 @@
/* jslint browser: true*/
/* global cordova,StatusBar,angular,console */
-angular.module('zmApp.controllers').controller('zmApp.ImportantMessageCtrl', ['$scope', '$ionicSideMenuDelegate', 'zm', '$stateParams', '$timeout', '$rootScope', function ($scope, $ionicSideMenuDelegate, zm, $stateParams, $timeout, $rootScope) {
- $scope.openMenu = function () {
+angular.module('zmApp.controllers').controller('zmApp.ImportantMessageCtrl', ['$scope', '$ionicSideMenuDelegate', 'zm', '$stateParams', '$timeout', '$rootScope', function($scope, $ionicSideMenuDelegate, zm, $stateParams, $timeout, $rootScope)
+{
+ $scope.openMenu = function()
+ {
$ionicSideMenuDelegate.toggleLeft();
};
-
-
-
//-------------------------------------------------------------------------
// Controller Main
//------------------------------------------------------------------------
- $scope.$on('$ionicView.enter', function () {
+ $scope.$on('$ionicView.enter', function()
+ {
console.log("**VIEW ** LowVersion Ctrl Entered");
$ionicSideMenuDelegate.canDragContent(true);
$scope.requiredVersion = zm.minAppVersion;
@@ -22,12 +22,14 @@ angular.module('zmApp.controllers').controller('zmApp.ImportantMessageCtrl', ['$
});
- $scope.openMenu = function () {
- $timeout(function () {
+ $scope.openMenu = function()
+ {
+ $timeout(function()
+ {
$rootScope.stateofSlide = $ionicSideMenuDelegate.isOpen();
}, 500);
$ionicSideMenuDelegate.toggleLeft();
};
-}]); \ No newline at end of file
+}]);
diff --git a/www/js/LogCtrl.js b/www/js/LogCtrl.js
index f204e8db..ed35f6a9 100644
--- a/www/js/LogCtrl.js
+++ b/www/js/LogCtrl.js
@@ -2,8 +2,10 @@
/* jslint browser: true*/
/* global saveAs, cordova,StatusBar,angular,console,moment */
-angular.module('zmApp.controllers').controller('zmApp.LogCtrl', ['$scope', '$rootScope', 'zm', '$ionicModal', 'NVRDataModel', '$ionicSideMenuDelegate', '$fileLogger', '$cordovaEmailComposer', '$ionicPopup', '$timeout', '$ionicHistory', '$state', '$interval', '$ionicLoading', '$translate', function ($scope, $rootScope, zm, $ionicModal, NVRDataModel, $ionicSideMenuDelegate, $fileLogger, $cordovaEmailComposer, $ionicPopup, $timeout, $ionicHistory, $state, $interval, $ionicLoading, $translate) {
- $scope.openMenu = function () {
+angular.module('zmApp.controllers').controller('zmApp.LogCtrl', ['$scope', '$rootScope', 'zm', '$ionicModal', 'NVRDataModel', '$ionicSideMenuDelegate', '$fileLogger', '$cordovaEmailComposer', '$ionicPopup', '$timeout', '$ionicHistory', '$state', '$interval', '$ionicLoading', '$translate', function($scope, $rootScope, zm, $ionicModal, NVRDataModel, $ionicSideMenuDelegate, $fileLogger, $cordovaEmailComposer, $ionicPopup, $timeout, $ionicHistory, $state, $interval, $ionicLoading, $translate)
+{
+ $scope.openMenu = function()
+ {
$ionicSideMenuDelegate.toggleLeft();
};
@@ -16,34 +18,35 @@ angular.module('zmApp.controllers').controller('zmApp.LogCtrl', ['$scope', '$roo
document.addEventListener("pause", onPause, false);
document.addEventListener("resume", onResume, false);
- function onPause() {
+ function onPause()
+ {
NVRDataModel.debug("LogCtrl: pause called, killing log timer");
// $interval.cancel(intervalLogUpdateHandle);
}
-
- function onResume() {
+ function onResume()
+ {
NVRDataModel.debug("LogCtrl: resume called, starting log timer");
- /* intervalLogUpdateHandle = $interval(function ()
- {
- loadLogs();
-
- }.bind(this), 3000);*/
-
loadLogs();
}
+ $scope.deleteLogs = function()
+ {
- $scope.deleteLogs = function () {
-
- $rootScope.zmPopup = $ionicPopup.confirm({
+ $rootScope.zmPopup = $ionicPopup.confirm(
+ {
title: $translate.instant('kPleaseConfirm'),
template: $translate.instant('kDeleteLogsConfirm'),
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
});
- $rootScope.zmPopup.then(function (res) {
- if (res) {
- $fileLogger.deleteLogfile().then(function () {
+ $rootScope.zmPopup.then(function(res)
+ {
+ if (res)
+ {
+ $fileLogger.deleteLogfile().then(function()
+ {
//console.log('Logfile deleted');
$fileLogger.setStorageFilename(zm.logFile);
$scope.log.logString = "";
@@ -55,17 +58,22 @@ angular.module('zmApp.controllers').controller('zmApp.LogCtrl', ['$scope', '$roo
//----------------------------------------------------------------
// Alarm notification handling
//----------------------------------------------------------------
- $scope.handleAlarms = function () {
+ $scope.handleAlarms = function()
+ {
$rootScope.isAlarm = !$rootScope.isAlarm;
- if (!$rootScope.isAlarm) {
+ if (!$rootScope.isAlarm)
+ {
$rootScope.alarmCount = "0";
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
- $state.go("events", {
+ $state.go("events",
+ {
"id": 0,
- "playEvent":false
- }, {
+ "playEvent": false
+ },
+ {
reload: true
});
return;
@@ -76,15 +84,20 @@ angular.module('zmApp.controllers').controller('zmApp.LogCtrl', ['$scope', '$roo
// Make sure user knows information masking is best effort
//--------------------------------------------------------------------------
- $scope.sendEmail = function (logstring) {
- $ionicPopup.confirm({
+ $scope.sendEmail = function(logstring)
+ {
+ $ionicPopup.confirm(
+ {
title: $translate.instant('kSensitiveTitle'),
- template: $rootScope.appName + ' ' + $translate.instant('kSensitiveBody')
+ template: $rootScope.appName + ' ' + $translate.instant('kSensitiveBody'),
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
})
- .then(function (res) {
- if (res)
- {
- logstring = "Logs for version:"+$scope.zmAppVersion+"\n"+logstring;
+ .then(function(res)
+ {
+ if (res)
+ {
+ logstring = "Logs for version:" + $scope.zmAppVersion + " ("+$rootScope.platformOS+")\n" + logstring;
sendEmailReally(logstring);
}
@@ -94,14 +107,14 @@ angular.module('zmApp.controllers').controller('zmApp.LogCtrl', ['$scope', '$roo
//--------------------------------------------------------------------------
// Convenience function to send logs via email
//--------------------------------------------------------------------------
- function sendEmailReally(logstring) {
- if (window.cordova) {
-
+ function sendEmailReally(logstring)
+ {
+ if (window.cordova)
+ {
// do my best to replace sensitive information
var loginData = NVRDataModel.getLogin();
-
// We don't need this anymore as log and debug now strip passwords
/*if (loginData.password !="")
{
@@ -110,35 +123,39 @@ angular.module('zmApp.controllers').controller('zmApp.LogCtrl', ['$scope', '$roo
}*/
// keep the protocol, helps to debug
var urlNoProtocol = loginData.url.replace(/.*?:\/\//, "");
- if (urlNoProtocol != "") {
+ if (urlNoProtocol != "")
+ {
var re2 = new RegExp(urlNoProtocol, "g");
// just replacing baseurl - that will take care of
// masking api but may not be cgi
logstring = logstring.replace(re2, "<server>");
}
urlNoProtocol = loginData.streamingurl.replace(/.*?:\/\//, "");
- if (urlNoProtocol != "") {
+ if (urlNoProtocol != "")
+ {
var re3 = new RegExp(urlNoProtocol, "g");
logstring = logstring.replace(re3, "<server>");
}
urlNoProtocol = loginData.eventServer.replace(/.*?:\/\//, "");
- if (urlNoProtocol != "") {
+ if (urlNoProtocol != "")
+ {
var re4 = new RegExp(urlNoProtocol, "g");
logstring = logstring.replace(re4, "<server>");
}
window.plugins.emailComposer.showEmailComposerWithCallback(callback, $rootScope.appName + ' logs', logstring, [zm.authoremail]);
-
- } else {
+ }
+ else
+ {
// console.log("Using default email client to send data");
var fname = $rootScope.appName + "-logs-" +
moment().format('MMM-DD-YY_HH-mm-ss') + ".txt";
-
- var blob = new Blob([logstring], {
+ var blob = new Blob([logstring],
+ {
type: "text/plain;charset=utf-8"
});
saveAs(blob, fname);
@@ -146,28 +163,32 @@ angular.module('zmApp.controllers').controller('zmApp.LogCtrl', ['$scope', '$roo
}
- function callback() {
+ function callback()
+ {
// console.log ("EMAIL SENT");
NVRDataModel.debug("Email sent callback called");
}
- function loadLogs() {
+ function loadLogs()
+ {
//console.log ("GETTING LOGS");
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kLoading'),
noBackdrop: true,
duration: zm.loadingTimeout
});
- $fileLogger.getLogfile().then(function (l) {
-
+ $fileLogger.getLogfile().then(function(l)
+ {
$scope.log.logString = l.split('\n').reverse().join('\n');
-
+
$ionicLoading.hide();
},
- function (error) {
+ function(error)
+ {
$scope.log.logString = "Error getting log: " + JSON.stringify(error);
$ionicLoading.hide();
});
@@ -180,7 +201,8 @@ angular.module('zmApp.controllers').controller('zmApp.LogCtrl', ['$scope', '$roo
// reset power state on exit as if it is called after we enter another
// state, that effectively overwrites current view power management needs
//------------------------------------------------------------------------
- $scope.$on('$ionicView.enter', function () {
+ $scope.$on('$ionicView.enter', function()
+ {
//console.log("**VIEW ** Log Ctrl Entered");
NVRDataModel.setAwake(false);
@@ -190,7 +212,6 @@ angular.module('zmApp.controllers').controller('zmApp.LogCtrl', ['$scope', '$roo
$scope.zmAppVersion = NVRDataModel.getAppVersion();
-
/* intervalLogUpdateHandle = $interval(function ()
{
loadLogs();
@@ -199,13 +220,12 @@ angular.module('zmApp.controllers').controller('zmApp.LogCtrl', ['$scope', '$roo
loadLogs();
-
-
});
- $scope.$on('$ionicView.leave', function () {
+ $scope.$on('$ionicView.leave', function()
+ {
//console.log ("Deleting Log interval...");
// $interval.cancel(intervalLogUpdateHandle);
});
-}]); \ No newline at end of file
+}]);
diff --git a/www/js/LoginCtrl.js b/www/js/LoginCtrl.js
index 4e4efc0a..31471ea4 100644
--- a/www/js/LoginCtrl.js
+++ b/www/js/LoginCtrl.js
@@ -2,14 +2,16 @@
/* jslint browser: true*/
/* global cordova,StatusBar,angular,console,alert,URI, localforage */
-angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$rootScope', 'zm', '$ionicModal', 'NVRDataModel', '$ionicSideMenuDelegate', '$ionicPopup', '$http', '$q', '$ionicLoading', 'zmAutoLogin', '$cordovaPinDialog', 'EventServer', '$ionicHistory', '$state', '$ionicActionSheet', 'SecuredPopups', '$stateParams', '$translate', function ($scope, $rootScope, zm, $ionicModal, NVRDataModel, $ionicSideMenuDelegate, $ionicPopup, $http, $q, $ionicLoading, zmAutoLogin, $cordovaPinDialog, EventServer, $ionicHistory, $state, $ionicActionSheet, SecuredPopups, $stateParams, $translate) {
- $scope.openMenu = function () {
-
- saveItems(false);
+angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$rootScope', 'zm', '$ionicModal', 'NVRDataModel', '$ionicSideMenuDelegate', '$ionicPopup', '$http', '$q', '$ionicLoading', 'zmAutoLogin', '$cordovaPinDialog', 'EventServer', '$ionicHistory', '$state', '$ionicActionSheet', 'SecuredPopups', '$stateParams', '$translate', function($scope, $rootScope, zm, $ionicModal, NVRDataModel, $ionicSideMenuDelegate, $ionicPopup, $http, $q, $ionicLoading, zmAutoLogin, $cordovaPinDialog, EventServer, $ionicHistory, $state, $ionicActionSheet, SecuredPopups, $stateParams, $translate)
+{
+ $scope.openMenu = function()
+ {
+
+ if ($scope.loginData.serverName)
+ saveItems(false);
$ionicSideMenuDelegate.toggleLeft();
-
- };
+ };
var oldName;
var serverbuttons = [];
@@ -24,72 +26,87 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
$scope.check.isUseAuth = ($scope.loginData.isUseAuth) ? true : false;
$scope.check.isUseEventServer = ($scope.loginData.isUseEventServer == true) ? true : false;
-
document.addEventListener("pause", onPause, false);
document.addEventListener("resume", onResume, false);
- function onResume() {
+ function onResume()
+ {
NVRDataModel.log("Login screen resumed");
}
- function onPause() {
+ function onPause()
+ {
NVRDataModel.log("Login screen going to background, saving data");
localforage.setItem("settings-temp-data", $scope.loginData);
}
-
//----------------------------------------------------------------
// Alarm notification handling
//----------------------------------------------------------------
- $scope.handleAlarms = function () {
+ $scope.handleAlarms = function()
+ {
$rootScope.isAlarm = !$rootScope.isAlarm;
- if (!$rootScope.isAlarm) {
+ if (!$rootScope.isAlarm)
+ {
$rootScope.alarmCount = "0";
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
- $state.go("events", {
+ $state.go("events",
+ {
"id": 0,
- "playEvent":false
- }, {
+ "playEvent": false
+ },
+ {
reload: true
});
return;
}
-
+
};
//----------------------------------------------------------------
// Specifies a linked profile to try if this profile fails
//----------------------------------------------------------------
- $scope.selectFallback = function () {
+ $scope.selectFallback = function()
+ {
var as = Object.keys(NVRDataModel.getServerGroups());
- if (as.length < 2) {
- $rootScope.zmPopup = SecuredPopups.show('alert', {
+ if (as.length < 2)
+ {
+ $rootScope.zmPopup = SecuredPopups.show('alert',
+ {
title: $translate.instant('kError'),
- template: $translate.instant('kFallback2Configs')
+ template: $translate.instant('kFallback2Configs'),
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
});
return;
}
- var ab = [{
+ var ab = [
+ {
text: $translate.instant('kClear')
}];
var ld = NVRDataModel.getLogin();
- as.forEach(function (item) {
- if (item != ld.serverName) ab.push({
+ as.forEach(function(item)
+ {
+ if (item != ld.serverName) ab.push(
+ {
text: item
});
});
- var sheet = $ionicActionSheet.show({
+ var sheet = $ionicActionSheet.show(
+ {
buttons: ab,
titleText: $translate.instant('kSelectFallback'),
cancelText: $translate.instant('kButtonCancel'),
- cancel: function () {},
- buttonClicked: function (index) {
+ cancel: function() {},
+ buttonClicked: function(index)
+ {
//console.log ("YOU WANT " + ab[index].text + index);
if (index == 0)
$scope.loginData.fallbackConfiguration = "";
@@ -100,27 +117,30 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
}
});
-
-
};
//----------------------------------------------------------------
// This is called when the user changes profiles
//----------------------------------------------------------------
- $scope.serverActionSheet = function () {
- var hideSheet = $ionicActionSheet.show({
+ $scope.serverActionSheet = function()
+ {
+ var hideSheet = $ionicActionSheet.show(
+ {
buttons: serverbuttons,
destructiveText: $translate.instant('kDelete'),
titleText: $translate.instant('kManageServerGroups'),
cancelText: $translate.instant('kButtonCancel'),
- cancel: function () {
+ cancel: function()
+ {
// add cancel code..
},
- buttonClicked: function (index) {
+ buttonClicked: function(index)
+ {
//console.log ("YOU WANT " + serverbuttons[index].text + " INDEX " + index);
- if (serverbuttons[index].text == $translate.instant('kServerAdd') + "...") {
+ if (serverbuttons[index].text == $translate.instant('kServerAdd') + "...")
+ {
$scope.loginData = angular.copy(NVRDataModel.getDefaultLoginObject());
return true;
@@ -131,7 +151,6 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
//console.log ("NEW LOGIN OBJECT IS " + JSON.stringify($scope.loginData));
-
$scope.check.isUseAuth = ($scope.loginData.isUseAuth) ? true : false;
$scope.check.isUseEventServer = ($scope.loginData.isUseEventServer == true) ? true : false;
@@ -147,19 +166,20 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
return true;
},
- destructiveButtonClicked: function () {
+ destructiveButtonClicked: function()
+ {
-
- if (!$scope.loginData.serverName) {
+ if (!$scope.loginData.serverName)
+ {
NVRDataModel.debug("cannot delete empty entry");
return true;
-
}
var zmServers = NVRDataModel.getServerGroups();
//console.log ("YOU WANT TO DELETE " + $scope.loginData.serverName);
//console.log ("LENGTH OF SERVERS IS " + Object.keys(zmServers).length);
- if (Object.keys(zmServers).length > 1) {
+ if (Object.keys(zmServers).length > 1)
+ {
NVRDataModel.log("Deleting " + $scope.loginData.serverName);
delete zmServers[$scope.loginData.serverName];
@@ -171,33 +191,37 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
NVRDataModel.setLogin($scope.loginData);
availableServers = Object.keys(NVRDataModel.getServerGroups());
- serverbuttons = [{
+ serverbuttons = [
+ {
text: $translate.instant('kServerAdd') + "..."
}];
- for (var servIter = 0; servIter < availableServers.length; servIter++) {
- serverbuttons.push({
+ for (var servIter = 0; servIter < availableServers.length; servIter++)
+ {
+ serverbuttons.push(
+ {
text: availableServers[servIter]
});
//console.log("ADDING : " + availableServers[servIter]);
}
//console.log (">>>>>>>delete: server buttons " + JSON.stringify(serverbuttons));
- } else {
+ }
+ else
+ {
NVRDataModel.displayBanner('error', [$translate.instant('kBannerCannotDeleteNeedOne')]);
}
return true;
}
-
});
};
-
//----------------------------------------------------------------
// This is when you tap on event server settings
//----------------------------------------------------------------
- $scope.eventServerSettings = function () {
+ $scope.eventServerSettings = function()
+ {
NVRDataModel.debug("Saving settings before going to Event Server settings");
//console.log ( "My loginData saved " + JSON.stringify($scope.loginData));
NVRDataModel.setLogin($scope.loginData);
@@ -205,8 +229,6 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
return;
};
-
-
//-------------------------------------------------------------------------
// Lets make sure we set screen dim properly as we enter
// The problem is we enter other states before we leave previous states
@@ -214,73 +236,81 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
// reset power state on exit as if it is called after we enter another
// state, that effectively overwrites current view power management needs
//------------------------------------------------------------------------
- $scope.$on('$ionicView.enter', function () {
+ $scope.$on('$ionicView.enter', function()
+ {
//console.log("**VIEW ** LoginCtrl Entered");
NVRDataModel.setAwake(false);
var ld = NVRDataModel.getLogin();
oldName = ld.serverName;
availableServers = Object.keys(NVRDataModel.getServerGroups());
- serverbuttons = [{
+ serverbuttons = [
+ {
text: $translate.instant('kServerAdd') + "..."
}];
- for (var servIter = 0; servIter < availableServers.length; servIter++) {
- serverbuttons.push({
+ for (var servIter = 0; servIter < availableServers.length; servIter++)
+ {
+ serverbuttons.push(
+ {
text: availableServers[servIter]
});
-
//console.log (">>>>>>>ionicview enter: server buttons " + JSON.stringify(serverbuttons));
}
-
-
NVRDataModel.debug("Does login need to hear the wizard? " + $stateParams.wizard);
- if ($stateParams.wizard == "true") {
+ if ($stateParams.wizard == "true")
+ {
NVRDataModel.log("Creating new login entry for wizard");
$scope.loginData = angular.copy(NVRDataModel.getDefaultLoginObject());
$scope.loginData.serverName = $rootScope.wizard.serverName;
$scope.loginData.url = $rootScope.wizard.loginURL;
$scope.loginData.apiurl = $rootScope.wizard.apiURL;
$scope.loginData.streamingurl = $rootScope.wizard.streamingURL;
- if ($rootScope.wizard.useauth && $rootScope.wizard.usezmauth) {
+ if ($rootScope.wizard.useauth && $rootScope.wizard.usezmauth)
+ {
$scope.loginData.username = $rootScope.wizard.zmuser;
$scope.loginData.password = $rootScope.wizard.zmpassword;
- } else {
+ }
+ else
+ {
$scope.loginData.isUseAuth = false;
}
- if ((/^https:\/\//i.test($scope.loginData.url))) {
+ if ((/^https:\/\//i.test($scope.loginData.url)))
+ {
$scope.loginData.useSSL = true;
}
-
- } else {
+ }
+ else
+ {
var savedData;
- localforage.getItem("settings-temp-data").then(function (value) {
+ localforage.getItem("settings-temp-data").then(function(value)
+ {
savedData = value;
//= zmStorageService.getObject ("settings-temp-data");
- if (!NVRDataModel.isEmpty(savedData)) {
+ if (!NVRDataModel.isEmpty(savedData))
+ {
$scope.loginData = savedData;
NVRDataModel.log("retrieved pre-stored loginData on past pause: " + JSON.stringify($scope.loginData));
localforage.removeItem("settings-temp-data");
//zmStorageService.setObject("settings-temp-data", {});
- } else {
+ }
+ else
+ {
NVRDataModel.log("Not recovering login data as its empty");
}
});
}
-
});
-
- $scope.$on('$ionicView.beforeLeave', function () {
+ $scope.$on('$ionicView.beforeLeave', function()
+ {
//console.log("**VIEW ** LoginCtrl Entered");
-
-
});
//----------------------------------------------------------------
@@ -315,54 +345,65 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
}
});*/
- $rootScope.$on('$stateChangeSuccess', function () {
+ $rootScope.$on('$stateChangeSuccess', function()
+ {
$scope.ignoreDirty = false;
});
// Make a noble attempt at deciphering
-
-
//--------------------------------------------------------------------------
// When PIN is enabled, this is called to specify a PIN
// FIXME: Get rid of cordovaPinDialog. It's really not needed
//--------------------------------------------------------------------------
- $scope.pinPrompt = function (evt) {
+ $scope.pinPrompt = function(evt)
+ {
NVRDataModel.log("Password prompt");
- if ($scope.loginData.usePin) {
+ if ($scope.loginData.usePin)
+ {
$scope.loginData.pinCode = "";
$cordovaPinDialog.prompt($translate.instant('kEnterPin'), $translate.instant('kPinProtect')).then(
- function (result1) {
+ function(result1)
+ {
// console.log (JSON.stringify(result1));
- if (result1.input1 && result1.buttonIndex == 1) {
+ if (result1.input1 && result1.buttonIndex == 1)
+ {
$cordovaPinDialog.prompt($translate.instant('kReconfirmPin'), $translate.instant('kPinProtect'))
- .then(function (result2) {
- if (result1.input1 == result2.input1) {
+ .then(function(result2)
+ {
+ if (result1.input1 == result2.input1)
+ {
NVRDataModel.log("Pin code match");
$scope.loginData.pinCode = result1.input1;
- } else {
+ }
+ else
+ {
NVRDataModel.log("Pin code mismatch");
$scope.loginData.usePin = false;
NVRDataModel.displayBanner('error', [$translate.instant('kBannerPinMismatch')]);
}
},
- function (error) {
+ function(error)
+ {
//console.log("Error inside");
$scope.loginData.usePin = false;
});
- } else {
+ }
+ else
+ {
$scope.loginData.usePin = false;
}
},
- function (error) {
+ function(error)
+ {
//console.log("Error outside");
$scope.loginData.usePin = false;
});
-
-
- } else {
+ }
+ else
+ {
NVRDataModel.debug("Password disabled");
}
};
@@ -371,47 +412,56 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
// Makes input easier
//-------------------------------------------------------------------------------
- $scope.portalKeypress = function (evt) {
+ $scope.portalKeypress = function(evt)
+ {
- if (/^https:\/\//i.test($scope.loginData.url)) {
+ if (/^https:\/\//i.test($scope.loginData.url))
+ {
$scope.loginData.useSSL = true;
- } else {
+ }
+ else
+ {
$scope.loginData.useSSL = false;
}
- if ($scope.loginData.url.slice(-1) == '/') {
+ if ($scope.loginData.url.slice(-1) == '/')
+ {
$scope.loginData.apiurl = $scope.loginData.url + "api";
$scope.loginData.streamingurl = $scope.loginData.url + "cgi-bin";
- } else {
+ }
+ else
+ {
$scope.loginData.apiurl = $scope.loginData.url + "/api";
$scope.loginData.streamingurl = $scope.loginData.url + "/cgi-bin";
}
-
};
//-------------------------------------------------------------------------------
// Adds http to url if not present
// http://stackoverflow.com/questions/11300906/check-if-a-string-starts-with-http-using-javascript
//-------------------------------------------------------------------------------
- function addhttp(url) {
+ function addhttp(url)
+ {
- if ((!/^(f|ht)tps?:\/\//i.test(url)) && (url != "")) {
+ if ((!/^(f|ht)tps?:\/\//i.test(url)) && (url != ""))
+ {
url = "http://" + url;
}
return url;
}
+ function addWsOrWss(url)
+ {
- function addWsOrWss(url) {
-
- if ((!/^wss?:\/\//i.test(url)) && (url != "")) {
+ if ((!/^wss?:\/\//i.test(url)) && (url != ""))
+ {
url = "ws://" + url;
}
return url;
}
-
- function endsWith(str, suffix) {
+ function endsWith(str, suffix)
+ {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
@@ -419,15 +469,14 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
// Perform the login action when the user submits the login form
//-----------------------------------------------------------------------------
- function saveItems(showalert) {
-
+ function saveItems(showalert)
+ {
//console.log ("*********** SAVE ITEMS CALLED ");
//console.log('Saving login');
NVRDataModel.setFirstUse(false);
-
+
// used for menu display
-
// lets so some basic sanitization of the data
// I am already adding "/" so lets remove spurious ones
@@ -443,29 +492,29 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
$scope.loginData.username = $scope.loginData.username.trim();
-
-
-
$scope.loginData.isUseAuth = ($scope.check.isUseAuth) ? true : false;
$scope.loginData.isUseEventServer = ($scope.check.isUseEventServer) ? true : false;
- if ($scope.loginData.url.slice(-1) == '/') {
+ if ($scope.loginData.url.slice(-1) == '/')
+ {
$scope.loginData.url = $scope.loginData.url.slice(0, -1);
}
- if ($scope.loginData.apiurl.slice(-1) == '/') {
+ if ($scope.loginData.apiurl.slice(-1) == '/')
+ {
$scope.loginData.apiurl = $scope.loginData.apiurl.slice(0, -1);
}
-
- if ($scope.loginData.streamingurl.slice(-1) == '/') {
+ if ($scope.loginData.streamingurl.slice(-1) == '/')
+ {
$scope.loginData.streamingurl = $scope.loginData.streamingurl.slice(0, -1);
}
- if ($scope.loginData.eventServer.slice(-1) == '/') {
+ if ($scope.loginData.eventServer.slice(-1) == '/')
+ {
$scope.loginData.eventServer = $scope.loginData.eventServer.slice(0, -1);
}
@@ -483,15 +532,17 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
$scope.loginData.streamingurl = addhttp($scope.loginData.streamingurl);
$scope.loginData.eventServer = addWsOrWss($scope.loginData.eventServer);
- if ($scope.loginData.useSSL) {
+ if ($scope.loginData.useSSL)
+ {
// replace all http with https
$scope.loginData.url = $scope.loginData.url.replace("http:", "https:");
$scope.loginData.apiurl = $scope.loginData.apiurl.replace("http:", "https:");
$scope.loginData.streamingurl = $scope.loginData.streamingurl.replace("http:", "https:");
$scope.loginData.eventServer = $scope.loginData.eventServer.replace("ws:", "wss:");
-
- } else {
+ }
+ else
+ {
// replace all https with http
$scope.loginData.url = $scope.loginData.url.replace("https:", "http:");
$scope.loginData.apiurl = $scope.loginData.apiurl.replace("https:", "http:");
@@ -502,27 +553,29 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
var apiurl = $scope.loginData.apiurl + '/host/getVersion.json';
var portalurl = $scope.loginData.url + '/index.php';
-
-
// Check if isUseAuth is set make sure u/p have a dummy value
- if ($scope.check.isUseAuth) {
+ if ($scope.check.isUseAuth)
+ {
if (!$scope.loginData.username) $scope.loginData.username = "x";
if (!$scope.loginData.password) $scope.loginData.password = "x";
//NVRDataModel.log("Authentication is disabled, setting dummy user & pass");
}
- if (parseInt($scope.loginData.maxMontage) <= 0) {
+ if (parseInt($scope.loginData.maxMontage) <= 0)
+ {
$scope.loginData.maxMontage = "100";
}
-
// do this before setLogin so message is sent
- if (!$scope.check.isUseEventServer) {
+ if (!$scope.check.isUseEventServer)
+ {
$rootScope.isAlarm = 0;
- if ($rootScope.apnsToken) {
+ if ($rootScope.apnsToken)
+ {
NVRDataModel.log("Making sure we don't get push notifications");
- EventServer.sendMessage('push', {
+ EventServer.sendMessage('push',
+ {
type: 'token',
platform: $rootScope.platformOS,
token: $rootScope.apnsToken,
@@ -532,23 +585,27 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
}
NVRDataModel.setLogin($scope.loginData);
-
+
$rootScope.runMode = NVRDataModel.getBandwidth();
-
+
oldName = $scope.loginData.serverName;
- if ($scope.check.isUseEventServer) {
+ if ($scope.check.isUseEventServer)
+ {
EventServer.init();
- if ($rootScope.apnsToken && $scope.loginData.disablePush != true) {
+ if ($rootScope.apnsToken && $scope.loginData.disablePush != true)
+ {
NVRDataModel.log("Making sure we get push notifications");
- EventServer.sendMessage('push', {
+ EventServer.sendMessage('push',
+ {
type: 'token',
platform: $rootScope.platformOS,
token: $rootScope.apnsToken,
state: "enabled"
}, 1);
}
- EventServer.sendMessage("control", {
+ EventServer.sendMessage("control",
+ {
type: 'filter',
monlist: $scope.loginData.eventServerMonitors,
intlist: $scope.loginData.eventServerInterval
@@ -556,19 +613,21 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
}
-
// lets logout
NVRDataModel.debug("Logging out of current session...");
$rootScope.authSession = "undefined";
- $http({
+ $http(
+ {
method: 'POST',
//withCredentials: true,
url: $scope.loginData.url + '/index.php',
- headers: {
+ headers:
+ {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json',
},
- transformRequest: function (obj) {
+ transformRequest: function(obj)
+ {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" +
@@ -577,12 +636,14 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
return params;
},
- data: {
+ data:
+ {
action: "logout",
view: "login"
}
})
- .finally(function (ans) {
+ .finally(function(ans)
+ {
zmAutoLogin.doLogin("<button class='button button-clear' style='line-height: normal; min-height: 0; min-width: 0; color:#fff;' ng-click='$root.cancelAuth()'><i class='ion-close-circled'></i>&nbsp;" + $translate.instant('kAuthenticating') + "...</button>")
// Do the happy menu only if authentication works
@@ -590,13 +651,15 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
// fail in app.js that will be called to show an error
// box
- .then(function (data) {
+ .then(function(data)
+ {
// Now let's validate if the API works
// note that due to reachability, it might have switched to another server
- if ($scope.loginData.serverName != NVRDataModel.getLogin().serverName) {
+ if ($scope.loginData.serverName != NVRDataModel.getLogin().serverName)
+ {
NVRDataModel.debug(">>> Server information has changed, likely a fallback took over!");
$scope.loginData = NVRDataModel.getLogin();
apiurl = $scope.loginData.apiurl + '/host/getVersion.json';
@@ -608,16 +671,17 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
NVRDataModel.log("Validating APIs at " + apiurl);
$http.get(apiurl)
- .success(function (data) {
+ .success(function(data)
+ {
+ NVRDataModel.getTimeZone(true);
var loginStatus = $translate.instant('kExploreEnjoy') + " " + $rootScope.appName + "!";
EventServer.refresh();
-
-
// now grab and report PATH_ZMS
NVRDataModel.getPathZms()
- .then(function (data) {
+ .then(function(data)
+ {
var ld = NVRDataModel.getLogin();
var zm_cgi = data.toLowerCase();
@@ -625,27 +689,35 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
NVRDataModel.log("ZM relative cgi-path: " + zm_cgi + ", you entered: " + user_cgi);
$http.get(ld.streamingurl + "/zms")
- .success(function (data) {
+ .success(function(data)
+ {
NVRDataModel.debug("Urk! cgi-path returned success, but it should not have come here");
loginStatus = $translate.instant('kLoginStatusNoCgi');
NVRDataModel.debug("refreshing API version...");
NVRDataModel.getAPIversion()
- .then(function (data) {
+ .then(function(data)
+ {
var refresh = NVRDataModel.getMonitors(1);
$rootScope.apiVersion = data;
},
- function (error) {
+ function(error)
+ {
var refresh = NVRDataModel.getMonitors(1);
$rootScope.apiVersion = "0.0.0";
NVRDataModel.debug("Error, failed API version, setting to " + $rootScope.apiVersion);
});
- if (showalert) {
- $rootScope.zmPopup = SecuredPopups.show('alert', {
+ if (showalert)
+ {
+ $rootScope.zmPopup = SecuredPopups.show('alert',
+ {
title: $translate.instant('kLoginValidatedTitle'),
- template: loginStatus
- }).then(function (res) {
+ template: loginStatus,
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
+ }).then(function(res)
+ {
$ionicSideMenuDelegate.toggleLeft();
NVRDataModel.debug("Force reloading monitors...");
@@ -653,19 +725,26 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
});
}
})
- .error(function (error, status) {
+ .error(function(error, status)
+ {
// If its 5xx, then the cgi-bin path is valid
// if its 4xx then the cgi-bin path is not valid
- if (status < 500) {
+ if (status < 500)
+ {
loginStatus = $translate.instant('kLoginStatusNoCgiAlt');
}
- if (showalert) {
- $rootScope.zmPopup = SecuredPopups.show('alert', {
+ if (showalert)
+ {
+ $rootScope.zmPopup = SecuredPopups.show('alert',
+ {
title: $translate.instant('kLoginValidatedTitle'),
- template: loginStatus
- }).then(function (res) {
+ template: loginStatus,
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
+ }).then(function(res)
+ {
$ionicSideMenuDelegate.toggleLeft();
NVRDataModel.debug("Force reloading monitors...");
@@ -674,11 +753,13 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
}
NVRDataModel.debug("refreshing API version...");
NVRDataModel.getAPIversion()
- .then(function (data) {
+ .then(function(data)
+ {
var refresh = NVRDataModel.getMonitors(1);
$rootScope.apiVersion = data;
},
- function (error) {
+ function(error)
+ {
var refresh = NVRDataModel.getMonitors(1);
$rootScope.apiVersion = "0.0.0";
NVRDataModel.debug("Error, failed API version, setting to " + $rootScope.apiVersion);
@@ -687,22 +768,22 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
});
});
-
-
})
- .error(function (error) {
+ .error(function(error)
+ {
NVRDataModel.displayBanner('error', [$translate.instant('kBannerAPICheckFailed'), $translate.instant('kBannerPleaseCheck')]);
NVRDataModel.log("API login error " + JSON.stringify(error));
- $rootScope.zmPopup = SecuredPopups.show('alert', {
+ $rootScope.zmPopup = SecuredPopups.show('alert',
+ {
title: $translate.instant('kLoginValidAPIFailedTitle'),
- template: $translate.instant('kBannerPleaseCheck')
+ template: $translate.instant('kBannerPleaseCheck'),
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
});
});
});
-
-
});
}
@@ -711,24 +792,33 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
// calling saveItems also updates the defaultServer
//-----------------------------------------------
- $scope.saveItems = function () {
+ $scope.saveItems = function()
+ {
- if (!$scope.loginData.serverName) {
- $rootScope.zmPopup = $ionicPopup.alert({
+ if (!$scope.loginData.serverName)
+ {
+ $rootScope.zmPopup = $ionicPopup.alert(
+ {
title: $translate.instant('kError'),
template: $translate.instant('kServerEmptyError'),
})
- .then(function (res) {
+ .then(function(res)
+ {
return;
});
- } else {
+ }
+ else
+ {
saveItems(true);
availableServers = Object.keys(NVRDataModel.getServerGroups());
- serverbuttons = [{
+ serverbuttons = [
+ {
text: $translate.instant('kServerAdd') + "..."
}];
- for (var servIter = 0; servIter < availableServers.length; servIter++) {
- serverbuttons.push({
+ for (var servIter = 0; servIter < availableServers.length; servIter++)
+ {
+ serverbuttons.push(
+ {
text: availableServers[servIter]
});
}
@@ -736,8 +826,6 @@ angular.module('zmApp.controllers').controller('zmApp.LoginCtrl', ['$scope', '$r
}
-
};
-
-}]); \ No newline at end of file
+}]);
diff --git a/www/js/LowVersionCtrl.js b/www/js/LowVersionCtrl.js
index 3ceda222..a7fbb7ff 100644
--- a/www/js/LowVersionCtrl.js
+++ b/www/js/LowVersionCtrl.js
@@ -2,18 +2,18 @@
/* jslint browser: true*/
/* global cordova,StatusBar,angular,console */
-angular.module('zmApp.controllers').controller('zmApp.LowVersionCtrl', ['$scope','$ionicSideMenuDelegate', 'zm', '$stateParams', function ($scope,$ionicSideMenuDelegate,zm, $stateParams) {
-$scope.openMenu = function () {
- $ionicSideMenuDelegate.toggleLeft();
- };
-
-
-
+angular.module('zmApp.controllers').controller('zmApp.LowVersionCtrl', ['$scope', '$ionicSideMenuDelegate', 'zm', '$stateParams', function($scope, $ionicSideMenuDelegate, zm, $stateParams)
+{
+ $scope.openMenu = function()
+ {
+ $ionicSideMenuDelegate.toggleLeft();
+ };
//-------------------------------------------------------------------------
// Controller Main
//------------------------------------------------------------------------
- $scope.$on('$ionicView.enter', function () {
+ $scope.$on('$ionicView.enter', function()
+ {
//console.log("**VIEW ** LowVersion Ctrl Entered");
$ionicSideMenuDelegate.canDragContent(true);
$scope.requiredVersion = zm.minAppVersion;
diff --git a/www/js/MenuController.js b/www/js/MenuController.js
index 94e15b0a..071db4df 100644
--- a/www/js/MenuController.js
+++ b/www/js/MenuController.js
@@ -2,52 +2,54 @@
/* jslint browser: true*/
/* global cordova,StatusBar,angular,console */
-angular.module('zmApp.controllers').controller('MenuController', ['$scope','$ionicSideMenuDelegate', 'zm', '$stateParams', '$ionicHistory','$state', 'NVRDataModel', '$rootScope', '$ionicPopup', '$translate', function ($scope,$ionicSideMenuDelegate,zm, $stateParams, $ionicHistory, $state, NVRDataModel, $rootScope, $ionicPopup, $translate) {
-$scope.openMenu = function () {
- $ionicSideMenuDelegate.toggleLeft();
- };
-
-//----------------------------------------------------------------
-// This controller sits along with the main app to bring up
-// the language menu from the main menu
-//----------------------------------------------------------------
+angular.module('zmApp.controllers').controller('MenuController', ['$scope', '$ionicSideMenuDelegate', 'zm', '$stateParams', '$ionicHistory', '$state', 'NVRDataModel', '$rootScope', '$ionicPopup', '$translate', function($scope, $ionicSideMenuDelegate, zm, $stateParams, $ionicHistory, $state, NVRDataModel, $rootScope, $ionicPopup, $translate)
+{
+ $scope.openMenu = function()
+ {
+ $ionicSideMenuDelegate.toggleLeft();
+ };
+
+ //----------------------------------------------------------------
+ // This controller sits along with the main app to bring up
+ // the language menu from the main menu
+ //----------------------------------------------------------------
$scope.switchLang = function()
{
$scope.lang = NVRDataModel.getLanguages();
- $scope.myopt = {lang:""};
-
- $rootScope.zmPopup = $ionicPopup.show({
+ $scope.myopt = {
+ lang: ""
+ };
+
+ $rootScope.zmPopup = $ionicPopup.show(
+ {
scope: $scope,
template: '<ion-radio-fix ng-repeat="item in lang" ng-value="item.value" ng-model="myopt.lang"> {{item.text}} </ion-radio-fix>',
-
title: $translate.instant('kSelectLanguage'),
-
+
buttons: [
+ {
+ text: $translate.instant('kButtonCancel'),
+ onTap: function(e)
{
- text: $translate.instant('kButtonCancel'),
- onTap: function (e) {
- //return "CANCEL";
- }
+ //return "CANCEL";
+ }
- },
+ },
+ {
+ text: $translate.instant('kButtonOk'),
+ onTap: function(e)
{
- text: $translate.instant('kButtonOk'),
- onTap: function (e) {
- NVRDataModel.log("Language selected:"+$scope.myopt.lang);
- NVRDataModel.setDefaultLanguage($scope.myopt.lang, true);
- $rootScope.$emit('language-changed');
-
-
- //return "OK";
-
- }
- }
- ]
+ NVRDataModel.log("Language selected:" + $scope.myopt.lang);
+ NVRDataModel.setDefaultLanguage($scope.myopt.lang, true);
+ $rootScope.$emit('language-changed');
+
+ //return "OK";
+
+ }
+ }]
});
-
-
+
};
-
-
+
}]);
diff --git a/www/js/MonitorCtrl.js b/www/js/MonitorCtrl.js
index e9ec2f4b..6a91a52e 100644
--- a/www/js/MonitorCtrl.js
+++ b/www/js/MonitorCtrl.js
@@ -7,8 +7,8 @@
angular.module('zmApp.controllers')
.controller('zmApp.MonitorCtrl', ['$ionicPopup', 'zm', '$scope', 'NVRDataModel', 'message', '$ionicSideMenuDelegate', '$ionicLoading', '$ionicModal', '$state', '$http', '$rootScope', '$timeout', '$ionicHistory', '$ionicPlatform', '$translate', '$q',
- function ($ionicPopup, zm, $scope, NVRDataModel, message, $ionicSideMenuDelegate, $ionicLoading, $ionicModal, $state, $http, $rootScope, $timeout, $ionicHistory, $ionicPlatform, $translate, $q) {
-
+ function($ionicPopup, zm, $scope, NVRDataModel, message, $ionicSideMenuDelegate, $ionicLoading, $ionicModal, $state, $http, $rootScope, $timeout, $ionicHistory, $ionicPlatform, $translate, $q)
+ {
//-----------------------------------------------------------------------
// Controller Main
@@ -18,8 +18,6 @@ angular.module('zmApp.controllers')
// console.log("***EVENTS: Waiting for Monitors to load before I proceed");
-
-
var loginData;
// --------------------------------------------------------
@@ -27,19 +25,26 @@ angular.module('zmApp.controllers')
// close the modal
// --------------------------------------------------------
- $ionicPlatform.registerBackButtonAction(function (e) {
+ $ionicPlatform.registerBackButtonAction(function(e)
+ {
e.preventDefault();
- if ($scope.modal != undefined && $scope.modal.isShown()) {
+ if ($scope.modal != undefined && $scope.modal.isShown())
+ {
// switch off awake, as liveview is finished
NVRDataModel.debug("Modal is open, closing it");
NVRDataModel.setAwake(false);
$scope.modal.remove();
- } else {
+ }
+ else
+ {
NVRDataModel.debug("Modal is closed, so toggling or exiting");
- if (!$ionicSideMenuDelegate.isOpenLeft()) {
+ if (!$ionicSideMenuDelegate.isOpenLeft())
+ {
$ionicSideMenuDelegate.toggleLeft();
- } else {
+ }
+ else
+ {
navigator.app.exitApp();
}
@@ -47,28 +52,31 @@ angular.module('zmApp.controllers')
}, 1000);
-
- $scope.openMenu = function () {
+ $scope.openMenu = function()
+ {
$ionicSideMenuDelegate.toggleLeft();
};
-
//----------------------------------------------------------------
// Alarm notification handling
//----------------------------------------------------------------
- $scope.handleAlarms = function () {
+ $scope.handleAlarms = function()
+ {
$rootScope.isAlarm = !$rootScope.isAlarm;
- if (!$rootScope.isAlarm) {
+ if (!$rootScope.isAlarm)
+ {
$rootScope.alarmCount = "0";
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
-
- $state.go("events", {
+ $state.go("events",
+ {
"id": 0,
- "playEvent":false
- }, {
+ "playEvent": false
+ },
+ {
reload: true
});
return;
@@ -79,10 +87,12 @@ angular.module('zmApp.controllers')
// For now, I've only limited it to enable/disable and change monitor mode
// and changing monitor function
//-----------------------------------------------------------------------
- $scope.changeConfig = function (monitorName, monitorId, enabled, func) {
+ $scope.changeConfig = function(monitorName, monitorId, enabled, func)
+ {
var checked = false;
- if (monitorName == 'All') {
+ if (monitorName == 'All')
+ {
monitorName = $translate.instant('kAll');
}
@@ -91,40 +101,43 @@ angular.module('zmApp.controllers')
//if monitorId is not specified, all monitors will be changed
var monitorsIds = [];
- if (monitorId == '') {
- for (var i = 0; i < $scope.monitors.length; i++) {
+ if (monitorId == '')
+ {
+ for (var i = 0; i < $scope.monitors.length; i++)
+ {
monitorsIds[i] = $scope.monitors[i].Monitor.Id;
}
- } else {
+ }
+ else
+ {
monitorsIds[0] = monitorId;
}
$scope.monFunctions = [
- {
- text: $translate.instant('kMonModect'),
- value: "Modect"
- },
- {
- text: $translate.instant('kMonMocord'),
- value: "Mocord"
- },
- {
- text: $translate.instant('kMonRecord'),
- value: "Record"
- },
- {
- text: $translate.instant('kMonNodect'),
- value: "Nodect"
- },
- {
- text: $translate.instant('kMonMonitor'),
- value: "Monitor"
- },
- {
- text: $translate.instant('kMonNone'),
- value: "None"
- }
- ];
+ {
+ text: $translate.instant('kMonModect'),
+ value: "Modect"
+ },
+ {
+ text: $translate.instant('kMonMocord'),
+ value: "Mocord"
+ },
+ {
+ text: $translate.instant('kMonRecord'),
+ value: "Record"
+ },
+ {
+ text: $translate.instant('kMonNodect'),
+ value: "Nodect"
+ },
+ {
+ text: $translate.instant('kMonMonitor'),
+ value: "Monitor"
+ },
+ {
+ text: $translate.instant('kMonNone'),
+ value: "None"
+ }];
$scope.monfunc = {
mymonitorsIds: monitorsIds,
@@ -134,134 +147,140 @@ angular.module('zmApp.controllers')
mypromises: []
};
- $rootScope.zmPopup = $ionicPopup.show({
+ $rootScope.zmPopup = $ionicPopup.show(
+ {
scope: $scope,
template: '<ion-toggle ng-model="monfunc.myenabled" ng-checked="monfunc.myenabled" toggle-class="toggle-calm">Enabled</ion-toggle><ion-radio-fix ng-repeat="item in monFunctions" ng-value="item.value" ng-model="monfunc.myfunc"> {{item.text}} </ion-radio-fix>',
-
title: $translate.instant('kChangeSettingsFor') + ' ' + monitorName,
buttons: [
- {
- text: $translate.instant('kButtonCancel'),
+ {
+ text: $translate.instant('kButtonCancel'),
- },
+ },
+ {
+ text: $translate.instant('kButtonSave'),
+ onTap: function(e)
{
- text: $translate.instant('kButtonSave'),
- onTap: function (e) {
- $scope.monfunc.mymonitorsIds.forEach(function (item, index) {
- NVRDataModel.debug("MonitorCtrl:changeConfig selection:" + $scope.monfunc.myenabled +
- $scope.monfunc.myfunc);
- var loginData = NVRDataModel.getLogin();
- var apiRestart = loginData.apiurl + "/states/change/restart.json";
- var apiMon = loginData.apiurl + "/monitors/" + item + ".json";
+ $scope.monfunc.mymonitorsIds.forEach(function(item, index)
+ {
+ NVRDataModel.debug("MonitorCtrl:changeConfig selection:" + $scope.monfunc.myenabled +
+ $scope.monfunc.myfunc);
+ var loginData = NVRDataModel.getLogin();
+ var apiRestart = loginData.apiurl + "/states/change/restart.json";
+ var apiMon = loginData.apiurl + "/monitors/" + item + ".json";
+
+ NVRDataModel.debug("MonitorCtrl: URLs for changeConfig save:" + apiMon);
+
+ var isEnabled = "";
+ isEnabled = ($scope.monfunc.myenabled == true) ? '1' : '0';
+
+ $ionicLoading.show(
+ {
+ template: $translate.instant('kApplyingChanges') + "...",
+ noBackdrop: true,
+ duration: zm.largeHttpTimeout,
+ });
- NVRDataModel.debug("MonitorCtrl: URLs for changeConfig save:" + apiMon);
+ var httpPromise = $http(
+ {
+ url: apiMon,
+ method: 'post',
+ headers:
+ {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'Accept': '*/*',
+ },
+ transformRequest: function(obj)
+ {
+ var str = [];
+ for (var p in obj)
+ str.push(encodeURIComponent(p) + "=" +
+ encodeURIComponent(obj[p]));
+ var foo = str.join("&");
+ // console.log("****RETURNING " + foo);
+ NVRDataModel.debug("MonitorCtrl: parmeters constructed: " + foo);
+ return foo;
+ },
+ data:
+ {
+ 'Monitor[Function]': $scope.monfunc.myfunc,
+ 'Monitor[Enabled]': isEnabled,
+ }
+
+ })
+ .success(function()
+ {
+ NVRDataModel.debug("MonitorCtrl: Not restarting ZM - Make sure you have the patch installed in MonitorsController.php or this won't work");
+ })
+ .error(function(data, status, headers, config)
+ {
+ NVRDataModel.debug("MonitorCtrl: Error changing monitor " + JSON.stringify(data));
+ $scope.monfunc.myfailedIds.push(item);
+ });
- var isEnabled = "";
- isEnabled = ($scope.monfunc.myenabled == true) ? '1' : '0';
+ $scope.monfunc.mypromises.push(httpPromise);
+ });
+ $q.all($scope.monfunc.mypromises).then(function(e)
+ {
+ $ionicLoading.hide();
+ // if there's a failed ID, an error has occurred
+ if ($scope.monfunc.myfailedIds.length != 0)
+ {
+ $ionicLoading.show(
+ {
+ template: $translate.instant('kErrorChangingMonitors') + ". Monitor IDs : " + $scope.monfunc.myfailedIds.toString(),
+ noBackdrop: true,
+ duration: 3000,
+ });
+ }
+ else
+ {
+ // I am not restarting ZM after monitor change
+ /* NVRDataModel.debug ("MonitorCtrl: Restarting ZM");
$ionicLoading.show({
- template: $translate.instant('kApplyingChanges') + "...",
+ template: "Successfully changed Monitor. Please wait, restarting ZoneMinder...",
noBackdrop: true,
duration: zm.largeHttpTimeout,
});
+ $http.post(apiRestart)
+ .then(function (success) {
+ $ionicLoading.hide();
+ var refresh = NVRDataModel.getMonitors(1);
+ refresh.then(function (data) {
+ $scope.monitors = data;
+ $scope.$broadcast('scroll.refreshComplete');
+ });
+
+ },
+ function (error) {
+ $ionicLoading.hide();
+
+ });*/
+ doRefresh();
+ }
+ });
+ }
- var httpPromise = $http({
- url: apiMon,
- method: 'post',
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
- 'Accept': '*/*',
- },
- transformRequest: function (obj) {
- var str = [];
- for (var p in obj)
- str.push(encodeURIComponent(p) + "=" +
- encodeURIComponent(obj[p]));
- var foo = str.join("&");
- // console.log("****RETURNING " + foo);
- NVRDataModel.debug("MonitorCtrl: parmeters constructed: " + foo);
- return foo;
- },
- data: {
- 'Monitor[Function]': $scope.monfunc.myfunc,
- 'Monitor[Enabled]': isEnabled,
- }
-
- })
- .success(function () {
- NVRDataModel.debug("MonitorCtrl: Not restarting ZM - Make sure you have the patch installed in MonitorsController.php or this won't work");
- })
- .error(function (data, status, headers, config) {
- NVRDataModel.debug("MonitorCtrl: Error changing monitor " + JSON.stringify(data));
- $scope.monfunc.myfailedIds.push(item);
- });
-
- $scope.monfunc.mypromises.push(httpPromise);
- });
-
- $q.all($scope.monfunc.mypromises).then(function (e) {
- $ionicLoading.hide();
- // if there's a failed ID, an error has occurred
- if ($scope.monfunc.myfailedIds.length != 0) {
- $ionicLoading.show({
- template: $translate.instant('kErrorChangingMonitors') + ". Monitor IDs : " + $scope.monfunc.myfailedIds.toString(),
- noBackdrop: true,
- duration: 3000,
- });
- } else {
- // I am not restarting ZM after monitor change
- /* NVRDataModel.debug ("MonitorCtrl: Restarting ZM");
- $ionicLoading.show({
- template: "Successfully changed Monitor. Please wait, restarting ZoneMinder...",
- noBackdrop: true,
- duration: zm.largeHttpTimeout,
- });
- $http.post(apiRestart)
- .then(function (success) {
- $ionicLoading.hide();
- var refresh = NVRDataModel.getMonitors(1);
- refresh.then(function (data) {
- $scope.monitors = data;
- $scope.$broadcast('scroll.refreshComplete');
- });
-
- },
- function (error) {
- $ionicLoading.hide();
-
- });*/
- doRefresh();
- }
- });
- }
-
-
-
- },
- ]
+ }, ]
});
};
-
-
// same logic as EventCtrl.js
- $scope.finishedLoadingImage = function () {
+ $scope.finishedLoadingImage = function()
+ {
// console.log("***Monitor image FINISHED Loading***");
$ionicLoading.hide();
};
-
- $scope.$on('$ionicView.loaded', function () {
+ $scope.$on('$ionicView.loaded', function()
+ {
// console.log("**VIEW ** Monitor Ctrl Loaded");
});
-
-
-
-
//-------------------------------------------------------------------------
// Lets make sure we set screen dim properly as we enter
// The problem is we enter other states before we leave previous states
@@ -269,32 +288,35 @@ angular.module('zmApp.controllers')
// reset power state on exit as if it is called after we enter another
// state, that effectively overwrites current view power management needs
//------------------------------------------------------------------------
- $scope.$on('$ionicView.enter', function () {
+ $scope.$on('$ionicView.enter', function()
+ {
// console.log("**VIEW ** Monitor Ctrl Entered");
NVRDataModel.setAwake(false);
$ionicSideMenuDelegate.canDragContent(true);
$scope.areImagesLoading = true;
});
-
- $scope.$on('$ionicView.afterEnter', function () {
+ $scope.$on('$ionicView.afterEnter', function()
+ {
// console.log("**VIEW ** Monitor Ctrl Entered");
$scope.monitors = [];
$scope.monitors = message;
-
+
//console.log (">>>>>>>>>>>> MONITOR CTRL " + JSON.stringify($scope.monitors));
-
-
- if ($scope.monitors.length == 0) {
- $rootScope.zmPopup = $ionicPopup.alert({
+ if ($scope.monitors.length == 0)
+ {
+ $rootScope.zmPopup = $ionicPopup.alert(
+ {
title: $translate.instant('kNoMonitors'),
template: $translate.instant('kPleaseCheckCredentials')
});
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
- $state.go("login", {
+ $state.go("login",
+ {
"wizard": false
});
return;
@@ -307,14 +329,17 @@ angular.module('zmApp.controllers')
// Now lets see if we need to load live screen
// $rootScope.tappedMid = 1;
- if ($rootScope.tappedMid != 0) {
+ if ($rootScope.tappedMid != 0)
+ {
NVRDataModel.log("Notification tapped, we need to go to monitor " + $rootScope.tappedMid);
var tm = $rootScope.tappedMid;
$rootScope.tappedMid = 0;
var monitem;
- for (var m = 0; m < $scope.monitors.length; m++) {
- if ($scope.monitors[m].Monitor.Id == tm) {
+ for (var m = 0; m < $scope.monitors.length; m++)
+ {
+ if ($scope.monitors[m].Monitor.Id == tm)
+ {
monitem = $scope.monitors[m];
break;
}
@@ -324,31 +349,31 @@ angular.module('zmApp.controllers')
openModal(monitem.Monitor.Id, monitem.Monitor.Controllable, monitem.Monitor.ControlId, monitem.Monitor.connKey, monitem);
}
-
});
-
- $scope.$on('$ionicView.leave', function () {
+ $scope.$on('$ionicView.leave', function()
+ {
// console.log("**VIEW ** Monitor Ctrl Left, force removing modal");
if ($scope.modal) $scope.modal.remove();
});
- $scope.$on('$ionicView.unloaded', function () {
+ $scope.$on('$ionicView.unloaded', function()
+ {
// console.log("**VIEW ** Monitor Ctrl Unloaded");
});
-
- $scope.openModal = function (mid, controllable, controlid, connKey, monitor) {
+ $scope.openModal = function(mid, controllable, controlid, connKey, monitor)
+ {
openModal(mid, controllable, controlid, connKey, monitor);
};
- function openModal(mid, controllable, controlid, connKey, monitor) {
+ function openModal(mid, controllable, controlid, connKey, monitor)
+ {
NVRDataModel.debug("MonitorCtrl:Open Monitor Modal with monitor Id=" + mid +
" and Controllable:" + controllable + " with control ID:" + controlid);
-
$scope.monitor = monitor;
//console.log (">>>>>>>>>>>> MONITOR CRL " + $scope.monitor.
$scope.monitorId = mid;
@@ -359,8 +384,6 @@ angular.module('zmApp.controllers')
NVRDataModel.log("Monitor Orientation is: " + $scope.orientation);
$rootScope.rand = Math.floor(Math.random() * (999999 - 111111 + 1)) + 111111;
-
-
$scope.showPTZ = false;
$scope.monitorId = mid;
$scope.monitorName = NVRDataModel.getMonitorName(mid);
@@ -369,7 +392,6 @@ angular.module('zmApp.controllers')
$scope.LoginData = NVRDataModel.getLogin();
$rootScope.modalRand = Math.floor(Math.random() * (999999 - 111111 + 1)) + 111111;
-
$scope.ptzMoveCommand = "";
$scope.ptzStopCommand = "";
@@ -382,24 +404,24 @@ angular.module('zmApp.controllers')
$scope.connKey = (Math.floor((Math.random() * 999999) + 1)).toString();
$scope.isControllable = controllable;
-
- $rootScope.modalRand = Math.floor(Math.random() * (999999 - 111111 + 1)) + 111111;
+ $rootScope.modalRand = Math.floor(Math.random() * (999999 - 111111 + 1)) + 111111;
// This is a modal to show the monitor footage
// We need to switch to always awake if set so the feed doesn't get interrupted
NVRDataModel.setAwake(NVRDataModel.getKeepAwake());
-
-
- $ionicModal.fromTemplateUrl('templates/monitors-modal.html', {
+ $ionicModal.fromTemplateUrl('templates/monitors-modal.html',
+ {
scope: $scope,
animation: 'slide-in-up'
})
- .then(function (modal) {
+ .then(function(modal)
+ {
$scope.modal = modal;
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kPleaseWait') + "...",
noBackdrop: true,
duration: zm.loadingTimeout
@@ -410,7 +432,8 @@ angular.module('zmApp.controllers')
}
- $scope.closeModal = function () {
+ $scope.closeModal = function()
+ {
// console.log("Close & Destroy Monitor Modal");
// stop networking -nph-zms keeps sucking data
@@ -418,73 +441,78 @@ angular.module('zmApp.controllers')
// switch off awake, as liveview is finished
NVRDataModel.setAwake(false);
$scope.modal.remove();
- $timeout(function () {
+ $timeout(function()
+ {
NVRDataModel.log("MonitorCtrl:Stopping network pull...");
if (NVRDataModel.isForceNetworkStop()) NVRDataModel.stopNetwork("MonitorCtrl-closeModal");
}, 300);
-
};
//Cleanup the modal when we're done with it!
- $scope.$on('$destroy', function () {
+ $scope.$on('$destroy', function()
+ {
//console.log("Destroy Monitor Modal");
if ($scope.modal) $scope.modal.remove();
});
-
-
-
//-----------------------------------------------------------------------
// Controller Main
//-----------------------------------------------------------------------
-
- function monitorStateCheck() {
+ function monitorStateCheck()
+ {
var apiMonCheck;
// The status is provided by zmdc.pl
// "not running", "pending", "running since", "Unable to connect"
var i;
- for (i = 0; i < $scope.monitors.length; i++) {
- (function (j) {
+ for (i = 0; i < $scope.monitors.length; i++)
+ {
+ (function(j)
+ {
$scope.monitors[j].Monitor.isRunningText = "...";
$scope.monitors[j].Monitor.isRunning = "...";
$scope.monitors[j].Monitor.color = zm.monitorCheckingColor;
$scope.monitors[j].Monitor.char = "ion-checkmark-circled";
apiMonCheck = loginData.apiurl + "/monitors/daemonStatus/id:" + $scope.monitors[j].Monitor.Id + "/daemon:zmc.json";
-
//apiMonCheck = apiMonCheck.replace(loginData.url, $scope.monitors[j].Monitor.baseURL);
-
// in multiserver replace apiurl with baseurl
-
NVRDataModel.debug("MonitorCtrl:monitorStateCheck: " + apiMonCheck);
//console.log("**** ZMC CHECK " + apiMonCheck);
$http.get(apiMonCheck)
- .success(function (data) {
+ .success(function(data)
+ {
NVRDataModel.debug("MonitorCtrl: monitor check state returned: " + JSON.stringify(data));
- if (data.statustext.indexOf("not running") > -1) {
+ if (data.statustext.indexOf("not running") > -1)
+ {
$scope.monitors[j].Monitor.isRunning = "false";
$scope.monitors[j].Monitor.color = zm.monitorNotRunningColor;
$scope.monitors[j].Monitor.char = "ion-close-circled";
- } else if (data.statustext.indexOf("pending") > -1) {
+ }
+ else if (data.statustext.indexOf("pending") > -1)
+ {
$scope.monitors[j].Monitor.isRunning = "pending";
$scope.monitors[j].Monitor.color = zm.monitorPendingColor;
- } else if (data.statustext.indexOf("running since") > -1) {
+ }
+ else if (data.statustext.indexOf("running since") > -1)
+ {
$scope.monitors[j].Monitor.isRunning = "true";
$scope.monitors[j].Monitor.color = zm.monitorRunningColor;
- } else if (data.statustext.indexOf("Unable to connect") > -1) {
+ }
+ else if (data.statustext.indexOf("Unable to connect") > -1)
+ {
$scope.monitors[j].Monitor.isRunning = "false";
$scope.monitors[j].Monitor.color = zm.monitorNotRunningColor;
$scope.monitors[j].Monitor.char = "ion-close-circled";
}
-
$scope.monitors[j].Monitor.isRunningText = data.statustext;
})
- .error(function (data) {
+ .error(function(data)
+ {
NVRDataModel.debug("MonitorCtrl: Error->monitor check state returned: " +
JSON.stringify(data));
NVRDataModel.displayBanner('error', [$translate.instant('kErrorRetrievingState'), $translate.instant('kPleaseTryAgain')]);
@@ -493,30 +521,30 @@ angular.module('zmApp.controllers')
$scope.monitors[j].Monitor.char = "ion-help-circled";
});
-
})(i);
}
}
-
- function doRefresh() {
+ function doRefresh()
+ {
$scope.monitors = [];
var refresh = NVRDataModel.getMonitors(1);
- refresh.then(function (data) {
+ refresh.then(function(data)
+ {
$scope.monitors = data;
monitorStateCheck();
$scope.$broadcast('scroll.refreshComplete');
});
}
- $scope.doRefresh = function () {
+ $scope.doRefresh = function()
+ {
//console.log("***Pull to Refresh");
doRefresh();
-
};
-
-}]); \ No newline at end of file
+ }
+ ]);
diff --git a/www/js/MonitorModalCtrl.js b/www/js/MonitorModalCtrl.js
index cff63da1..4b3a7288 100644
--- a/www/js/MonitorModalCtrl.js
+++ b/www/js/MonitorModalCtrl.js
@@ -3,13 +3,8 @@
/* jslint browser: true*/
/* global saveAs, cordova,StatusBar,angular,console,ionic, moment, imagesLoaded, chrome */
-
-
-angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$rootScope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$q', '$sce', 'carouselUtils', '$ionicPopup', 'SecuredPopups', '$translate', function ($scope, $rootScope, zm, NVRDataModel, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $stateParams, $ionicHistory, $ionicScrollDelegate, $q, $sce, carouselUtils, $ionicPopup, SecuredPopups, $translate) {
-
-
-
-
+angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$rootScope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$q', '$sce', 'carouselUtils', '$ionicPopup', 'SecuredPopups', '$translate', function($scope, $rootScope, zm, NVRDataModel, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $stateParams, $ionicHistory, $ionicScrollDelegate, $q, $sce, carouselUtils, $ionicPopup, SecuredPopups, $translate)
+{
$scope.animationInProgress = false;
$scope.imageFit = true;
@@ -21,7 +16,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
$rootScope.authSession = "undefined";
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kNegotiatingStreamAuth') + '...',
animation: 'fade-in',
showBackdrop: true,
@@ -30,7 +26,6 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
showDelay: 0
});
-
$scope.currentStreamMode = 'single';
NVRDataModel.log("Using stream mode " + $scope.currentStreamMode);
@@ -38,13 +33,15 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
$rootScope.validMonitorId = $scope.monitors[0].Monitor.Id;
NVRDataModel.getAuthKey($rootScope.validMonitorId, $scope.monitors[0].Monitor.connKey)
- .then(function (success) {
+ .then(function(success)
+ {
$ionicLoading.hide();
$rootScope.authSession = success;
NVRDataModel.log("Modal: Stream authentication construction: " + $rootScope.authSession);
},
- function (error) {
+ function(error)
+ {
$ionicLoading.hide();
NVRDataModel.debug("ModalCtrl: Error details of stream auth:" + error);
@@ -52,19 +49,18 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
NVRDataModel.log("Modal: Error returned Stream authentication construction. Retaining old value of: " + $rootScope.authSession);
});
-
-
$interval.cancel(intervalModalHandle);
$interval.cancel(cycleHandle);
- intervalModalHandle = $interval(function () {
+ intervalModalHandle = $interval(function()
+ {
loadModalNotifications();
// console.log ("Refreshing Image...");
}.bind(this), 5000);
-
$timeout.cancel(nphTimer);
- nphTimer = $timeout(function () {
+ nphTimer = $timeout(function()
+ {
$scope.currentStreamMode = 'jpeg';
NVRDataModel.log("Switching playback via nphzms");
}, zm.nphSwitchTimer);
@@ -77,7 +73,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
background: '#2F4F4F',
isOpen: true,
toggleOnClick: false,
- button: {
+ button:
+ {
cssClass: "fa fa-arrows-alt",
},
items: [
@@ -85,7 +82,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
content: '',
cssClass: 'fa fa-chevron-circle-up',
empty: false,
- onclick: function () {
+ onclick: function()
+ {
controlPTZ($scope.monitorId, $scope.ptzMoveCommand + 'Down');
}
},
@@ -94,7 +92,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
content: '',
cssClass: 'fa fa-chevron-circle-up',
empty: false,
- onclick: function () {
+ onclick: function()
+ {
controlPTZ($scope.monitorId, $scope.ptzMoveCommand + 'DownLeft');
}
},
@@ -104,7 +103,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
cssClass: 'fa fa-chevron-circle-up',
empty: false,
- onclick: function () {
+ onclick: function()
+ {
controlPTZ($scope.monitorId, $scope.ptzMoveCommand + 'Left');
}
},
@@ -112,7 +112,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
content: 'D',
empty: true,
- onclick: function () {
+ onclick: function()
+ {
// console.log('About');
}
},
@@ -121,7 +122,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
content: '',
cssClass: 'fa fa-chevron-circle-up',
empty: false,
- onclick: function () {
+ onclick: function()
+ {
controlPTZ($scope.monitorId, $scope.ptzMoveCommand + 'UpLeft');
}
},
@@ -130,7 +132,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
content: '',
cssClass: 'fa fa-chevron-circle-up',
empty: false,
- onclick: function () {
+ onclick: function()
+ {
controlPTZ($scope.monitorId, $scope.ptzMoveCommand + 'Up');
}
},
@@ -139,7 +142,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
content: '',
cssClass: 'fa fa-chevron-circle-up',
empty: false,
- onclick: function () {
+ onclick: function()
+ {
controlPTZ($scope.monitorId, $scope.ptzMoveCommand + 'UpRight');
}
},
@@ -147,7 +151,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
{
content: 'H',
empty: true,
- onclick: function () {
+ onclick: function()
+ {
//console.log('About');
}
},
@@ -156,17 +161,18 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
content: '',
cssClass: 'fa fa-chevron-circle-up',
empty: false,
- onclick: function () {
+ onclick: function()
+ {
controlPTZ($scope.monitorId, $scope.ptzMoveCommand + 'Right');
}
},
-
{
content: '',
cssClass: 'fa fa-chevron-circle-up',
empty: false,
- onclick: function () {
+ onclick: function()
+ {
controlPTZ($scope.monitorId, $scope.ptzMoveCommand + 'DownRight');
}
},
@@ -174,36 +180,36 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
{
content: 'K',
empty: true,
- onclick: function () {
+ onclick: function()
+ {
//console.log('About');
}
},
- ]
+ ]
};
//-------------------------------------------------------------
// On re-auth, we need a new zms
//-------------------------------------------------------------
- $rootScope.$on("auth-success", function () {
+ $rootScope.$on("auth-success", function()
+ {
NVRDataModel.debug("MonitorModalCtrl: Re-login detected, resetting everything & re-generating connkey");
NVRDataModel.stopNetwork("MonitorModal-auth success");
$scope.connKey = (Math.floor((Math.random() * 999999) + 1)).toString();
-
});
-
- $scope.cast = function (mid, mon) {
-
+ $scope.cast = function(mid, mon) {
};
//----------------------------------
// toggles monitor cycling
//----------------------------------
- $scope.toggleCycle = function () {
+ $scope.toggleCycle = function()
+ {
//console.log ("HERE");
$scope.isCycle = !$scope.isCycle;
var ld = NVRDataModel.getLogin();
@@ -211,15 +217,19 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
NVRDataModel.setLogin(ld);
$scope.cycleText = $scope.isCycle ? $translate.instant('kOn') : $translate.instant('kOff');
- if ($scope.isCycle) {
+ if ($scope.isCycle)
+ {
NVRDataModel.log("re-starting cycle timer");
$interval.cancel(cycleHandle);
- cycleHandle = $interval(function () {
+ cycleHandle = $interval(function()
+ {
moveToMonitor($scope.monitorId, 1);
// console.log ("Refreshing Image...");
}.bind(this), ld.cycleMonitorsInterval * 1000);
- } else {
+ }
+ else
+ {
NVRDataModel.log("cancelling cycle timer");
$interval.cancel(cycleHandle);
}
@@ -230,19 +240,21 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
// PTZ enable/disable
//-------------------------------------------------------------
-
- $scope.togglePTZ = function () {
+ $scope.togglePTZ = function()
+ {
//console.log("PTZ");
- if ($scope.isControllable == '1') {
+ if ($scope.isControllable == '1')
+ {
//console.log ("iscontrollable is true");
$scope.showPTZ = !$scope.showPTZ;
-
-
- } else {
- $ionicLoading.show({
+ }
+ else
+ {
+ $ionicLoading.show(
+ {
template: $translate.instant('kPTZnotConfigured'),
noBackdrop: true,
duration: 3000,
@@ -251,13 +263,12 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
};
-
-
//-------------------------------------------------------------
// Pause and resume handlers
//-------------------------------------------------------------
- function onPause() {
+ function onPause()
+ {
NVRDataModel.debug("ModalCtrl: onpause called");
$interval.cancel(intervalModalHandle);
$interval.cancel(cycleHandle);
@@ -266,10 +277,11 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
// FIXME: Do I need to setAwake(false) here?
}
-
- function onResume() {
+ function onResume()
+ {
NVRDataModel.debug("ModalCtrl: Modal resume called");
- if ($scope.isModalActive) {
+ if ($scope.isModalActive)
+ {
NVRDataModel.log("ModalCtrl: Restarting Modal timer on resume");
$interval.cancel(intervalModalHandle);
@@ -277,16 +289,19 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
var ld = NVRDataModel.getLogin();
- intervalModalHandle = $interval(function () {
+ intervalModalHandle = $interval(function()
+ {
loadModalNotifications();
}.bind(this), 5000);
- if (ld.cycleMonitors) {
+ if (ld.cycleMonitors)
+ {
NVRDataModel.debug("Cycling enabled at " + ld.cycleMonitorsInterval);
$interval.cancel(cycleHandle);
- cycleHandle = $interval(function () {
+ cycleHandle = $interval(function()
+ {
moveToMonitor($scope.monitorId, 1);
// console.log ("Refreshing Image...");
}.bind(this), ld.cycleMonitorsInterval * 1000);
@@ -299,116 +314,117 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
}
-
//-------------------------------------------------------------
// Queries the 1.30 API for recording state of current monitor
//-------------------------------------------------------------
- function loadModalNotifications() {
+ function loadModalNotifications()
+ {
- if (NVRDataModel.versionCompare($rootScope.apiVersion, "1.30") == -1) {
+ if (NVRDataModel.versionCompare($rootScope.apiVersion, "1.30") == -1)
+ {
return;
}
-
+
if (NVRDataModel.getLogin().enableLowBandwidth)
return;
var status = [$translate.instant('kMonIdle'),
- $translate.instant('kMonPreAlarm'),
- $translate.instant('kMonAlarmed'),
- $translate.instant('kMonAlert'),
- $translate.instant('kMonRecord')
- ];
+ $translate.instant('kMonPreAlarm'),
+ $translate.instant('kMonAlarmed'),
+ $translate.instant('kMonAlert'),
+ $translate.instant('kMonRecord')
+ ];
//console.log ("Inside Modal timer...");
var apiurl = NVRDataModel.getLogin().apiurl;
var alarmurl = apiurl + "/monitors/alarm/id:" + $scope.monitorId + "/command:status.json";
NVRDataModel.log("Invoking " + alarmurl);
-
$http.get(alarmurl)
- .then(function (data) {
+ .then(function(data)
+ {
// NVRDataModel.debug ("Success in monitor alarmed status " + JSON.stringify(data));
$scope.monStatus = status[parseInt(data.data.status)];
},
- function (error) {
-
+ function(error)
+ {
$scope.monStatus = "";
NVRDataModel.debug("Error in monitor alarmed status ");
});
-
}
-
//-------------------------------------------------------------
// Enable/Disable preset list
//-------------------------------------------------------------
- $scope.togglePresets = function () {
+ $scope.togglePresets = function()
+ {
$scope.presetOn = !$scope.presetOn;
- if ($scope.presetOn) {
+ if ($scope.presetOn)
+ {
$scope.controlToggle = "hide buttons";
- } else {
+ }
+ else
+ {
$scope.controlToggle = "show buttons";
}
//console.log("Changing preset to " + $scope.presetOn);
var element = angular.element(document.getElementById("presetlist"));
// bring it in
- if ($scope.presetOn) {
+ if ($scope.presetOn)
+ {
element.removeClass("animated fadeOutUp");
-
- } else {
+ }
+ else
+ {
element.removeClass("animated fadeInDown");
element.addClass("animated fadeOutUp");
}
-
-
};
-
//-------------------------------------------------------------
// this is checked to make sure we are not pulling images
// when app is in background. This is a problem with Android,
// for example
//-------------------------------------------------------------
- $scope.isBackground = function () {
+ $scope.isBackground = function()
+ {
// console.log ("Is background called from ModalCtrl and returned " +
// NVRDataModel.isBackground());
return NVRDataModel.isBackground();
};
-
-
-
-
-
//-------------------------------------------------------------
// Send PTZ command to ZM
// Note: PTZ fails on desktop, don't bother about it
//-------------------------------------------------------------
-
- $scope.controlPTZ = function (monitorId, cmd) {
+ $scope.controlPTZ = function(monitorId, cmd)
+ {
controlPTZ(monitorId, cmd);
};
- function controlPTZ(monitorId, cmd) {
+ function controlPTZ(monitorId, cmd)
+ {
//presetGotoX
//presetHome
//curl -X POST "http://server.com/zm/index.php?view=request" -d
//"request=control&user=admin&passwd=xx&id=4&control=moveConLeft"
- if (!$scope.ptzMoveCommand) {
- $ionicLoading.show({
+ if (!$scope.ptzMoveCommand)
+ {
+ $ionicLoading.show(
+ {
template: $translate.instant('kPTZNotReady'),
noBackdrop: true,
duration: 2000,
@@ -417,7 +433,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
}
var ptzData = "";
- if (cmd.lastIndexOf("preset", 0) === 0) {
+ if (cmd.lastIndexOf("preset", 0) === 0)
+ {
NVRDataModel.debug("PTZ command is a preset, so skipping xge/lge");
ptzData = {
view: "request",
@@ -428,7 +445,9 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
// yge: "30", //wtf
};
- } else {
+ }
+ else
+ {
ptzData = {
view: "request",
@@ -443,7 +462,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
//console.log("Command value " + cmd + " with MID=" + monitorId);
//console.log("PTZDATA is " + JSON.stringify(ptzData));
$ionicLoading.hide();
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kPleaseWait') + "...",
noBackdrop: true,
duration: zm.loadingTimeout,
@@ -451,22 +471,25 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
var loginData = NVRDataModel.getLogin();
$ionicLoading.hide();
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kSendingPTZ') + "...",
noBackdrop: true,
duration: zm.loadingTimeout,
});
-
- var req = $http({
+ var req = $http(
+ {
method: 'POST',
/*timeout: 15000,*/
url: loginData.url + '/index.php',
- headers: {
+ headers:
+ {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json',
},
- transformRequest: function (obj) {
+ transformRequest: function(obj)
+ {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" +
@@ -480,49 +503,50 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
});
- req.success(function (resp) {
+ req.success(function(resp)
+ {
$ionicLoading.hide();
});
- req.error(function (resp) {
+ req.error(function(resp)
+ {
$ionicLoading.hide();
//console.log("ERROR: " + JSON.stringify(resp));
NVRDataModel.log("Error sending PTZ:" + JSON.stringify(resp), "error");
});
}
-
-
-
- $scope.getZoomLevel = function () {
+ $scope.getZoomLevel = function()
+ {
//console.log("ON RELEASE");
var zl = $ionicScrollDelegate.$getByHandle("imgscroll").getScrollPosition();
//console.log(JSON.stringify(zl));
};
- $scope.onTap = function (m, d) {
+ $scope.onTap = function(m, d)
+ {
moveToMonitor(m, d);
};
-
- $scope.onSwipe = function (m, d) {
+ $scope.onSwipe = function(m, d)
+ {
var ld = NVRDataModel.getLogin();
if (!ld.canSwipeMonitors) return;
- if ($ionicScrollDelegate.$getByHandle("imgscroll").getScrollPosition().zoom != 1) {
+ if ($ionicScrollDelegate.$getByHandle("imgscroll").getScrollPosition().zoom != 1)
+ {
//console.log("Image is zoomed in - not honoring swipe");
return;
}
$scope.monStatus = "";
moveToMonitor(m, d);
-
-
};
- function moveToMonitor(m, d) {
+ function moveToMonitor(m, d)
+ {
var curstate = $ionicHistory.currentStateName();
var found = 0;
var mid;
@@ -537,19 +561,22 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
m = mid;
//console.log("Next Monitor is " + m);
-
found = 0;
- for (var i = 0; i < $scope.monitors.length; i++) {
+ for (var i = 0; i < $scope.monitors.length; i++)
+ {
if ($scope.monitors[i].Monitor.Id == mid &&
// if you came from monitors, then ignore noshow
($scope.monitors[i].Monitor.listDisplay != 'noshow' || curstate == "monitors") &&
$scope.monitors[i].Monitor.Function != 'None' &&
- $scope.monitors[i].Monitor.Enabled != '0') {
+ $scope.monitors[i].Monitor.Enabled != '0')
+ {
found = 1;
//console.log(mid + "is part of the monitor list");
NVRDataModel.debug("ModalCtrl: swipe detected, moving to " + mid);
break;
- } else {
+ }
+ else
+ {
NVRDataModel.debug("skipping " + $scope.monitors[i].Monitor.Id +
" listDisplay=" + $scope.monitors[i].Monitor.listDisplay +
" Function=" + $scope.monitors[i].Monitor.Function +
@@ -557,18 +584,19 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
}
}
-
}
while (found != 1);
-
var slidein;
var slideout;
var dirn = d;
- if (dirn == 1) {
+ if (dirn == 1)
+ {
slideout = "animated slideOutLeft";
slidein = "animated slideInRight";
- } else {
+ }
+ else
+ {
slideout = "animated slideOutRight";
slidein = "animated slideInLeft";
}
@@ -577,16 +605,16 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
element.addClass(slideout)
.one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', outWithOld);
-
-
- function outWithOld() {
+ function outWithOld()
+ {
NVRDataModel.log("ModalCtrl:Stopping network pull...");
NVRDataModel.stopNetwork("MonitorModal-outwithOld");
$scope.rand = Math.floor((Math.random() * 100000) + 1);
$scope.animationInProgress = true;
- $timeout(function () {
+ $timeout(function()
+ {
element.removeClass(slideout);
element.addClass(slidein)
.one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', inWithNew);
@@ -597,7 +625,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
}, 200);
}
- function inWithNew() {
+ function inWithNew()
+ {
element.removeClass(slidein);
$scope.animationInProgress = false;
@@ -605,14 +634,16 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
NVRDataModel.log("New image loaded in");
var ld = NVRDataModel.getLogin();
carouselUtils.setStop(false);
- if (ld.useNphZms == true) {
+ if (ld.useNphZms == true)
+ {
$scope.currentStreamMode = 'single';
NVRDataModel.log("Setting timer to play nph-zms mode");
// first 5 seconds, load a snapshot, then switch to real FPS display
// this is to avoid initial image load delay
// FIXME: 5 seconds fair?
$timeout.cancel(nphTimer);
- nphTimer = $timeout(function () {
+ nphTimer = $timeout(function()
+ {
$scope.currentStreamMode = 'jpeg';
NVRDataModel.log("Switching playback via nphzms");
}, zm.nphSwitchTimer);
@@ -620,21 +651,19 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
}
-
$ionicLoading.hide();
-
}
-
-
//-----------------------------------------------------------------------
// Sucess/Error handlers for saving a snapshot of the
// monitor image to phone storage
//-----------------------------------------------------------------------
- function SaveSuccess() {
- $ionicLoading.show({
+ function SaveSuccess()
+ {
+ $ionicLoading.show(
+ {
template: $translate.instant('kDone'),
noBackdrop: true,
duration: 1000
@@ -642,8 +671,10 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
NVRDataModel.debug("ModalCtrl:Photo saved successfuly");
}
- function SaveError(e) {
- $ionicLoading.show({
+ function SaveError(e)
+ {
+ $ionicLoading.show(
+ {
template: $translate.instant('kErrorSave'),
noBackdrop: true,
duration: 2000
@@ -652,60 +683,69 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
//console.log("***ERROR");
}
-
//-------------------------------------------------------------
// Turns on or off an alarm forcibly (mode true = on, false = off)
//-------------------------------------------------------------
- $scope.enableAlarm = function (mid, mode) {
+ $scope.enableAlarm = function(mid, mode)
+ {
if (mode) // trigger alarm
{
- $rootScope.zmPopup = SecuredPopups.show('show', {
+ $rootScope.zmPopup = SecuredPopups.show('show',
+ {
title: 'Confirm',
template: $translate.instant('kForceAlarmConfirm') + $scope.monitorName + "?",
buttons: [
+ {
+ text: $translate.instant('kButtonYes'),
+ onTap: function(e)
{
- text: $translate.instant('kButtonYes'),
- onTap: function (e) {
- enableAlarm(mid, mode);
- }
- },
+ enableAlarm(mid, mode);
+ }
+ },
+ {
+ text: $translate.instant('kButtonNo'),
+ onTap: function(e)
{
- text: $translate.instant('kButtonNo'),
- onTap: function (e) {
- return;
- }
- }
- ]
+ return;
+ }
+ }]
});
- } else
+ }
+ else
enableAlarm(mid, mode);
- function enableAlarm(mid, mode) {
+ function enableAlarm(mid, mode)
+ {
var apiurl = NVRDataModel.getLogin().apiurl;
var c = mode ? "on" : "off";
var alarmurl = apiurl + "/monitors/alarm/id:" + mid + "/command:" + c + ".json";
NVRDataModel.log("Invoking " + alarmurl);
var status = mode ? $translate.instant('kForcingAlarm') : $translate.instant('kCancellingAlarm');
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: status,
noBackdrop: true,
duration: zm.largeHttpTimeout,
});
$http.get(alarmurl)
- .then(function (data) {
- $ionicLoading.show({
+ .then(function(data)
+ {
+ $ionicLoading.show(
+ {
template: $translate.instant('kSuccess'),
noBackdrop: true,
duration: 2000,
});
},
- function (error) {
+ function(error)
+ {
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kAlarmAPIError'),
noBackdrop: true,
duration: 3000,
@@ -714,25 +754,24 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
});
}
-
-
};
-
//-----------------------------------------------------------------------
// color for monitor state
//-----------------------------------------------------------------------
- $scope.stateColor = function () {
+ $scope.stateColor = function()
+ {
var status = [$translate.instant('kMonIdle'),
- $translate.instant('kMonPreAlarm'),
- $translate.instant('kMonAlarmed'),
- $translate.instant('kMonAlert'),
- $translate.instant('kMonRecord')
- ];
+ $translate.instant('kMonPreAlarm'),
+ $translate.instant('kMonAlarmed'),
+ $translate.instant('kMonAlert'),
+ $translate.instant('kMonRecord')
+ ];
//console.log ("***MONSTATUS**"+$scope.monStatus+"**");
var color = "";
- switch ($scope.monStatus) {
+ switch ($scope.monStatus)
+ {
case "":
color = "background-color:none";
break;
@@ -756,44 +795,46 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
return "padding-left:4px;padding-right:4px;" + color;
};
-
//-----------------------------------------------------------------------
// Saves a snapshot of the monitor image to phone storage
//-----------------------------------------------------------------------
- $scope.saveImageToPhoneWithPerms = function (mid)
+ $scope.saveImageToPhoneWithPerms = function(mid)
{
if ($rootScope.platformOS != 'android')
{
saveImageToPhone(mid);
return;
}
-
-
+
NVRDataModel.debug("ModalCtrl: Permission checking for write");
var permissions = cordova.plugins.permissions;
permissions.hasPermission(permissions.WRITE_EXTERNAL_STORAGE, checkPermissionCallback, null);
-
- function checkPermissionCallback(status) {
+
+ function checkPermissionCallback(status)
+ {
if (!status.hasPermission)
{
SaveError("No permission to write to external storage");
}
- permissions.requestPermission(permissions.WRITE_EXTERNAL_STORAGE, succ,err);
+ permissions.requestPermission(permissions.WRITE_EXTERNAL_STORAGE, succ, err);
}
-
+
function succ(s)
{
saveImageToPhone(mid);
}
+
function err(e)
{
- SaveError ("Error in requestPermission");
+ SaveError("Error in requestPermission");
}
};
-
- function saveImageToPhone(mid) {
- $ionicLoading.show({
+
+ function saveImageToPhone(mid)
+ {
+ $ionicLoading.show(
+ {
template: $translate.instant('kSavingSnapshot') + '...',
noBackdrop: true,
duration: zm.httpTimeout
@@ -808,7 +849,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
NVRDataModel.log("SavetoPhone:Trying to save image from " + url);
var img = new Image();
- img.onload = function () {
+ img.onload = function()
+ {
// console.log("********* ONLOAD");
canvas = document.createElement('canvas');
canvas.width = img.width;
@@ -819,8 +861,10 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
imageDataUrl = canvas.toDataURL('image/jpeg', 1.0);
imageData = imageDataUrl.replace(/data:image\/jpeg;base64,/, '');
- if ($rootScope.platformOS != "desktop") {
- try {
+ if ($rootScope.platformOS != "desktop")
+ {
+ try
+ {
cordova.exec(
SaveSuccess,
@@ -828,82 +872,87 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
'Canvas2ImagePlugin',
'saveImageDataToLibrary', [imageData]
);
- } catch (e) {
+ }
+ catch (e)
+ {
SaveError(e.message);
}
- } else {
-
+ }
+ else
+ {
var fname = $scope.monitorName + "-" +
moment().format('MMM-DD-YY_HH-mm-ss') + ".png";
- canvas.toBlob(function (blob) {
+ canvas.toBlob(function(blob)
+ {
saveAs(blob, fname);
SaveSuccess();
});
}
};
- try {
+ try
+ {
img.src = url;
// console.log ("SAVING IMAGE SOURCE");
- } catch (e) {
+ }
+ catch (e)
+ {
SaveError(e.message);
}
}
-
-
-
//-------------------------------------------------------------
//reloaads mon - do we need it?
//-------------------------------------------------------------
-
- $scope.reloadView = function () {
+ $scope.reloadView = function()
+ {
NVRDataModel.log("Reloading view for modal view, recomputing rand");
$rootScope.modalRand = Math.floor((Math.random() * 100000) + 1);
$scope.isModalActive = true;
};
- $scope.scaleImage = function () {
+ $scope.scaleImage = function()
+ {
$scope.imageFit = !$scope.imageFit;
// console.log("Switching image style to " + $scope.imageFit);
};
- $scope.$on('$ionicView.enter', function () {
-
+ $scope.$on('$ionicView.enter', function()
+ {
//https://server/zm/api/zones/forMonitor/X.json
});
- $scope.$on('$ionicView.leave', function () {
+ $scope.$on('$ionicView.leave', function()
+ {
// console.log("**MODAL: Stopping modal timer");
$scope.isModalActive = false;
$interval.cancel(intervalModalHandle);
$interval.cancel(cycleHandle);
});
-
- $scope.$on('$ionicView.beforeLeave', function () {
+ $scope.$on('$ionicView.beforeLeave', function()
+ {
NVRDataModel.log("Nullifying the streams...");
-
var element = document.getElementById("singlemonitor");
- if (element) {
+ if (element)
+ {
NVRDataModel.debug("Nullifying " + element.src);
element.src = "";
}
-
-
});
- $scope.$on('$ionicView.unloaded', function () {
+ $scope.$on('$ionicView.unloaded', function()
+ {
$scope.isModalActive = false;
$interval.cancel(intervalModalHandle);
@@ -911,7 +960,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
});
- $scope.$on('modal.removed', function () {
+ $scope.$on('modal.removed', function()
+ {
$scope.isModalActive = false;
//console.log("**MODAL REMOVED: Stopping modal timer");
$interval.cancel(intervalModalHandle);
@@ -920,8 +970,6 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
NVRDataModel.debug("Modal removed - killing connkey");
controlStream(17, "", $scope.connKey, -1);
-
-
// Execute action
});
@@ -931,13 +979,15 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
// anyway
//-------------------------------------------------------------
-
- function controlStream(cmd, disp, connkey, ndx) {
+ function controlStream(cmd, disp, connkey, ndx)
+ {
// console.log("Command value " + cmd);
- if (disp) {
+ if (disp)
+ {
$ionicLoading.hide();
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kPleaseWait') + '...',
noBackdrop: true,
duration: zm.loadingTimeout,
@@ -967,15 +1017,18 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
var myauthtoken = $rootScope.authSession.replace("&auth=", "");
//&auth=
- var req = $http({
+ var req = $http(
+ {
method: 'POST',
/*timeout: 15000,*/
url: loginData.url + '/index.php',
- headers: {
+ headers:
+ {
'Content-Type': 'application/x-www-form-urlencoded',
//'Accept': '*/*',
},
- transformRequest: function (obj) {
+ transformRequest: function(obj)
+ {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" +
@@ -985,7 +1038,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
return foo;
},
- data: {
+ data:
+ {
view: "request",
request: "stream",
connkey: connkey,
@@ -994,20 +1048,25 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
}
});
- req.success(function (resp) {
+ req.success(function(resp)
+ {
- if (resp.result == "Ok" && ndx != -1) {
+ if (resp.result == "Ok" && ndx != -1)
+ {
var ld = NVRDataModel.getLogin();
var apiurl = ld.apiurl + "/events/" + resp.status.event + ".json";
//console.log ("API " + apiurl);
$http.get(apiurl)
- .success(function (data) {
- if ($scope.MontageMonitors[ndx].eventUrlTime != data.event.Event.StartTime) {
+ .success(function(data)
+ {
+ if ($scope.MontageMonitors[ndx].eventUrlTime != data.event.Event.StartTime)
+ {
var element = angular.element(document.getElementById($scope.MontageMonitors[ndx].Monitor.Id + "-timeline"));
element.removeClass('animated slideInRight');
element.addClass('animated slideOutRight');
- $timeout(function () {
+ $timeout(function()
+ {
element.removeClass('animated slideOutRight');
element.addClass('animated slideInRight');
$scope.MontageMonitors[ndx].eventUrlTime = data.event.Event.StartTime;
@@ -1016,7 +1075,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
}
})
- .error(function (data) {
+ .error(function(data)
+ {
$scope.MontageMonitors[ndx].eventUrlTime = "-";
});
@@ -1024,31 +1084,31 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
});
- req.error(function (resp) {
+ req.error(function(resp)
+ {
//console.log("ERROR: " + JSON.stringify(resp));
NVRDataModel.log("Error sending event command " + JSON.stringify(resp), "error");
});
}
-
//-------------------------------------------------------------
// Zoom in and out via +- for desktops
//-------------------------------------------------------------
- $scope.zoomImage = function (val) {
+ $scope.zoomImage = function(val)
+ {
var zl = parseInt($ionicScrollDelegate.$getByHandle("imgscroll").getScrollPosition().zoom);
- if (zl == 1 && val == -1) {
+ if (zl == 1 && val == -1)
+ {
NVRDataModel.debug("Already zoomed out max");
return;
}
-
zl += val;
NVRDataModel.debug("Zoom level is " + zl);
$ionicScrollDelegate.$getByHandle("imgscroll").zoomTo(zl, true);
};
-
//-------------------------------------------------------------
// Retrieves PTZ state for each monitor
//-------------------------------------------------------------
@@ -1056,7 +1116,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
// $scope.isControllable
// $scope.controlid
//
- function configurePTZ(mid) {
+ function configurePTZ(mid)
+ {
$scope.presetAndControl = $translate.instant('kMore');
$scope.ptzWakeCommand = "";
$scope.ptzSleepCommand = "";
@@ -1077,7 +1138,8 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
var ld = NVRDataModel.getLogin();
var url = ld.apiurl + "/monitors/" + mid + ".json";
$http.get(url)
- .success(function (data) {
+ .success(function(data)
+ {
$scope.isControllable = data.monitor.Monitor.Controllable;
// *** Only for testing - comment out //
@@ -1085,15 +1147,16 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
// for testing only
// $scope.isControllable = 1;
$scope.controlid = data.monitor.Monitor.ControlId;
- if ($scope.isControllable == '1') {
-
+ if ($scope.isControllable == '1')
+ {
var apiurl = NVRDataModel.getLogin().apiurl;
var myurl = apiurl + "/controls/" + $scope.controlid + ".json";
NVRDataModel.debug("configurePTZ : getting controllable data " + myurl);
$http.get(myurl)
- .success(function (data) {
+ .success(function(data)
+ {
// *** Only for testing - comment out - start//
/*data.Control.Control.CanSleep = '1';
@@ -1104,30 +1167,34 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
data.control.Control.HasHomePreset = '1';*/
// *** Only for testing - comment out - end //
-
-
$scope.ptzMoveCommand = "move"; // start with as move;
$scope.ptzStopCommand = "";
- if (data.control.Control.CanZoom == '1') {
+ if (data.control.Control.CanZoom == '1')
+ {
$scope.canZoom = true;
- if (data.control.Control.CanZoomCon == '1') {
+ if (data.control.Control.CanZoomCon == '1')
+ {
$scope.zoomInCommand = "zoomConTele";
$scope.zoomOutCommand = "zoomConWide";
- } else if (data.control.Control.CanZoomRel == '1') {
+ }
+ else if (data.control.Control.CanZoomRel == '1')
+ {
$scope.zoomInCommand = "zoomRelTele";
$scope.zoomOutCommand = "zoomRelWide";
- } else if (data.control.Control.CanZoomAbs == '1') {
+ }
+ else if (data.control.Control.CanZoomAbs == '1')
+ {
$scope.zoomInCommand = "zoomRelAbs";
$scope.zoomOutCommand = "zoomRelAbs";
}
}
-
NVRDataModel.debug("configurePTZ: control data returned " + JSON.stringify(data));
- if (data.control.Control.CanMoveRel == '1') {
+ if (data.control.Control.CanMoveRel == '1')
+ {
$scope.ptzMoveCommand = "moveRel";
$scope.ptzStopCommand = "moveStop";
@@ -1136,28 +1203,29 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
// Prefer con over rel if both enabled
// I've tested con
- if (data.control.Control.CanMoveCon == '1') {
+ if (data.control.Control.CanMoveCon == '1')
+ {
$scope.ptzMoveCommand = "moveCon";
$scope.ptzStopCommand = "moveStop";
}
-
-
// presets
NVRDataModel.debug("ConfigurePTZ Preset value is " + data.control.Control.HasPresets);
$scope.ptzPresets = [];
- if (data.control.Control.HasPresets == '1') {
+ if (data.control.Control.HasPresets == '1')
+ {
//$scope.presetAndControl = $translate.instant('kPresets');
$scope.ptzPresetCount = parseInt(data.control.Control.NumPresets);
NVRDataModel.debug("ConfigurePTZ Number of presets is " + $scope.ptzPresetCount);
-
- for (var p = 0; p < $scope.ptzPresetCount; p++) {
- $scope.ptzPresets.push({
+ for (var p = 0; p < $scope.ptzPresetCount; p++)
+ {
+ $scope.ptzPresets.push(
+ {
name: (p + 1).toString(),
icon: '',
cmd: "presetGoto" + (p + 1).toString(),
@@ -1166,8 +1234,10 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
}
- if (data.control.Control.HasHomePreset == '1') {
- $scope.ptzPresets.unshift({
+ if (data.control.Control.HasHomePreset == '1')
+ {
+ $scope.ptzPresets.unshift(
+ {
name: '',
icon: "ion-ios-home",
cmd: 'presetHome',
@@ -1176,8 +1246,6 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
}
-
-
}
/*else
{
@@ -1188,13 +1256,16 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
// no need to darken these buttons if presets are not there
var buttonAccent = "button-dark";
- if ($scope.ptzPresets.length == 0) {
+ if ($scope.ptzPresets.length == 0)
+ {
buttonAccent = "";
}
- if (data.control.Control.CanWake == '1') {
+ if (data.control.Control.CanWake == '1')
+ {
- $scope.ptzPresets.push({
+ $scope.ptzPresets.push(
+ {
name: 'W',
icon: "ion-eye",
cmd: 'wake',
@@ -1203,8 +1274,10 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
}
- if (data.control.Control.CanSleep == '1') {
- $scope.ptzPresets.push({
+ if (data.control.Control.CanSleep == '1')
+ {
+ $scope.ptzPresets.push(
+ {
name: 'S',
icon: "ion-eye-disabled",
cmd: 'sleep',
@@ -1213,8 +1286,10 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
}
- if (data.control.Control.CanReset == '1') {
- $scope.ptzPresets.push({
+ if (data.control.Control.CanReset == '1')
+ {
+ $scope.ptzPresets.push(
+ {
name: 'R',
icon: "ion-ios-loop-strong",
cmd: 'reset',
@@ -1223,29 +1298,30 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
}
-
NVRDataModel.log("ConfigurePTZ Modal: ControlDB reports PTZ command to be " + $scope.ptzMoveCommand);
})
- .error(function (data) {
+ .error(function(data)
+ {
// console.log("** Error retrieving move PTZ command");
NVRDataModel.log("ConfigurePTZ : Error retrieving PTZ command " + JSON.stringify(data), "error");
});
- } else {
+ }
+ else
+ {
NVRDataModel.log("configurePTZ " + mid + " is not PTZ controllable");
}
})
- .error(function (data) {
+ .error(function(data)
+ {
// console.log("** Error retrieving move PTZ command");
NVRDataModel.log("configurePTZ : Error retrieving PTZ command " + JSON.stringify(data), "error");
});
-
-
}
-
- $scope.$on('modal.shown', function () {
+ $scope.$on('modal.shown', function()
+ {
$scope.monStatus = "";
document.addEventListener("pause", onPause, false);
@@ -1259,24 +1335,25 @@ angular.module('zmApp.controllers').controller('MonitorModalCtrl', ['$scope', '$
$scope.monStatus = "";
$scope.isCycle = ld.cycleMonitors;
$scope.cycleText = $scope.isCycle ? $translate.instant('kOn') : $translate.instant('kOff');
-
- $scope.quality = (NVRDataModel.getBandwidth()=="lowbw")? zm.monSingleImageQualityLowBW:ld.monSingleImageQuality;
+
+ $scope.quality = (NVRDataModel.getBandwidth() == "lowbw") ? zm.monSingleImageQualityLowBW : ld.monSingleImageQuality;
configurePTZ($scope.monitorId);
- if (ld.cycleMonitors) {
+ if (ld.cycleMonitors)
+ {
NVRDataModel.debug("Cycling enabled at " + ld.cycleMonitorsInterval);
$interval.cancel(cycleHandle);
- cycleHandle = $interval(function () {
+ cycleHandle = $interval(function()
+ {
moveToMonitor($scope.monitorId, 1);
// console.log ("Refreshing Image...");
}.bind(this), ld.cycleMonitorsInterval * 1000);
}
-
});
-}]); \ No newline at end of file
+}]);
diff --git a/www/js/MontageCtrl.js b/www/js/MontageCtrl.js
index 6e7571ce..8a8209df 100644
--- a/www/js/MontageCtrl.js
+++ b/www/js/MontageCtrl.js
@@ -3,1329 +3,1684 @@
/* jslint browser: true*/
/* 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 main
- //---------------------------------------------------------------------
-
- var intervalHandleMontage; // image re-load handler
- var intervalHandleAlarmStatus; // status of each alarm state
-
-
- var gridcontainer;
- var pckry, draggie;
- var draggies;
- var loginData;
- var timestamp;
- var sizeInProgress;
- var modalIntervalHandle;
- var ld;
- var refreshSec;
-
-
- //--------------------------------------------------------------------------------------
- // Handles bandwidth change, if required
- //
- //--------------------------------------------------------------------------------------
-
- $rootScope.$on("bandwidth-change", function (e,data) {
- // not called for offline, I'm only interested in BW switches
- NVRDataModel.debug("Got network change:" + data);
- var ds;
- if (data == 'lowbw') {
- ds = $translate.instant('kLowBWDisplay');
- } else {
- ds = $translate.instant('kHighBWDisplay');
- }
- NVRDataModel.displayBanner('net', [ds]);
- var ld = NVRDataModel.getLogin();
- refreshSec = (NVRDataModel.getBandwidth()=='lowbw') ? ld.refreshSecLowBW : ld.refreshSec;
- $interval.cancel(intervalHandleMontage);
- intervalHandleMontage = $interval(function () {
- loadNotifications();
- }.bind(this), refreshSec * 1000);
-
-
- if (NVRDataModel.getBandwidth() == 'lowbw')
- {
- NVRDataModel.debug("Enabling low bandwidth parameters");
- $scope.LoginData.montageQuality = zm.montageQualityLowBW;
- $scope.LoginData.singleImageQuality = zm.eventSingleImageQualityLowBW;
- $scope.LoginData.montageHistoryQuality = zm.montageQualityLowBW;
-
- }
- });
-
-
- // --------------------------------------------------------
- // Handling of back button in case modal is open should
- // close the modal
- // --------------------------------------------------------
+ .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)
+ {
+ //---------------------------------------------------------------------
+ // Controller main
+ //---------------------------------------------------------------------
+
+ var intervalHandleMontage; // image re-load handler
+ var intervalHandleAlarmStatus; // status of each alarm state
+
+ var gridcontainer;
+ var pckry, draggie;
+ var draggies;
+ var loginData;
+ var timestamp;
+ var sizeInProgress;
+ var modalIntervalHandle;
+ var ld;
+ var refreshSec;
+
+ //--------------------------------------------------------------------------------------
+ // Handles bandwidth change, if required
+ //
+ //--------------------------------------------------------------------------------------
+
+ $rootScope.$on("bandwidth-change", function(e, data)
+ {
+ // not called for offline, I'm only interested in BW switches
+ NVRDataModel.debug("Got network change:" + data);
+ var ds;
+ if (data == 'lowbw')
+ {
+ ds = $translate.instant('kLowBWDisplay');
+ }
+ else
+ {
+ ds = $translate.instant('kHighBWDisplay');
+ }
+ NVRDataModel.displayBanner('net', [ds]);
+ var ld = NVRDataModel.getLogin();
+ refreshSec = (NVRDataModel.getBandwidth() == 'lowbw') ? ld.refreshSecLowBW : ld.refreshSec;
+ $interval.cancel(intervalHandleMontage);
+ intervalHandleMontage = $interval(function()
+ {
+ loadNotifications();
+ }.bind(this), refreshSec * 1000);
- $ionicPlatform.registerBackButtonAction(function (e) {
- e.preventDefault();
- if ($scope.modal != undefined && $scope.modal.isShown()) {
- // switch off awake, as liveview is finished
- NVRDataModel.debug("Modal is open, closing it");
- NVRDataModel.setAwake(false);
- $scope.isModalActive = false;
- cleanupOnClose();
- } else {
- NVRDataModel.debug("Modal is closed, so toggling or exiting");
- if (!$ionicSideMenuDelegate.isOpenLeft()) {
- $ionicSideMenuDelegate.toggleLeft();
+ if (NVRDataModel.getBandwidth() == 'lowbw')
+ {
+ NVRDataModel.debug("Enabling low bandwidth parameters");
+ $scope.LoginData.montageQuality = zm.montageQualityLowBW;
+ $scope.LoginData.singleImageQuality = zm.eventSingleImageQualityLowBW;
+ $scope.LoginData.montageHistoryQuality = zm.montageQualityLowBW;
- } else {
- navigator.app.exitApp();
}
+ });
- }
+ // --------------------------------------------------------
+ // Handling of back button in case modal is open should
+ // close the modal
+ // --------------------------------------------------------
- }, 1000);
+ $ionicPlatform.registerBackButtonAction(function(e)
+ {
+ e.preventDefault();
+ if ($scope.modal != undefined && $scope.modal.isShown())
+ {
+ // switch off awake, as liveview is finished
+ NVRDataModel.debug("Modal is open, closing it");
+ NVRDataModel.setAwake(false);
+ $scope.isModalActive = false;
+ cleanupOnClose();
+ }
+ else
+ {
+ NVRDataModel.debug("Modal is closed, so toggling or exiting");
+ if (!$ionicSideMenuDelegate.isOpenLeft())
+ {
+ $ionicSideMenuDelegate.toggleLeft();
- /*$scope.toggleHide = function(mon)
- {
-
-
- if (mon.Monitor.listDisplay == 'noshow')
- mon.Monitor.listDisplay = 'show';
- else
- mon.Monitor.listDisplay = 'noshow';
+ }
+ else
+ {
+ navigator.app.exitApp();
+ }
-
-
- };*/
+ }
+ }, 1000);
+ /*$scope.toggleHide = function(mon)
+ {
+
+ if (mon.Monitor.listDisplay == 'noshow')
+ mon.Monitor.listDisplay = 'show';
+ else
+ mon.Monitor.listDisplay = 'noshow';
- // called by afterEnter to load Packery
- function initPackery() {
+
+
+ };*/
+ // called by afterEnter to load Packery
+ function initPackery()
+ {
- $ionicLoading.show({
- template: $translate.instant('kArrangingImages'),
- noBackdrop: true,
- duration: zm.loadingTimeout
- });
+ $ionicLoading.show(
+ {
+ template: $translate.instant('kArrangingImages'),
+ noBackdrop: true,
+ duration: zm.loadingTimeout
+ });
- var progressCalled = false;
- draggies = [];
- var layouttype = true;
- var ld = NVRDataModel.getLogin();
+ var progressCalled = false;
+ draggies = [];
+ var layouttype = true;
+ var ld = NVRDataModel.getLogin();
+ var positionsStr = ld.packeryPositions;
+ var positions = {};
- var positionsStr = ld.packeryPositions;
- var positions = {};
+ if (positionsStr == '')
+ {
+ NVRDataModel.log("Did NOT find a packery layout");
+ layouttype = true;
+ }
+ else
+ {
- if (positionsStr == '') {
- NVRDataModel.log("Did NOT find a packery layout");
- layouttype = true;
- } else {
+ //console.log ("POSITION STR IS " + positionsStr);
+ positions = JSON.parse(positionsStr);
+ NVRDataModel.log("found a packery layout");
- //console.log ("POSITION STR IS " + positionsStr);
- positions = JSON.parse(positionsStr);
- NVRDataModel.log("found a packery layout");
-
- layouttype = false;
- }
+ layouttype = false;
+ }
+ var cnt = 0;
+ $scope.MontageMonitors.forEach(function(elem)
+ {
+ if ((elem.Monitor.Enabled != '0') && (elem.Monitor.Function != 'None'))
+ cnt++;
+ });
- var cnt = 0;
- $scope.MontageMonitors.forEach(function (elem) {
- if ((elem.Monitor.Enabled != '0') && (elem.Monitor.Function != 'None'))
- cnt++;
- });
+ NVRDataModel.log("Monitors that are active and not DOM hidden: " + cnt + " while grid has " + positions.length);
- NVRDataModel.log("Monitors that are active and not DOM hidden: " + cnt + " while grid has " + positions.length);
+ if (cnt > NVRDataModel.getLogin().maxMontage)
+ {
+ cnt = NVRDataModel.getLogin().maxMontage;
+ NVRDataModel.log("restricting monitor count to " + cnt + " due to max-montage setting");
+ }
- if (cnt > NVRDataModel.getLogin().maxMontage) {
- cnt = NVRDataModel.getLogin().maxMontage;
- NVRDataModel.log("restricting monitor count to " + cnt + " due to max-montage setting");
- }
+ if (cnt != positions.length)
+ {
- if (cnt != positions.length) {
+ NVRDataModel.log("Whoops!! Monitors have changed. I'm resetting layouts, sorry!");
+ layouttype = true;
+ positions = {};
+ }
- NVRDataModel.log("Whoops!! Monitors have changed. I'm resetting layouts, sorry!");
- layouttype = true;
- positions = {};
- }
+ var elem = angular.element(document.getElementById("mygrid"));
- var elem = angular.element(document.getElementById("mygrid"));
+ //console.log ("**** mygrid is " + JSON.stringify(elem));
- //console.log ("**** mygrid is " + JSON.stringify(elem));
-
-
- pckry = new Packery('.grid', {
+ pckry = new Packery('.grid',
+ {
itemSelector: '.grid-item',
percentPosition: true,
columnWidth: '.grid-sizer',
gutter: 0,
- initLayout: layouttype
+ initLayout: layouttype,
+ shiftPercentResize: true
});
- imagesLoaded(elem).on('progress', function (instance, img) {
+ imagesLoaded(elem).on('progress', function(instance, img)
+ {
- var result = img.isLoaded ? 'loaded' : 'broken';
- NVRDataModel.debug( '~~loaded image is ' + result + ' for ' + img.img.src );
- pckry.layout();
- progressCalled = true;
-
+ var result = img.isLoaded ? 'loaded' : 'broken';
+ NVRDataModel.debug('~~loaded image is ' + result + ' for ' + img.img.src);
- // if (layouttype) $timeout (function(){layout(pckry);},100);
- });
+ // lay out every image if a pre-arranged position has not been found
- imagesLoaded(elem).on('always', function () {
- //console.log ("******** ALL IMAGES LOADED");
- // $scope.$digest();
- NVRDataModel.debug("All images loaded");
-
- $scope.areImagesLoading = false;
-
+ $timeout(function()
+ {
+ if (layouttype) pckry.layout();
+ }, 100);
- $ionicLoading.hide();
+ progressCalled = true;
- if (!progressCalled) {
- NVRDataModel.log("*** PROGRESS WAS NOT CALLED");
- pckry.reloadItems();
- }
-
-
- $timeout(function () {
+ // if (layouttype) $timeout (function(){layout(pckry);},100);
+ });
- pckry.getItemElements().forEach(function (itemElem) {
-
- draggie = new Draggabilly(itemElem);
- pckry.bindDraggabillyEvents(draggie);
- draggies.push(draggie);
- draggie.disable();
- draggie.unbindHandles();
- });
+ imagesLoaded(elem).on('always', function()
+ {
+ //console.log ("******** ALL IMAGES LOADED");
+ // $scope.$digest();
+ NVRDataModel.debug("All images loaded");
- pckry.on('dragItemPositioned', itemDragged);
+ $scope.areImagesLoading = false;
+ $ionicLoading.hide();
+ if (!progressCalled)
+ {
+ NVRDataModel.log("*** PROGRESS WAS NOT CALLED");
+ // pckry.reloadItems();
+ }
- if (!isEmpty(positions)) {
- NVRDataModel.log("Arranging as per packery grid");
+ $timeout(function()
+ {
- 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) {
- $scope.MontageMonitors[i].Monitor.gridScale = positions[j].size;
- $scope.MontageMonitors[i].Monitor.listDisplay = positions[j].display;
- NVRDataModel.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);
- }
- }
+ pckry.getItemElements().forEach(function(itemElem)
+ {
+ draggie = new Draggabilly(itemElem);
+ pckry.bindDraggabillyEvents(draggie);
+ draggies.push(draggie);
+ draggie.disable();
+ draggie.unbindHandles();
+ });
- NVRDataModel.debug("All images loaded, doing image layout");
-
-
- }
- $timeout(function () {
- NVRDataModel.log("Force calling resize");
- pckry.reloadItems();
- //pckry.initShiftLayout(positions,"data-item-id");
- // now do a jiggle
- $timeout (function() {pckry.layout(); } ,300);
-
- }, 20);
-
-
+ pckry.on('dragItemPositioned', itemDragged);
+
+ if (!isEmpty(positions))
+ {
+ NVRDataModel.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)
+ {
+ $scope.MontageMonitors[i].Monitor.gridScale = positions[j].size;
+ $scope.MontageMonitors[i].Monitor.listDisplay = positions[j].display;
+ NVRDataModel.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);
+ }
+ }
- //pckry.onresize();
+ NVRDataModel.debug("All images loaded, doing image layout");
- }, 20);
+ }
+ $timeout(function()
+ {
+ //NVRDataModel.log("Force calling resize");
+ ///pckry.reloadItems();
+ ///positions is defined only if layouttype was false
+ if (!layouttype) pckry.initShiftLayout(positions, "data-item-id");
+ // now do a jiggle
+ $timeout(function()
+ {
+ NVRDataModel.debug("doing the jiggle and dance...");
+ pckry.resize(true);
+ }, 300);
- });
+ }, 100);
+ //pckry.onresize();
+ }, 20);
- function itemDragged(item) {
- NVRDataModel.debug("drag complete");
+ });
- //pckry.getItemElements().forEach(function (itemElem) {
+ function itemDragged(item)
+ {
+ NVRDataModel.debug("drag complete");
+ $timeout (function(){pckry.shiftLayout();},20);
- //console.log (itemElem.attributes['data-item-id'].value+" size "+itemElem.attributes['data-item-size'].value );
- // });
+ pckry.once ('layoutComplete', function() {
- var positions = pckry.getShiftPositions('data-item-id');
- //console.log ("POSITIONS MAP " + JSON.stringify(positions));
- var ld = NVRDataModel.getLogin();
- ld.packeryPositions = JSON.stringify(positions);
- //console.log ("Saving " + ld.packeryPositions);
- NVRDataModel.setLogin(ld);
- }
+ var positions = pckry.getShiftPositions('data-item-id');
+ //console.log ("POSITIONS MAP " + JSON.stringify(positions));
+ var ld = NVRDataModel.getLogin();
+ ld.packeryPositions = JSON.stringify(positions);
+ //console.log ("Saving " + ld.packeryPositions);
+ NVRDataModel.setLogin(ld);
+ NVRDataModel.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;
+
+ function isEmpty(obj)
+ {
+ for (var prop in obj)
+ {
+ return false;
+ }
+ return true;
}
- return attr;
- };
+ //-----------------------------------------------------------------------
+ // 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;
+ }
- //-----------------------------------------------------------------------
- // cycle through all displayed monitors and check alarm status
- //-----------------------------------------------------------------------
+ return attr;
+ };
- function loadAlarmStatus() {
+ //-----------------------------------------------------------------------
+ // cycle through all displayed monitors and check alarm status
+ //-----------------------------------------------------------------------
- if ((NVRDataModel.versionCompare($rootScope.apiVersion, "1.30") == -1) ||
- (NVRDataModel.getBandwidth() == 'lowbw') ||
- (NVRDataModel.getLogin().disableAlarmCheckMontage == true))
+ function loadAlarmStatus()
{
- return;
- }
+ if ((NVRDataModel.versionCompare($rootScope.apiVersion, "1.30") == -1) ||
+ (NVRDataModel.getBandwidth() == 'lowbw') ||
+ (NVRDataModel.getLogin().disableAlarmCheckMontage == true))
+ {
-
+ return;
+ }
+
+ for (var i = 0; i < $scope.MontageMonitors.length; i++)
+ {
+ if (($scope.MontageMonitors[i].Monitor.Function == 'None') ||
+ ($scope.MontageMonitors[i].Monitor.Enabled == '0') ||
+ ($scope.MontageMonitors[i].Monitor.listDisplay == 'noshow'))
+ {
+ continue;
+ }
+ getAlarmStatus($scope.MontageMonitors[i]);
- for (var i = 0; i < $scope.MontageMonitors.length; i++) {
- if (($scope.MontageMonitors[i].Monitor.Function == 'None') ||
- ($scope.MontageMonitors[i].Monitor.Enabled == '0') ||
- ($scope.MontageMonitors[i].Monitor.listDisplay == 'noshow')) {
- continue;
}
- getAlarmStatus($scope.MontageMonitors[i]);
}
- }
-
- //-----------------------------------------------------------------------
- // get alarm status over HTTP for a single monitor
- //-----------------------------------------------------------------------
- function getAlarmStatus(monitor) {
- var apiurl = NVRDataModel.getLogin().apiurl;
- //console.log ("ALARM CALLED WITH " +JSON.stringify(monitor));
-
- var alarmurl = apiurl + "/monitors/alarm/id:" + monitor.Monitor.Id + "/command:status.json";
- // console.log("Alarm Check: Invoking " + alarmurl);
-
-
- $http.get(alarmurl)
- .then(function (data) {
- // NVRDataModel.debug ("Success in monitor alarmed status " + JSON.stringify(data));
-
- var sid = parseInt(data.data.status);
- switch (sid) {
- case 0: // idle
- monitor.Monitor.alarmState = 'color:rgba(0,0,0,0);';
- break;
- case 1: // pre alarm
- monitor.Monitor.alarmState = 'color:#e67e22;';
- break;
- case 2: // alarm
- monitor.Monitor.alarmState = 'color:#D91E18;';
- break;
- case 3: // alert
- monitor.Monitor.alarmState = 'color:#e67e22;';
- break;
- case 4:
- monitor.Monitor.alarmState = 'color:#26A65B;';
- break;
+ //-----------------------------------------------------------------------
+ // get alarm status over HTTP for a single monitor
+ //-----------------------------------------------------------------------
+ function getAlarmStatus(monitor)
+ {
+ var apiurl = NVRDataModel.getLogin().apiurl;
+ //console.log ("ALARM CALLED WITH " +JSON.stringify(monitor));
+
+ var alarmurl = apiurl + "/monitors/alarm/id:" + monitor.Monitor.Id + "/command:status.json";
+ // console.log("Alarm Check: Invoking " + alarmurl);
+
+ $http.get(alarmurl)
+ .then(function(data)
+ {
+ // NVRDataModel.debug ("Success in monitor alarmed status " + JSON.stringify(data));
+
+ var sid = parseInt(data.data.status);
+ switch (sid)
+ {
+ case 0: // idle
+ monitor.Monitor.alarmState = 'color:rgba(0,0,0,0);';
+ break;
+ case 1: // pre alarm
+ monitor.Monitor.alarmState = 'color:#e67e22;';
+ break;
+ case 2: // alarm
+ monitor.Monitor.alarmState = 'color:#D91E18;';
+ break;
+ case 3: // alert
+ monitor.Monitor.alarmState = 'color:#e67e22;';
+ break;
+ case 4:
+ monitor.Monitor.alarmState = 'color:#26A65B;';
+ break;
- }
+ }
- },
- function (error) {
+ },
+ function(error)
+ {
+ monitor.Monitor.alarmState = 'color:rgba(0,0,0,0);';
+ NVRDataModel.debug("Error in monitor alarmed status ");
+ });
+ }
- monitor.Monitor.alarmState = 'color:rgba(0,0,0,0);';
- NVRDataModel.debug("Error in monitor alarmed status ");
- });
- }
+ //-----------------------------------------------------------------------
+ // re-compute rand so snapshot in montage reloads
+ //-----------------------------------------------------------------------
+ function loadNotifications()
+ {
- //-----------------------------------------------------------------------
- // re-compute rand so snapshot in montage reloads
- //-----------------------------------------------------------------------
+ if ($scope.areImagesLoading)
+ {
+ NVRDataModel.debug("skipping image refresh, packery is still loading");
+ return;
+ }
- function loadNotifications() {
+ //if (pckry && !$scope.isDragabillyOn) pckry.shiftLayout();
+ $rootScope.rand = Math.floor((Math.random() * 100000) + 1);
+
+ // if you see the time move, montage should move
+
+ if ($scope.iconTimeNow == 'local')
+ $scope.timeNow = moment().format(NVRDataModel.getTimeFormatSec());
+ else
+ $scope.timeNow = moment().tz(NVRDataModel.getTimeZoneNow()).format(NVRDataModel.getTimeFormatSec());
+ //$scope.timeNow = moment().format(NVRDataModel.getTimeFormatSec());
+
+ //console.log ("Inside Montage timer...");
- if ($scope.areImagesLoading) {
- NVRDataModel.debug("skipping image refresh, packery is still loading");
- return;
}
- //if (pckry && !$scope.isDragabillyOn) pckry.shiftLayout();
- $rootScope.rand = Math.floor((Math.random() * 100000) + 1);
-
- // if you see the time move, montage should move
-
- if ($scope.iconTimeNow == 'local')
- $scope.timeNow = moment().format(NVRDataModel.getTimeFormatSec());
- else
- $scope.timeNow = moment().tz(NVRDataModel.getTimeZoneNow()).format(NVRDataModel.getTimeFormatSec());
- //$scope.timeNow = moment().format(NVRDataModel.getTimeFormatSec());
+ $scope.cancelReorder = function()
+ {
+ $scope.modal.remove();
+ };
- //console.log ("Inside Montage timer...");
+ $scope.saveReorder = function()
+ {
+ NVRDataModel.debug("Saving monitor hide/unhide");
- }
+ // redo packery as monitor status has changed
+ // DOM may need reloading if you've hidden/unhidden stuff
+ $scope.MontageMonitors = $scope.copyMontage;
+ $scope.modal.remove();
- $scope.cancelReorder = function () {
- $scope.modal.remove();
- };
+ $timeout(function()
+ {
-
-
-
- $scope.saveReorder = function () {
- NVRDataModel.debug("Saving monitor hide/unhide");
+ draggies.forEach(function(drag)
+ {
+ drag.destroy();
+ });
+ pckry.reloadItems();
+ draggies = [];
+ pckry.once('layoutComplete', savePackeryOrder);
+ pckry.layout();
+ }, 400);
- // redo packery as monitor status has changed
- // DOM may need reloading if you've hidden/unhidden stuff
- $scope.MontageMonitors = $scope.copyMontage;
- $scope.modal.remove();
-
- $timeout(function () {
-
- draggies.forEach(function (drag) {
- drag.destroy();
- });
-
- pckry.reloadItems();
- draggies = [];
- pckry.once ('layoutComplete', savePackeryOrder);
- pckry.layout();
-
-
-
- }, 400);
-
- function savePackeryOrder ()
- {
- $timeout(function () {
+ function savePackeryOrder()
+ {
+ $timeout(function()
+ {
var positions = pckry.getShiftPositions('data-item-id');
NVRDataModel.debug("POSITIONS MAP " + JSON.stringify(positions));
var ld = NVRDataModel.getLogin();
ld.packeryPositions = JSON.stringify(positions);
//console.log ("Savtogging " + ld.packeryPositions);
NVRDataModel.setLogin(ld);
-
- pckry.getItemElements().forEach(function (itemElem) {
+
+ pckry.getItemElements().forEach(function(itemElem)
+ {
draggie = new Draggabilly(itemElem);
pckry.bindDraggabillyEvents(draggie);
draggies.push(draggie);
draggie.disable();
});
-
+
$ionicScrollDelegate.$getByHandle("montage-delegate").scrollTop();
-
- // Now also ask DataModel to update its monitor display status
- NVRDataModel.reloadMonitorDisplayStatus();
- pckry.layout();
- },20);
- }
-
- };
+ // Now also ask DataModel to update its monitor display status
+ NVRDataModel.reloadMonitorDisplayStatus();
+ pckry.layout();
+ }, 20);
+ }
+ };
- $scope.toggleHide = function (i) {
+ $scope.toggleHide = function(i)
+ {
- if ($scope.copyMontage[i].Monitor.listDisplay == 'show')
- $scope.copyMontage[i].Monitor.listDisplay = 'noshow';
- else
- $scope.copyMontage[i].Monitor.listDisplay = 'show';
+ if ($scope.copyMontage[i].Monitor.listDisplay == 'show')
+ $scope.copyMontage[i].Monitor.listDisplay = 'noshow';
+ else
+ $scope.copyMontage[i].Monitor.listDisplay = 'show';
- NVRDataModel.debug("index " + i + " is now " + $scope.copyMontage[i].Monitor.listDisplay);
- };
+ NVRDataModel.debug("index " + i + " is now " + $scope.copyMontage[i].Monitor.listDisplay);
+ };
- $scope.hideUnhide = function () {
- if ($scope.isDragabillyOn) {
- dragToggle();
- }
- // make a copy of the current list and work on that
- // this is to avoid packery screw ups while you are hiding/unhiding
- $scope.copyMontage = angular.copy($scope.MontageMonitors);
- $ionicModal.fromTemplateUrl('templates/reorder-modal.html', {
- scope: $scope,
- animation: 'slide-in-up'
- })
- .then(function (modal) {
- $scope.modal = modal;
- $scope.modal.show();
- });
- };
+ $scope.hideUnhide = function()
+ {
+ if ($scope.isDragabillyOn)
+ {
+ dragToggle();
+ }
+ // make a copy of the current list and work on that
+ // this is to avoid packery screw ups while you are hiding/unhiding
+ $scope.copyMontage = angular.copy($scope.MontageMonitors);
+ $ionicModal.fromTemplateUrl('templates/reorder-modal.html',
+ {
+ scope: $scope,
+ animation: 'slide-in-up'
+ })
+ .then(function(modal)
+ {
+ $scope.modal = modal;
+ $scope.modal.show();
+ });
+ };
+ /*
+ $scope.closeReorderModal = function () {
+
+ $scope.modal.remove();
+ };
+ */
- /*
- $scope.closeReorderModal = function () {
-
- $scope.modal.remove();
+ //----------------------------------------------------------------
+ // Alarm emit handling
+ //----------------------------------------------------------------
+ $rootScope.$on("alarm", function(event, args)
+ {
+ // FIXME: I should probably unregister this instead
+ if (typeof $scope.monitors === undefined)
+ return;
+ //console.log ("***EVENT TRAP***");
+ var alarmMonitors = args.message;
+ for (var i = 0; i < alarmMonitors.length; i++)
+ {
+ //console.log ("**** TRAPPED EVENT: "+alarmMonitors[i]);
- };
- */
-
- //----------------------------------------------------------------
- // Alarm emit handling
- //----------------------------------------------------------------
- $rootScope.$on("alarm", function (event, args) {
- // FIXME: I should probably unregister this instead
- if (typeof $scope.monitors === undefined)
- return;
- //console.log ("***EVENT TRAP***");
- var alarmMonitors = args.message;
- for (var i = 0; i < alarmMonitors.length; i++) {
- //console.log ("**** TRAPPED EVENT: "+alarmMonitors[i]);
-
- for (var j = 0; j < $scope.MontageMonitors.length; j++) {
- if ($scope.MontageMonitors[j].Monitor.Id == alarmMonitors[i]) {
- NVRDataModel.debug("Enabling alarm for Monitor:" + $scope.monitors[j].Monitor.Id);
- $scope.MontageMonitors[j].Monitor.isAlarmed = true;
- scheduleRemoveFlash(j);
+ for (var j = 0; j < $scope.MontageMonitors.length; j++)
+ {
+ if ($scope.MontageMonitors[j].Monitor.Id == alarmMonitors[i])
+ {
+ NVRDataModel.debug("Enabling alarm for Monitor:" + $scope.monitors[j].Monitor.Id);
+ $scope.MontageMonitors[j].Monitor.isAlarmed = true;
+ scheduleRemoveFlash(j);
+ }
}
+
}
+ });
+
+ function scheduleRemoveFlash(id)
+ {
+ NVRDataModel.debug("Scheduled a " + zm.alarmFlashTimer + "ms timer for dis-alarming monitor ID:" + $scope.MontageMonitors[id].Monitor.Id);
+ $timeout(function()
+ {
+ $scope.MontageMonitors[id].Monitor.isAlarmed = false;
+ NVRDataModel.debug("dis-alarming monitor ID:" + $scope.MontageMonitors[id].Monitor.Id);
+ }, zm.alarmFlashTimer);
}
+ //----------------------------------------------------------------
+ // Alarm notification handling
+ //----------------------------------------------------------------
+ $scope.handleAlarms = function()
+ {
+ $rootScope.isAlarm = !$rootScope.isAlarm;
+ if (!$rootScope.isAlarm)
+ {
+ $rootScope.alarmCount = "0";
+ $ionicHistory.nextViewOptions(
+ {
+ disableBack: true
+ });
+ $state.go("events",
+ {
+ "id": 0,
+ "playEvent": false
+ },
+ {
+ reload: true
+ });
+ return;
+ }
+ };
- });
-
- function scheduleRemoveFlash(id) {
- NVRDataModel.debug("Scheduled a " + zm.alarmFlashTimer + "ms timer for dis-alarming monitor ID:" + $scope.MontageMonitors[id].Monitor.Id);
- $timeout(function () {
- $scope.MontageMonitors[id].Monitor.isAlarmed = false;
- NVRDataModel.debug("dis-alarming monitor ID:" + $scope.MontageMonitors[id].Monitor.Id);
- }, zm.alarmFlashTimer);
- }
-
- //----------------------------------------------------------------
- // Alarm notification handling
- //----------------------------------------------------------------
- $scope.handleAlarms = function () {
- $rootScope.isAlarm = !$rootScope.isAlarm;
- if (!$rootScope.isAlarm) {
- $rootScope.alarmCount = "0";
- $ionicHistory.nextViewOptions({
- disableBack: true
- });
- $state.go("events", {
- "id": 0,
- "playEvent":false
- }, {
- reload: true
- });
- return;
- }
- };
+ $scope.handleAlarmsWhileMinimized = function()
+ {
+ $rootScope.isAlarm = !$rootScope.isAlarm;
+
+ $scope.minimal = !$scope.minimal;
+ NVRDataModel.debug("MontageCtrl: switch minimal is " + $scope.minimal);
+ ionic.Platform.fullScreen($scope.minimal, !$scope.minimal);
+ //console.log ("alarms:Cancelling timer");
+ $interval.cancel(intervalHandleMontage);
+ $interval.cancel(intervalHandleAlarmStatus);
+
+ if (!$rootScope.isAlarm)
+ {
+ $rootScope.alarmCount = "0";
+ $ionicHistory.nextViewOptions(
+ {
+ disableBack: true
+ });
+ $state.go("events",
+ {
+ "id": 0,
+ "playEvent": false
+ },
+ {
+ reload: true
+ });
+ return;
+ }
+ };
- $scope.handleAlarmsWhileMinimized = function () {
- $rootScope.isAlarm = !$rootScope.isAlarm;
+ //-------------------------------------------------------------
+ // this is checked to make sure we are not pulling images
+ // when app is in background. This is a problem with Android,
+ // for example
+ //-------------------------------------------------------------
- $scope.minimal = !$scope.minimal;
- NVRDataModel.debug("MontageCtrl: switch minimal is " + $scope.minimal);
- ionic.Platform.fullScreen($scope.minimal, !$scope.minimal);
- //console.log ("alarms:Cancelling timer");
- $interval.cancel(intervalHandleMontage);
- $interval.cancel(intervalHandleAlarmStatus);
+ $scope.isBackground = function()
+ {
+ //console.log ("Is background called from Montage and returned " +
+ //NVRDataModel.isBackground());
+ return NVRDataModel.isBackground();
+ };
- if (!$rootScope.isAlarm) {
- $rootScope.alarmCount = "0";
- $ionicHistory.nextViewOptions({
+ //---------------------------------------------------------------------
+ // Triggered when you enter/exit full screen
+ //---------------------------------------------------------------------
+ $scope.switchMinimal = function()
+ {
+ $scope.minimal = !$scope.minimal;
+ NVRDataModel.debug("MontageCtrl: switch minimal is " + $scope.minimal);
+ // console.log("Hide Statusbar");
+ ionic.Platform.fullScreen($scope.minimal, !$scope.minimal);
+ //console.log ("minimal switch:Cancelling timer");
+ $interval.cancel(intervalHandleMontage); //we will renew on reload
+ $interval.cancel(intervalHandleAlarmStatus);
+ // We are reloading this view, so we don't want entry animations
+ $ionicHistory.nextViewOptions(
+ {
+ disableAnimate: true,
disableBack: true
});
- $state.go("events", {
- "id": 0,
- "playEvent":false
- }, {
- reload: true
+ $state.go("montage",
+ {
+ minimal: $scope.minimal,
+ isRefresh: true
});
return;
- }
- };
-
-
- //-------------------------------------------------------------
- // this is checked to make sure we are not pulling images
- // when app is in background. This is a problem with Android,
- // for example
- //-------------------------------------------------------------
-
- $scope.isBackground = function () {
- //console.log ("Is background called from Montage and returned " +
- //NVRDataModel.isBackground());
- return NVRDataModel.isBackground();
- };
-
-
- //---------------------------------------------------------------------
- // Triggered when you enter/exit full screen
- //---------------------------------------------------------------------
- $scope.switchMinimal = function () {
- $scope.minimal = !$scope.minimal;
- NVRDataModel.debug("MontageCtrl: switch minimal is " + $scope.minimal);
- // console.log("Hide Statusbar");
- ionic.Platform.fullScreen($scope.minimal, !$scope.minimal);
- //console.log ("minimal switch:Cancelling timer");
- $interval.cancel(intervalHandleMontage); //we will renew on reload
- $interval.cancel(intervalHandleAlarmStatus);
- // We are reloading this view, so we don't want entry animations
- $ionicHistory.nextViewOptions({
- disableAnimate: true,
- disableBack: true
- });
- $state.go("montage", {
- minimal: $scope.minimal,
- isRefresh: true
- });
- return;
- };
-
- //---------------------------------------------------------------------
- // Show/Hide PTZ control in monitor view
- //---------------------------------------------------------------------
- $scope.togglePTZ = function () {
- $scope.showPTZ = !$scope.showPTZ;
- };
+ };
+ //---------------------------------------------------------------------
+ // Show/Hide PTZ control in monitor view
+ //---------------------------------------------------------------------
+ $scope.togglePTZ = function()
+ {
+ $scope.showPTZ = !$scope.showPTZ;
+ };
- $scope.toggleSelectItem = function (ndx) {
+ function getIndex (mid)
+ {
+ var ndx = 0;
+ for (var i=0; i< $scope.MontageMonitors.length; i++)
+ {
+ if ($scope.MontageMonitors[i].Monitor.Id == mid)
+ {
+ ndx = i;
+ break;
+ }
+ }
+ return ndx;
- if ($scope.MontageMonitors[ndx].Monitor.selectStyle !== "undefined" && $scope.MontageMonitors[ndx].Monitor.selectStyle == "dragborder-selected") {
- $scope.MontageMonitors[ndx].Monitor.selectStyle = "";
- } else {
- $scope.MontageMonitors[ndx].Monitor.selectStyle = "dragborder-selected";
}
- //console.log ("Switched value to " + $scope.MontageMonitors[ndx].Monitor.selectStyle);
- };
- //---------------------------------------------------------------------
- // Called when you enable/disable dragging
- //---------------------------------------------------------------------
+ $scope.toggleStamp = function ()
+ {
+ if (!$scope.isDragabillyOn) return;
+ var found = false;
+
- $scope.dragToggle = function () {
- dragToggle();
+ for (var i=0; i< $scope.MontageMonitors.length; i++)
+ {
+ if ($scope.MontageMonitors[i].Monitor.selectStyle == 'dragborder-selected')
+ {
+
+ findPackeryElement(i);
+ }
+ }
+ function findPackeryElement(i)
+ {
+ pckry.getItemElements().forEach(function(elem)
+ {
- };
+ var id = elem.getAttribute("data-item-id");
+ if (id == $scope.MontageMonitors[i].Monitor.Id)
+ {
+ if ($scope.MontageMonitors[i].Monitor.isStamp)
+ pckry.unstamp(elem);
+ else
+ pckry.stamp(elem);
- function dragToggle() {
- var i;
- $scope.isDragabillyOn = !$scope.isDragabillyOn;
+ $scope.MontageMonitors[i].Monitor.isStamp = !$scope.MontageMonitors[i].Monitor.isStamp;
+ NVRDataModel.debug ("Stamp for "+$scope.MontageMonitors[i].Monitor.Name + " is:"+$scope.MontageMonitors[i].Monitor.isStamp );
+ //break;
- $ionicSideMenuDelegate.canDragContent($scope.isDragabillyOn ? false : true);
+ }
+ });
+ }
- //$timeout(function(){pckry.reloadItems();},10);
- NVRDataModel.debug("setting dragabilly to " + $scope.isDragabillyOn);
- if ($scope.isDragabillyOn) {
- $scope.showSizeButtons = true;
- $scope.dragBorder = "dragborder";
- NVRDataModel.debug("Enabling drag for " + draggies.length + " items");
- for (i = 0; i < draggies.length; i++) {
- draggies[i].enable();
- draggies[i].bindHandles();
- }
+ };
+
+ $scope.hideMonitor = function (mid)
+ {
+ if (!$scope.isDragabillyOn) return;
+ var found = false;
+ for (var i=0; i< $scope.MontageMonitors.length; i++)
+ {
+ if ($scope.MontageMonitors[i].Monitor.selectStyle == 'dragborder-selected')
+ {
+ $scope.MontageMonitors[i].Monitor.listDisplay = 'noshow';
+ $scope.MontageMonitors[i].Monitor.selectStyle = "";
+ found = true;
+ }
- // reflow and reload as some may be hidden
- // $timeout(function(){pckry.reloadItems();$timeout(function(){pckry.layout();},300);},100);
- } else {
- $scope.dragBorder = "";
- NVRDataModel.debug("Disabling drag for " + draggies.length + " items");
- for (i = 0; i < draggies.length; i++) {
- draggies[i].disable();
- draggies[i].unbindHandles();
}
- for (i = 0; i < $scope.MontageMonitors.length; i++) {
- $scope.MontageMonitors[i].Monitor.selectStyle = "";
+ if (found)
+ {
+ pckry.once ('layoutComplete', saveUpdatedLayout);
+ $timeout (function() {pckry.shiftLayout();},300);
}
- // reflow and reload as some may be hidden
- $timeout(function () {
- $timeout(function () {
+
+ function saveUpdatedLayout()
+ {
+ $timeout(function()
+ {
var positions = pckry.getShiftPositions('data-item-id');
- //console.log ("POSITIONS MAP " + JSON.stringify(positions));
+ console.log("SAVING");
var ld = NVRDataModel.getLogin();
+
ld.packeryPositions = JSON.stringify(positions);
- //console.log ("Saving " + ld.packeryPositions);
+ //console.log ("Saving " + ld.packeryPositions);
NVRDataModel.setLogin(ld);
- }, 300);
- }, 100);
+ $ionicLoading.hide();
+ //$scope.sliderChanging = false;
+ }, 20);
+ }
+
+ };
+
+ $scope.toggleSelectItem = function(mid)
+ {
+ var ndx = getIndex(mid);
+ //console.log ("TOGGLE DETECTED AT INDEX:"+ndx+" NAME="+$scope.MontageMonitors[ndx].Monitor.Name);
+ if ($scope.MontageMonitors[ndx].Monitor.selectStyle !== "undefined" && $scope.MontageMonitors[ndx].Monitor.selectStyle == "dragborder-selected")
+ {
+ $scope.MontageMonitors[ndx].Monitor.selectStyle = "";
+ }
+ else
+ {
+ $scope.MontageMonitors[ndx].Monitor.selectStyle = "dragborder-selected";
+ }
+ //console.log ("Switched value to " + $scope.MontageMonitors[ndx].Monitor.selectStyle);
+ };
+
+ //---------------------------------------------------------------------
+ // Called when you enable/disable dragging
+ //---------------------------------------------------------------------
+
+ $scope.dragToggle = function()
+ {
+ dragToggle();
+
+ };
+
+ function dragToggle()
+ {
+ var i;
+ $scope.isDragabillyOn = !$scope.isDragabillyOn;
+
+ for ( i = 0; i < $scope.MontageMonitors.length; i++)
+ {
+ $scope.MontageMonitors[i].Monitor.isStamp = false;
+ }
+
+ $ionicSideMenuDelegate.canDragContent($scope.isDragabillyOn ? false : true);
+
+ //$timeout(function(){pckry.reloadItems();},10);
+ NVRDataModel.debug("setting dragabilly to " + $scope.isDragabillyOn);
+ if ($scope.isDragabillyOn)
+ {
+ $scope.toggleSubMenu = true;
+ $scope.dragBorder = "dragborder";
+ NVRDataModel.debug("Enabling drag for " + draggies.length + " items");
+ for (i = 0; i < draggies.length; i++)
+ {
+ draggies[i].enable();
+ draggies[i].bindHandles();
+ }
+
+ // reflow and reload as some may be hidden
+ // $timeout(function(){pckry.reloadItems();$timeout(function(){pckry.layout();},300);},100);
+ }
+ else
+ {
+ $scope.dragBorder = "";
+ NVRDataModel.debug("Disabling drag for " + draggies.length + " items");
+ for (i = 0; i < draggies.length; i++)
+ {
+ draggies[i].disable();
+ draggies[i].unbindHandles();
+ }
+ for (i = 0; i < $scope.MontageMonitors.length; i++)
+ {
+ $scope.MontageMonitors[i].Monitor.selectStyle = "";
+ }
+ // reflow and reload as some may be hidden
+ $timeout(function()
+ {
+ $timeout(function()
+ {
+ var positions = pckry.getShiftPositions('data-item-id');
+ //console.log ("POSITIONS MAP " + JSON.stringify(positions));
+ var ld = NVRDataModel.getLogin();
+ ld.packeryPositions = JSON.stringify(positions);
+ //console.log ("Saving " + ld.packeryPositions);
+ NVRDataModel.setLogin(ld);
+ }, 300);
+ }, 100);
+
+ }
}
- }
+ //---------------------------------------------------------------------
+ // main monitor modal open - if drag is not on, this is called on touch
+ //---------------------------------------------------------------------
+ $scope.openModal = function(mid, controllable, controlid, connKey, monitor)
+ {
+ openModal(mid, controllable, controlid, connKey, monitor);
+ };
- //---------------------------------------------------------------------
- // main monitor modal open - if drag is not on, this is called on touch
- //---------------------------------------------------------------------
+ function openModal(mid, controllable, controlid, connKey, monitor)
+ {
+ NVRDataModel.debug("MontageCtrl: Open Monitor Modal with monitor Id=" + mid + " and Controllable:" + controllable + " with control ID:" + controlid);
+ // $scope.isModalActive = true;
+ // Note: no need to setAwake(true) as its already awake
+ // in montage view
+
+ NVRDataModel.log("Cancelling montage timer, opening Modal");
+ // NVRDataModel.log("Starting Modal timer");
+ //console.log ("openModal:Cancelling timer");
+ $interval.cancel(intervalHandleMontage);
+ $interval.cancel(intervalHandleAlarmStatus);
+
+ $scope.monitor = monitor;
+ $scope.showPTZ = false;
+ $scope.monitorId = mid;
+ $scope.monitorName = NVRDataModel.getMonitorName(mid);
+ $scope.controlid = controlid;
+
+ //$scope.LoginData = NVRDataModel.getLogin();
+ $rootScope.modalRand = Math.floor(Math.random() * (999999 - 111111 + 1)) + 111111;
+
+ $scope.ptzMoveCommand = "";
+ $scope.ptzStopCommand = "";
+
+ $scope.zoomInCommand = "";
+ $scope.zoomOutCommand = "";
+ $scope.zoomStopCommand = "zoomStop";
+ $scope.canZoom = false;
+
+ $scope.presetOn = false;
+
+ $scope.connKey = (Math.floor((Math.random() * 999999) + 1)).toString();
+ $scope.isControllable = controllable;
+ $scope.refMonitor = monitor;
+
+ // This is a modal to show the monitor footage
+ // We need to switch to always awake if set so the feed doesn't get interrupted
+ NVRDataModel.setAwake(NVRDataModel.getKeepAwake());
+
+ // This is a modal to show the monitor footage
+ $ionicModal.fromTemplateUrl('templates/monitors-modal.html',
+ {
+ scope: $scope,
+ animation: 'slide-in-up'
- $scope.openModal = function (mid, controllable, controlid, connKey, monitor) {
- openModal(mid, controllable, controlid, connKey, monitor);
- };
+ })
+ .then(function(modal)
+ {
+ $scope.modal = modal;
- function openModal(mid, controllable, controlid, connKey, monitor) {
- NVRDataModel.debug("MontageCtrl: Open Monitor Modal with monitor Id=" + mid + " and Controllable:" + controllable + " with control ID:" + controlid);
- // $scope.isModalActive = true;
- // Note: no need to setAwake(true) as its already awake
- // in montage view
+ $ionicLoading.show(
+ {
+ template: $translate.instant('kPleaseWait'),
+ noBackdrop: true,
+ duration: zm.loadingTimeout
+ });
- NVRDataModel.log("Cancelling montage timer, opening Modal");
- // NVRDataModel.log("Starting Modal timer");
- //console.log ("openModal:Cancelling timer");
- $interval.cancel(intervalHandleMontage);
- $interval.cancel(intervalHandleAlarmStatus);
+ // we don't really need this as we have stopped the timer
+ // $scope.isModalActive = true;
- $scope.monitor = monitor;
- $scope.showPTZ = false;
- $scope.monitorId = mid;
- $scope.monitorName = NVRDataModel.getMonitorName(mid);
- $scope.controlid = controlid;
+ //$timeout (function() {pckry.shiftLayout();},zm.packeryTimer);
+ $scope.modal.show();
- //$scope.LoginData = NVRDataModel.getLogin();
- $rootScope.modalRand = Math.floor(Math.random() * (999999 - 111111 + 1)) + 111111;
+ });
+ }
- $scope.ptzMoveCommand = "";
- $scope.ptzStopCommand = "";
+ //---------------------------------------------------------------------
+ //
+ //---------------------------------------------------------------------
- $scope.zoomInCommand = "";
- $scope.zoomOutCommand = "";
- $scope.zoomStopCommand = "zoomStop";
- $scope.canZoom = false;
+ function cleanupOnClose()
+ {
+ $scope.modal.remove();
+ $timeout(function()
+ {
+ NVRDataModel.log("MontageCtrl:Stopping network pull...");
+ if (NVRDataModel.isForceNetworkStop()) NVRDataModel.stopNetwork();
+ }, 50);
- $scope.presetOn = false;
+ $rootScope.rand = Math.floor((Math.random() * 100000) + 1);
+ $scope.isModalActive = false;
- $scope.connKey = (Math.floor((Math.random() * 999999) + 1)).toString();
- $scope.isControllable = controllable;
- $scope.refMonitor = monitor;
+ NVRDataModel.log("Restarting montage timer, closing Modal...");
+ var ld = NVRDataModel.getLogin();
+ // console.log ("closeModal: Cancelling timer");
+ $interval.cancel(intervalHandleMontage);
+ $interval.cancel(intervalHandleAlarmStatus);
+ intervalHandleMontage = $interval(function()
+ {
+ loadNotifications();
+ // console.log ("Refreshing Image...");
+ }.bind(this), refreshSec * 1000);
- // This is a modal to show the monitor footage
- // We need to switch to always awake if set so the feed doesn't get interrupted
- NVRDataModel.setAwake(NVRDataModel.getKeepAwake());
+ intervalHandleAlarmStatus = $interval(function()
+ {
+ loadAlarmStatus();
+ // console.log ("Refreshing Image...");
+ }.bind(this), 5000);
+ // $timeout (function() {pckry.shiftLayout();},zm.packeryTimer);
+ }
- // This is a modal to show the monitor footage
- $ionicModal.fromTemplateUrl('templates/monitors-modal.html', {
- scope: $scope,
- animation: 'slide-in-up'
+ $scope.closeModal = function()
+ {
+ NVRDataModel.debug("MontageCtrl: Close & Destroy Monitor Modal");
+ cleanupOnClose();
+ // $scope.isModalActive = false;
+ // Note: no need to setAwake(false) as needs to be awake
+ // in montage view
- })
- .then(function (modal) {
- $scope.modal = modal;
+ };
- $ionicLoading.show({
- template: $translate.instant('kPleaseWait'),
- noBackdrop: true,
- duration: zm.loadingTimeout
- });
+ //---------------------------------------------------------------------
+ // In Android, the app runs full steam while in background mode
+ // while in iOS it gets suspended unless you ask for specific resources
+ // So while this view, we DON'T want Android to keep sending 1 second
+ // refreshes to the server for images we are not seeing
+ //---------------------------------------------------------------------
+ function onPause()
+ {
+ NVRDataModel.debug("MontageCtrl: onpause called");
+ $interval.cancel(intervalHandleMontage);
+ $interval.cancel(intervalHandleAlarmStatus);
+ // $interval.cancel(modalIntervalHandle);
- // we don't really need this as we have stopped the timer
- // $scope.isModalActive = true;
+ // FIXME: Do I need to setAwake(false) here?
+ }
- //$timeout (function() {pckry.shiftLayout();},zm.packeryTimer);
- $scope.modal.show();
+ function onResume()
+ {
- });
+ }
- }
+ $scope.openMenu = function()
+ {
+ $timeout(function()
+ {
+ $rootScope.stateofSlide = $ionicSideMenuDelegate.isOpen();
+ }, 500);
- //---------------------------------------------------------------------
- //
- //---------------------------------------------------------------------
+ $ionicSideMenuDelegate.toggleLeft();
+ };
- function cleanupOnClose() {
- $scope.modal.remove();
- $timeout(function () {
- NVRDataModel.log("MontageCtrl:Stopping network pull...");
- if (NVRDataModel.isForceNetworkStop()) NVRDataModel.stopNetwork();
- }, 50);
+ $scope.$on('$destroy', function() {
- $rootScope.rand = Math.floor((Math.random() * 100000) + 1);
- $scope.isModalActive = false;
+ });
- NVRDataModel.log("Restarting montage timer, closing Modal...");
- var ld = NVRDataModel.getLogin();
- // console.log ("closeModal: Cancelling timer");
- $interval.cancel(intervalHandleMontage);
- $interval.cancel(intervalHandleAlarmStatus);
+ $scope.$on('$ionicView.loaded', function()
+ {
+ // console.log("**VIEW ** Montage Ctrl Loaded");
+ });
- intervalHandleMontage = $interval(function () {
- loadNotifications();
- // console.log ("Refreshing Image...");
- }.bind(this), refreshSec * 1000);
+ $scope.$on('$ionicView.leave', function()
+ {
+ // console.log("**VIEW ** Montage Ctrl Left, force removing modal");
+ if ($scope.modal) $scope.modal.remove();
+ });
- intervalHandleAlarmStatus = $interval(function () {
- loadAlarmStatus();
- // console.log ("Refreshing Image...");
- }.bind(this), 5000);
+ function orientationChanged()
+ {
+
+ }
- // $timeout (function() {pckry.shiftLayout();},zm.packeryTimer);
+ // remove a saved montage profile
+ $scope.deleteMontageProfile = function()
+ {
+ var posArray;
- }
+ try
+ {
+ posArray = NVRDataModel.getLogin().packeryPositionsArray;
+ //console.log ("PA="+JSON.stringify(posArray));
- $scope.closeModal = function () {
- NVRDataModel.debug("MontageCtrl: Close & Destroy Monitor Modal");
- cleanupOnClose();
- // $scope.isModalActive = false;
- // Note: no need to setAwake(false) as needs to be awake
- // in montage view
+ }
+ 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'),
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
+ });
+ 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'),
+ subTitle:$translate.instant('kSelectDelete'),
+ scope: $scope,
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
- //---------------------------------------------------------------------
- // In Android, the app runs full steam while in background mode
- // while in iOS it gets suspended unless you ask for specific resources
- // So while this view, we DON'T want Android to keep sending 1 second
- // refreshes to the server for images we are not seeing
- //---------------------------------------------------------------------
+ }).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);
- function onPause() {
- NVRDataModel.debug("MontageCtrl: onpause called");
- $interval.cancel(intervalHandleMontage);
- $interval.cancel(intervalHandleAlarmStatus);
- // $interval.cancel(modalIntervalHandle);
+ });
- // FIXME: Do I need to setAwake(false) here?
- }
+ };
+ // switch to another montage profile
+ $scope.switchMontageProfile = function()
+ {
+ var posArray;
- function onResume() {
+ 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.openMenu = function () {
- $timeout(function () {
- $rootScope.stateofSlide = $ionicSideMenuDelegate.isOpen();
- }, 500);
+ $scope.listdata = [];
+ for (var key in posArray)
+ {
+ if (posArray.hasOwnProperty(key))
+ {
+ $scope.listdata.push(key);
+ }
+ }
- $ionicSideMenuDelegate.toggleLeft();
- };
+ if (!$scope.listdata.length)
+ {
- $scope.$on('$destroy', function () {
+ $rootScope.zmPopup = $ionicPopup.alert(
+ {
+ title: $translate.instant('kError'),
+ template: $translate.instant('kMontageNoSavedProfiles'),
- });
+ });
+ return;
+ }
+ $scope.data = {
+ 'selectedVal': ''
+ };
- $scope.$on('$ionicView.loaded', function () {
- // console.log("**VIEW ** Montage Ctrl Loaded");
- });
+ $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'),
+ subTitle:$translate.instant('kSelectSwitch'),
+ scope: $scope,
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
+
+ }).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();
+ }
+ });
- $scope.$on('$ionicView.leave', function () {
- // console.log("**VIEW ** Montage Ctrl Left, force removing modal");
- if ($scope.modal) $scope.modal.remove();
- });
+ };
+ // 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,
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
+ }).then(function(res)
+ {
+ console.log(res);
+ if (res) // ok
+ {
- function orientationChanged() {
- /* NVRDataModel.debug("Detected orientation change, redoing packery resize");
- $timeout(function () {
- if (pckry) pckry.onresize();
- }, 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);
- }
+ var ld = NVRDataModel.getLogin();
- $scope.toggleSizeButtons = function () {
+ 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.showSizeButtons = !$scope.showSizeButtons;
+ }
+ });
- NVRDataModel.debug("toggling size buttons:" + $scope.showSizeButtons);
- if ($scope.showSizeButtons) $ionicScrollDelegate.$getByHandle("montage-delegate").scrollTop();
- };
+ };
- // minimal has to be beforeEnter or header won't hide
- $scope.$on('$ionicView.beforeEnter', function () {
- $scope.minimal = $stateParams.minimal;
- //console.log ("**************** MINIMAL ENTER " + $scope.minimal);
- $scope.zmMarginTop = $scope.minimal ? 0 : 15;
+ $scope.toggleSubMenuFunction = function()
+ {
+ $scope.toggleSubMenu = !$scope.toggleSubMenu;
+ NVRDataModel.debug("toggling size buttons:" + $scope.toggleSubMenu);
+ if ($scope.toggleSubMenu) $ionicScrollDelegate.$getByHandle("montage-delegate").scrollTop();
+ var ld = NVRDataModel.getLogin();
+ ld.showMontageSubMenu = $scope.toggleSubMenu;
+ NVRDataModel.setLogin(ld);
+ };
+ // minimal has to be beforeEnter or header won't hide
+ $scope.$on('$ionicView.beforeEnter', function()
+ {
+ $scope.minimal = $stateParams.minimal;
+ //console.log ("**************** MINIMAL ENTER " + $scope.minimal);
+ $scope.zmMarginTop = $scope.minimal ? 0 : 15;
- });
+ });
- $scope.toggleTimeType = function()
- {
- if (NVRDataModel.isTzSupported())
+ $scope.toggleTimeType = function()
{
- if ($scope.iconTimeNow == 'server')
+ if (NVRDataModel.isTzSupported())
{
+ if ($scope.iconTimeNow == 'server')
+ {
$scope.iconTimeNow = 'local';
$scope.timeNow = $translate.instant('kPleaseWait');
- }
- else
- {
+ }
+ else
+ {
$scope.iconTimeNow = 'server';
$scope.timeNow = $translate.instant('kPleaseWait');
+ }
}
- }
- else
- NVRDataModel.debug ("timezone API not supported, can't display");
- };
-
-
- $scope.$on('$ionicView.afterEnter', function () {
- NVRDataModel.debug("Setting image mode to snapshot, will change to image when packery is all done");
- $scope.areImagesLoading = true;
- $scope.isDragabillyOn = false;
-
-
- if (NVRDataModel.isTzSupported())
- $scope.iconTimeNow = 'server';
- else
- $scope.iconTimeNow = 'local';
-
- if ($scope.iconTimeNow == 'local')
- $scope.timeNow = moment().format(NVRDataModel.getTimeFormatSec());
- else
- $scope.timeNow = moment().tz(NVRDataModel.getTimeZoneNow()).format(NVRDataModel.getTimeFormatSec());
-
-
- $scope.gridScale = "grid-item-50";
- $scope.LoginData = NVRDataModel.getLogin();
- //FIXME
-
- if (NVRDataModel.getBandwidth() == 'lowbw') {
- NVRDataModel.debug("Enabling low bandwidth parameters");
- $scope.LoginData.montageQuality = zm.montageQualityLowBW;
- $scope.LoginData.singleImageQuality = zm.eventSingleImageQualityLowBW;
- $scope.LoginData.montageHistoryQuality = zm.montageQualityLowBW;
-
+ else
+ NVRDataModel.debug("timezone API not supported, can't display");
+ };
- }
+ $scope.$on('$ionicView.afterEnter', function()
+ {
+ NVRDataModel.debug("Setting image mode to snapshot, will change to image when packery is all done");
+ $scope.areImagesLoading = true;
+ $scope.isDragabillyOn = false;
+ if (NVRDataModel.isTzSupported())
+ $scope.iconTimeNow = 'server';
+ else
+ $scope.iconTimeNow = 'local';
- $scope.monLimit = $scope.LoginData.maxMontage;
- $scope.showSizeButtons = false;
+ if ($scope.iconTimeNow == 'local')
+ $scope.timeNow = moment().format(NVRDataModel.getTimeFormatSec());
+ else
+ $scope.timeNow = moment().tz(NVRDataModel.getTimeZoneNow()).format(NVRDataModel.getTimeFormatSec());
+ $scope.gridScale = "grid-item-50";
+ $scope.LoginData = NVRDataModel.getLogin();
+ //FIXME
- $scope.monitors = message;
- $scope.MontageMonitors = angular.copy(message);
- $scope.sliderChanging = false;
- loginData = NVRDataModel.getLogin();
+ if (NVRDataModel.getBandwidth() == 'lowbw')
+ {
+ NVRDataModel.debug("Enabling low bandwidth parameters");
+ $scope.LoginData.montageQuality = zm.montageQualityLowBW;
+ $scope.LoginData.singleImageQuality = zm.eventSingleImageQualityLowBW;
+ $scope.LoginData.montageHistoryQuality = zm.montageQualityLowBW;
- $scope.isRefresh = $stateParams.isRefresh;
- sizeInProgress = false;
- $scope.imageStyle = true;
- intervalHandleMontage = "";
- $scope.isModalActive = false;
- $scope.isReorder = false;
+ }
- $ionicSideMenuDelegate.canDragContent($scope.minimal ? true : true);
+ $scope.monLimit = $scope.LoginData.maxMontage;
+ $scope.toggleSubMenu = NVRDataModel.getLogin().showMontageSubMenu;
+ $scope.monitors = message;
+ $scope.MontageMonitors = angular.copy(message);
+ $scope.sliderChanging = false;
+ loginData = NVRDataModel.getLogin();
- $scope.areImagesLoading = true;
- var ld = NVRDataModel.getLogin();
+ $scope.isRefresh = $stateParams.isRefresh;
+ sizeInProgress = false;
+ $scope.imageStyle = true;
+ intervalHandleMontage = "";
+ $scope.isModalActive = false;
+ $scope.isReorder = false;
- refreshSec = (NVRDataModel.getBandwidth()=='lowbw') ? ld.refreshSecLowBW : ld.refreshSec;
+ $ionicSideMenuDelegate.canDragContent($scope.minimal ? true : true);
- NVRDataModel.debug("bandwidth: " + NVRDataModel.getBandwidth() + " montage refresh set to: " + refreshSec);
+ $scope.areImagesLoading = true;
+ var ld = NVRDataModel.getLogin();
- //console.log("Setting Awake to " + NVRDataModel.getKeepAwake());
- NVRDataModel.setAwake(NVRDataModel.getKeepAwake());
+ refreshSec = (NVRDataModel.getBandwidth() == 'lowbw') ? ld.refreshSecLowBW : ld.refreshSec;
- $interval.cancel(intervalHandleMontage);
- $interval.cancel(intervalHandleAlarmStatus);
+ NVRDataModel.debug("bandwidth: " + NVRDataModel.getBandwidth() + " montage refresh set to: " + refreshSec);
- intervalHandleMontage = $interval(function () {
- loadNotifications();
- // console.log ("Refreshing Image...");
- }.bind(this), refreshSec * 1000);
+ //console.log("Setting Awake to " + NVRDataModel.getKeepAwake());
+ NVRDataModel.setAwake(NVRDataModel.getKeepAwake());
- intervalHandleAlarmStatus = $interval(function () {
- loadAlarmStatus();
- // console.log ("Refreshing Image...");
- }.bind(this), 5000);
+ $interval.cancel(intervalHandleMontage);
+ $interval.cancel(intervalHandleAlarmStatus);
+ intervalHandleMontage = $interval(function()
+ {
+ loadNotifications();
+ // console.log ("Refreshing Image...");
+ }.bind(this), refreshSec * 1000);
- loadNotifications();
+ intervalHandleAlarmStatus = $interval(function()
+ {
+ loadAlarmStatus();
+ // console.log ("Refreshing Image...");
+ }.bind(this), 5000);
- if ($scope.MontageMonitors.length == 0) {
- $rootScope.zmPopup = $ionicPopup.alert({
- title: $translate.instant('kNoMonitors'),
- template: $translate.instant('kCheckCredentials')
- });
- $ionicHistory.nextViewOptions({
- disableBack: true
- });
- $state.go("login", {
- "wizard": false
- });
- return;
- }
+ loadNotifications();
- ld = NVRDataModel.getLogin();
+ if ($scope.MontageMonitors.length == 0)
+ {
+ $rootScope.zmPopup = $ionicPopup.alert(
+ {
+ title: $translate.instant('kNoMonitors'),
+ template: $translate.instant('kCheckCredentials'),
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
+ });
+ $ionicHistory.nextViewOptions(
+ {
+ disableBack: true
+ });
+ $state.go("login",
+ {
+ "wizard": false
+ });
+ return;
+ }
- $rootScope.authSession = "undefined";
- $ionicLoading.show({
- template: $translate.instant('kNegotiatingStreamAuth'),
- animation: 'fade-in',
- showBackdrop: true,
- duration: zm.loadingTimeout,
- maxWidth: 300,
- showDelay: 0
- });
+ ld = NVRDataModel.getLogin();
+ $rootScope.authSession = "undefined";
+ $ionicLoading.show(
+ {
+ template: $translate.instant('kNegotiatingStreamAuth'),
+ animation: 'fade-in',
+ showBackdrop: true,
+ duration: zm.loadingTimeout,
+ maxWidth: 300,
+ showDelay: 0
+ });
- NVRDataModel.log("Inside Montage Ctrl:We found " + $scope.monitors.length + " monitors");
+ NVRDataModel.log("Inside Montage Ctrl:We found " + $scope.monitors.length + " monitors");
- // set them all at 50% for packery
- for (var i = 0; i < $scope.MontageMonitors.length; i++) {
- $scope.MontageMonitors[i].Monitor.gridScale = "50";
- $scope.MontageMonitors[i].Monitor.selectStyle = "";
- $scope.MontageMonitors[i].Monitor.alarmState = 'color:rgba(0,0,0,0);';
+ // set them all at 50% for packery
+ for (var i = 0; i < $scope.MontageMonitors.length; i++)
+ {
+ $scope.MontageMonitors[i].Monitor.gridScale = "50";
+ $scope.MontageMonitors[i].Monitor.selectStyle = "";
+ $scope.MontageMonitors[i].Monitor.alarmState = 'color:rgba(0,0,0,0);';
+ $scope.MontageMonitors[i].Monitor.isStamp = false;
- }
+ }
- $rootScope.validMonitorId = $scope.monitors[0].Monitor.Id;
- NVRDataModel.getAuthKey($rootScope.validMonitorId, (Math.floor((Math.random() * 999999) + 1)).toString())
- .then(function (success) {
- $ionicLoading.hide();
- //console.log(success);
- $rootScope.authSession = success;
- NVRDataModel.log("Stream authentication construction: " +
- $rootScope.authSession);
- $timeout(function () {
+ $rootScope.validMonitorId = $scope.monitors[0].Monitor.Id;
+ NVRDataModel.getAuthKey($rootScope.validMonitorId, (Math.floor((Math.random() * 999999) + 1)).toString())
+ .then(function(success)
+ {
+ $ionicLoading.hide();
+ //console.log(success);
+ $rootScope.authSession = success;
+ NVRDataModel.log("Stream authentication construction: " +
+ $rootScope.authSession);
+ $timeout(function()
+ {
initPackery();
}, zm.packeryTimer);
- },
- function (error) {
+ },
+ function(error)
+ {
- $ionicLoading.hide();
- NVRDataModel.debug("MontageCtrl: Error in authkey retrieval " + error);
- //$rootScope.authSession="";
- NVRDataModel.log("MontageCtrl: Error returned Stream authentication construction. Retaining old value of: " + $rootScope.authSession);
- $timeout(function () {
+ $ionicLoading.hide();
+ NVRDataModel.debug("MontageCtrl: Error in authkey retrieval " + error);
+ //$rootScope.authSession="";
+ NVRDataModel.log("MontageCtrl: Error returned Stream authentication construction. Retaining old value of: " + $rootScope.authSession);
+ $timeout(function()
+ {
initPackery();
}, zm.packeryTimer);
- });
-
- //console.log("**VIEW ** Montage Ctrl AFTER ENTER");
- window.addEventListener("resize", orientationChanged, false);
-
- document.addEventListener("pause", onPause, false);
- document.addEventListener("resume", onResume, false);
-
-
-
-
- });
-
- $scope.$on('$ionicView.beforeLeave', function () {
- // console.log("**VIEW ** Montage Ctrl Left, force removing modal");
-
- //console.log ("beforeLeave:Cancelling timer");
- $interval.cancel(intervalHandleMontage);
- $interval.cancel(intervalHandleAlarmStatus);
- pckry.destroy();
- window.removeEventListener("resize", orientationChanged, false);
+ });
+ //console.log("**VIEW ** Montage Ctrl AFTER ENTER");
+ window.addEventListener("resize", orientationChanged, false);
- // make sure this is applied in scope digest to stop network pull
- // thats why we are doing it beforeLeave
+ document.addEventListener("pause", onPause, false);
+ document.addEventListener("resume", onResume, false);
- if (NVRDataModel.isForceNetworkStop()) {
- NVRDataModel.log("MontageCtrl:Stopping network pull...");
- NVRDataModel.stopNetwork();
+ });
- }
+ $scope.$on('$ionicView.beforeLeave', function()
+ {
+ // console.log("**VIEW ** Montage Ctrl Left, force removing modal");
- });
+ //console.log ("beforeLeave:Cancelling timer");
+ $interval.cancel(intervalHandleMontage);
+ $interval.cancel(intervalHandleAlarmStatus);
+ pckry.destroy();
+ window.removeEventListener("resize", orientationChanged, false);
+ // make sure this is applied in scope digest to stop network pull
+ // thats why we are doing it beforeLeave
+ if (NVRDataModel.isForceNetworkStop())
+ {
+ NVRDataModel.log("MontageCtrl:Stopping network pull...");
+ NVRDataModel.stopNetwork();
+ }
- $scope.$on('$ionicView.unloaded', function () {
+ });
- });
+ $scope.$on('$ionicView.unloaded', function() {
+ });
- $scope.resetSizes = function () {
- var somethingReset = false;
- for (var i = 0; i < $scope.MontageMonitors.length; i++) {
- if ($scope.isDragabillyOn) {
- if ($scope.MontageMonitors[i].Monitor.selectStyle == "dragborder-selected") {
+ $scope.resetSizes = function()
+ {
+ var somethingReset = false;
+ for (var i = 0; i < $scope.MontageMonitors.length; i++)
+ {
+ if ($scope.isDragabillyOn)
+ {
+ if ($scope.MontageMonitors[i].Monitor.selectStyle == "dragborder-selected")
+ {
+ $scope.MontageMonitors[i].Monitor.gridScale = "50";
+ somethingReset = true;
+ }
+ }
+ else
+ {
$scope.MontageMonitors[i].Monitor.gridScale = "50";
- somethingReset = true;
+ // somethingReset = true;
}
- } else {
- $scope.MontageMonitors[i].Monitor.gridScale = "50";
- // somethingReset = true;
}
- }
- if (!somethingReset && $scope.isDragabillyOn) // nothing was selected
- {
- for (i = 0; i < $scope.MontageMonitors.length; i++) {
- $scope.MontageMonitors[i].Monitor.gridScale = "50";
+ if (!somethingReset && $scope.isDragabillyOn) // nothing was selected
+ {
+ for (i = 0; i < $scope.MontageMonitors.length; i++)
+ {
+ $scope.MontageMonitors[i].Monitor.gridScale = "50";
+ }
}
- }
- $timeout(function () {
- pckry.reloadItems();
+ $timeout(function()
+ {
+ pckry.reloadItems();
- pckry.once('layoutComplete', function () {
- //console.log ("Layout complete");
- var positions = pckry.getShiftPositions('data-item-id');
- //console.log ("POSITIONS MAP " + JSON.stringify(positions));
- var ld = NVRDataModel.getLogin();
-
- ld.packeryPositions = JSON.stringify(positions);
- //console.log ("Saving " + ld.packeryPositions);
- NVRDataModel.setLogin(ld);
- // $scope.slider.monsize = 2;
- });
- //layout(pckry);
-
+ pckry.once('layoutComplete', function()
+ {
+ //console.log ("Layout complete");
+ var positions = pckry.getShiftPositions('data-item-id');
+ //console.log ("POSITIONS MAP " + JSON.stringify(positions));
+ var ld = NVRDataModel.getLogin();
+
+ 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;
+ });
pckry.layout();
-
+ }, 20);
- }, 20);
+ };
- };
+ function layout(pckry)
+ {
+ pckry.shiftLayout();
+ }
+ //---------------------------------------------------------
+ // slider is tied to the view slider for montage
+ //Remember not to use a variable. I'm using an object
+ // so it's passed as a reference - otherwise it makes
+ // a copy and the value never changes
+ //---------------------------------------------------------
- function layout(pckry) {
- pckry.shiftLayout();
- }
+ $scope.sliderChanged = function(dirn)
+ {
- //---------------------------------------------------------
- // slider is tied to the view slider for montage
- //Remember not to use a variable. I'm using an object
- // so it's passed as a reference - otherwise it makes
- // a copy and the value never changes
- //---------------------------------------------------------
+ if ($scope.sliderChanging)
+ {
+ console.log("too fast my friend");
+ //$scope.slider.monsize = oldSliderVal;
+ return;
+ }
- $scope.sliderChanged = function (dirn) {
+ $scope.sliderChanging = true;
- if ($scope.sliderChanging) {
- console.log ("too fast my friend");
- //$scope.slider.monsize = oldSliderVal;
- return;
- }
+ $ionicLoading.show(
+ {
+ template: $translate.instant('kPleaseWait'),
+ noBackdrop: true,
+ duration: 5000
+ });
-
-
- $scope.sliderChanging = true;
-
- $ionicLoading.show({
- template: $translate.instant('kPleaseWait'),
- noBackdrop: true,
- duration: 5000
- });
+ var somethingReset = false;
- var somethingReset = false;
-
- var oldScales = {};
- pckry.getItemElements().forEach(function (elem) {
+ var oldScales = {};
+ pckry.getItemElements().forEach(function(elem)
+ {
var id = elem.getAttribute("data-item-id");
var sz = elem.getAttribute("data-item-size");
oldScales[id] = sz;
- console.log ("REMEMBERING "+id+":"+sz);
-
+ console.log("REMEMBERING " + id + ":" + sz);
+
});
-
- // this only changes items that are selected
- for (var i = 0; i < $scope.MontageMonitors.length; i++) {
+ // this only changes items that are selected
+ for (var i = 0; i < $scope.MontageMonitors.length; i++)
+ {
- var curVal = parseInt($scope.MontageMonitors[i].Monitor.gridScale);
- curVal = curVal + (10 * dirn);
- if (curVal < 10) curVal = 10;
- if (curVal > 100) curVal = 100;
- //console.log ("For Index: " + i + " From: " + $scope.MontageMonitors[i].Monitor.gridScale + " To: " + curVal);
+ var curVal = parseInt($scope.MontageMonitors[i].Monitor.gridScale);
+ curVal = curVal + (10 * dirn);
+ if (curVal < 10) curVal = 10;
+ if (curVal > 100) curVal = 100;
+ //console.log ("For Index: " + i + " From: " + $scope.MontageMonitors[i].Monitor.gridScale + " To: " + curVal);
- if ($scope.isDragabillyOn) {
- // only do this for selected monitors
- if ($scope.MontageMonitors[i].Monitor.selectStyle == "dragborder-selected") {
+ if ($scope.isDragabillyOn)
+ {
+ // only do this for selected monitors
+ if ($scope.MontageMonitors[i].Monitor.selectStyle == "dragborder-selected")
+ {
+ $scope.MontageMonitors[i].Monitor.gridScale = curVal;
+ somethingReset = true;
+ }
+ }
+ else
+ {
$scope.MontageMonitors[i].Monitor.gridScale = curVal;
- somethingReset = true;
+ //somethingReset = true;
+
}
- } else {
- $scope.MontageMonitors[i].Monitor.gridScale = curVal;
- //somethingReset = true;
}
- }
-
- // this changes all items if none were selected
- if (!somethingReset && $scope.isDragabillyOn) // nothing was selected
- {
- for (i = 0; i < $scope.MontageMonitors.length; i++) {
- var cv = parseInt($scope.MontageMonitors[i].Monitor.gridScale);
- cv = cv + (10 * dirn);
- if (cv < 10) cv = 10;
- if (cv > 100) cv = 100;
- $scope.MontageMonitors[i].Monitor.gridScale = cv;
- }
- }
-
- // reload sizes from DOM and trigger a layout
-
- $timeout (function() {
- console.log ("Calling re-layout");
- //pckry.reloadItems();
-
- if (dirn == 1) //expand
- {
- pckry.getItemElements().forEach(function (elem) {
- var id = elem.getAttribute("data-item-id");
- var sz = elem.getAttribute("data-item-size");
- console.log ("NOW IT IS-> "+id+":"+sz);
- if (oldScales[id] != sz)
+ // this changes all items if none were selected
+ if (!somethingReset && $scope.isDragabillyOn) // nothing was selected
+ {
+ for (i = 0; i < $scope.MontageMonitors.length; i++)
{
- console.log ("Calling FIT on " + id + " size:"+oldScales[id]+"->"+sz);
- pckry.once('fitComplete', resizeComplete);
- pckry.fit(elem);
-
+ var cv = parseInt($scope.MontageMonitors[i].Monitor.gridScale);
+ cv = cv + (10 * dirn);
+ if (cv < 10) cv = 10;
+ if (cv > 100) cv = 100;
+ $scope.MontageMonitors[i].Monitor.gridScale = cv;
}
- });
- }
- else //shrink
- {
- console.log ("Calling shift");
- pckry.once('layoutComplete', resizeComplete);
- pckry.shiftLayout();
-
-
- }
-
- },20);
-
-
-
+ }
+ // reload sizes from DOM and trigger a layout
- /* if (!somethingReset) {
- //console.log (">>>SOMETHING NOT RESET");
- $timeout(function () {
- pckry.layout();
- }, zm.packeryTimer);
- } else {
-
- //console.log (">>>SOMETHING RESET");
- $timeout(function () {
- layout(pckry);
- }, zm.packeryTimer);
- }*/
- function resizeComplete()
- {
- //console.log ("HERE");
- $timeout(function () {
- var positions = pckry.getShiftPositions('data-item-id');
- console.log ("SAVING");
- var ld = NVRDataModel.getLogin();
-
- ld.packeryPositions = JSON.stringify(positions);
- //console.log ("Saving " + ld.packeryPositions);
- NVRDataModel.setLogin(ld);
- $ionicLoading.hide();
- $scope.sliderChanging = false;
- }, 20);
-
- }
+ $timeout(function()
+ {
+ console.log("Calling re-layout");
+ //pckry.reloadItems();
+ if (dirn == 1) //expand
+ {
+ pckry.getItemElements().forEach(function(elem)
+ {
+ var id = elem.getAttribute("data-item-id");
+ var sz = elem.getAttribute("data-item-size");
+ console.log("NOW IT IS-> " + id + ":" + sz);
+ if (oldScales[id] != sz)
+ {
+ console.log("Calling FIT on " + id + " size:" + oldScales[id] + "->" + sz);
+ pckry.once('fitComplete', resizeComplete);
+ pckry.fit(elem);
+ }
+ });
+ }
+ else //shrink
+ {
+ console.log("Calling shift");
+ pckry.once('layoutComplete', resizeComplete);
+ pckry.shiftLayout();
- };
+ }
+ }, 20);
+ /* if (!somethingReset) {
+ //console.log (">>>SOMETHING NOT RESET");
+ $timeout(function () {
+ pckry.layout();
+ }, zm.packeryTimer);
+ } else {
+
+ //console.log (">>>SOMETHING RESET");
+ $timeout(function () {
+ layout(pckry);
+ }, zm.packeryTimer);
+ }*/
+ function resizeComplete()
+ {
+ //console.log ("HERE");
+ $timeout(function()
+ {
+ var positions = pckry.getShiftPositions('data-item-id');
+ console.log("SAVING");
+ var ld = NVRDataModel.getLogin();
- $scope.$on('$ionicView.afterEnter', function () {
- // This rand is really used to reload the monitor image in img-src so it is not cached
- // I am making sure the image in montage view is always fresh
- // I don't think I am using this anymore FIXME: check and delete if needed
- // $rootScope.rand = Math.floor((Math.random() * 100000) + 1);
- });
+ ld.packeryPositions = JSON.stringify(positions);
+ //console.log ("Saving " + ld.packeryPositions);
+ NVRDataModel.setLogin(ld);
+ $ionicLoading.hide();
+ $scope.sliderChanging = false;
+ }, 20);
- $scope.reloadView = function () {
- $rootScope.rand = Math.floor((Math.random() * 100000) + 1);
- NVRDataModel.log("User action: image reload " + $rootScope.rand);
- };
+ }
- $scope.doRefresh = function () {
+ };
+ $scope.$on('$ionicView.afterEnter', function()
+ {
+ // This rand is really used to reload the monitor image in img-src so it is not cached
+ // I am making sure the image in montage view is always fresh
+ // I don't think I am using this anymore FIXME: check and delete if needed
+ // $rootScope.rand = Math.floor((Math.random() * 100000) + 1);
+ });
- // console.log("***Pull to Refresh, recomputing Rand");
- NVRDataModel.log("Reloading view for montage view, recomputing rand");
- $rootScope.rand = Math.floor((Math.random() * 100000) + 1);
- $scope.monitors = [];
- imageLoadingDataShare.set(0);
+ $scope.reloadView = function()
+ {
+ $rootScope.rand = Math.floor((Math.random() * 100000) + 1);
+ NVRDataModel.log("User action: image reload " + $rootScope.rand);
+ };
- var refresh = NVRDataModel.getMonitors(1);
+ $scope.doRefresh = function()
+ {
- refresh.then(function (data) {
- $scope.monitors = data;
- $scope.$broadcast('scroll.refreshComplete');
- });
- };
+ // console.log("***Pull to Refresh, recomputing Rand");
+ NVRDataModel.log("Reloading view for montage view, recomputing rand");
+ $rootScope.rand = Math.floor((Math.random() * 100000) + 1);
+ $scope.monitors = [];
+ imageLoadingDataShare.set(0);
+ var refresh = NVRDataModel.getMonitors(1);
+
+ refresh.then(function(data)
+ {
+ $scope.monitors = data;
+ $scope.$broadcast('scroll.refreshComplete');
+ });
+ };
-}]);
+ }]);
diff --git a/www/js/MontageHistoryCtrl.js b/www/js/MontageHistoryCtrl.js
index 7b1ca2c1..4c7851d5 100644
--- a/www/js/MontageHistoryCtrl.js
+++ b/www/js/MontageHistoryCtrl.js
@@ -3,16 +3,18 @@
/* jslint browser: true*/
/* global cordova,StatusBar,angular,console,ionic,Masonry,moment,Packery, Draggabilly, imagesLoaded, Chart */
// FIXME: This is a copy of montageCtrl - needs a lot of code cleanup
-angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$scope', '$rootScope', 'NVRDataModel', 'message', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$ionicPopup', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$ionicPlatform', 'zm', '$ionicPopover', '$controller', 'imageLoadingDataShare', '$window', '$translate', 'qHttp', '$q', function ($scope, $rootScope, NVRDataModel, message, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $ionicPopup, $stateParams, $ionicHistory, $ionicScrollDelegate, $ionicPlatform, zm, $ionicPopover, $controller, imageLoadingDataShare, $window, $translate, qHttp, $q) {
+angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$scope', '$rootScope', 'NVRDataModel', 'message', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$ionicPopup', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$ionicPlatform', 'zm', '$ionicPopover', '$controller', 'imageLoadingDataShare', '$window', '$translate', 'qHttp', '$q', function($scope, $rootScope, NVRDataModel, message, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $ionicPopup, $stateParams, $ionicHistory, $ionicScrollDelegate, $ionicPlatform, zm, $ionicPopover, $controller, imageLoadingDataShare, $window, $translate, qHttp, $q)
+{
//--------------------------------------------------------------------------------------
// Handles bandwidth change, if required
//
//--------------------------------------------------------------------------------------
- $rootScope.$on("bandwidth-change", function (e, data) {
+ $rootScope.$on("bandwidth-change", function(e, data)
+ {
// nothing to do for now
// eventUrl will use lower BW in next query cycle
});
-
+
$scope.getLocalTZ = function()
{
return moment.tz.guess();
@@ -20,70 +22,83 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
//--------------------------------------
// formats events dates in a nice way
//---------------------------------------
- $scope.prettifyDateTimeFirst = function (str) {
- if (NVRDataModel.getLogin().useLocalTimeZone)
- return moment.tz(str, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format(NVRDataModel.getTimeFormat() + '/MMM Do');
+ $scope.prettifyDateTimeFirst = function(str)
+ {
+ if (NVRDataModel.getLogin().useLocalTimeZone)
+ return moment.tz(str, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format(NVRDataModel.getTimeFormat() + '/MMM Do');
else
return moment(str).format(NVRDataModel.getTimeFormat() + '/MMM Do');
};
- $scope.prettifyDate = function (str) {
+ $scope.prettifyDate = function(str)
+ {
return moment(str).format('MMM Do, YYYY ' + NVRDataModel.getTimeFormat());
};
- function prettifyDate(str) {
- if (NVRDataModel.getLogin().useLocalTimeZone)
+ function prettifyDate(str)
+ {
+ if (NVRDataModel.getLogin().useLocalTimeZone)
return moment.tz(str, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format('MMM Do');
else
- return moment(str).format('MMM Do');
+ return moment(str).format('MMM Do');
}
- $scope.prettifyTime = function (str) {
+ $scope.prettifyTime = function(str)
+ {
if (NVRDataModel.getLogin().useLocalTimeZone)
return moment.tz(str, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format('h:mm a');
else
return moment(str).format('h:mm a');
};
- $scope.prettify = function (str) {
+ $scope.prettify = function(str)
+ {
if (NVRDataModel.getLogin().useLocalTimeZone)
return moment.tz(str, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format(NVRDataModel.getTimeFormat() + ' on MMMM Do YYYY');
else
return moment(str).format(NVRDataModel.getTimeFormat() + ' on MMMM Do YYYY');
};
- $scope.humanizeTime = function (str) {
- // if (NVRDataModel.getLogin().useLocalTimeZone)
- return moment.tz(str, NVRDataModel.getTimeZoneNow()).fromNow();
- // else
- // return moment(str).fromNow();
-
+ $scope.humanizeTime = function(str)
+ {
+ // if (NVRDataModel.getLogin().useLocalTimeZone)
+ return moment.tz(str, NVRDataModel.getTimeZoneNow()).fromNow();
+ // else
+ // return moment(str).fromNow();
+
};
// if you change date in footer, change hrs
- $scope.dateChanged = function () {
+ $scope.dateChanged = function()
+ {
$scope.datetimeValueFrom.hrs = Math.round(moment.duration(moment().diff(moment($scope.datetimeValueFrom.value))).asHours());
};
// if you change hrs in footer, change date
- $scope.hrsChanged = function () {
+ $scope.hrsChanged = function()
+ {
$scope.datetimeValueFrom.value = moment().subtract($scope.datetimeValueFrom.hrs, 'hours').toDate();
timefrom.toDate();
};
- function orientationChanged() {
- // NVRDataModel.debug("Detected orientation change, redoing packery resize");
- /* $timeout(function () {
- pckry.onresize();
- });*/
+ function orientationChanged()
+ {
+ // NVRDataModel.debug("Detected orientation change, redoing packery resize");
+ /* $timeout(function () {
+ pckry.onresize();
+ });*/
}
//--------------------------------------
// pause/unpause nph-zms
//---------------------------------------
- $scope.togglePause = function (mid) {
+ $scope.togglePause = function(mid)
+ {
//console.log ("TOGGLE PAUSE " + mid);
var m = -1;
- for (var i = 0; i < $scope.MontageMonitors.length; i++) {
- if ($scope.MontageMonitors[i].Monitor.Id == mid) {
+ for (var i = 0; i < $scope.MontageMonitors.length; i++)
+ {
+ if ($scope.MontageMonitors[i].Monitor.Id == mid)
+ {
m = i;
break;
}
}
- if (m != -1) {
+ if (m != -1)
+ {
$scope.MontageMonitors[m].Monitor.isPaused = !$scope.MontageMonitors[m].Monitor.isPaused;
var cmd = 1;
@@ -92,66 +107,79 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
}
};
- function sendCmd(mid, cmd, extra) {
-
-
+ function sendCmd(mid, cmd, extra)
+ {
var m = -1;
- for (var i = 0; i < $scope.MontageMonitors.length; i++) {
- if ($scope.MontageMonitors[i].Monitor.Id == mid) {
+ for (var i = 0; i < $scope.MontageMonitors.length; i++)
+ {
+ if ($scope.MontageMonitors[i].Monitor.Id == mid)
+ {
m = i;
break;
}
}
- if (m != -1) {
+ if (m != -1)
+ {
NVRDataModel.debug("Sending CMD:" + cmd + " for monitor " + $scope.MontageMonitors[m].Monitor.Name);
return controlEventStream(cmd, "", $scope.MontageMonitors[m].Monitor.connKey, -1, extra);
}
}
- $scope.seek = function (mid, p) {
+ $scope.seek = function(mid, p)
+ {
NVRDataModel.debug("Slider called with mid=" + mid + " progress=" + p);
var m = -1;
- for (var i = 0; i < $scope.MontageMonitors.length; i++) {
- if ($scope.MontageMonitors[i].Monitor.Id == mid) {
+ for (var i = 0; i < $scope.MontageMonitors.length; i++)
+ {
+ if ($scope.MontageMonitors[i].Monitor.Id == mid)
+ {
m = i;
break;
}
}
- if (m != -1) {
+ if (m != -1)
+ {
$scope.MontageMonitors[i].Monitor.seek = true;
}
-
sendCmd(mid, '14', "&offset=" + p)
- .then(function (success) {
+ .then(function(success)
+ {
//console.log ("Removing seek status from " + $scope.MontageMonitors[i].Monitor.Name);
$scope.MontageMonitors[i].Monitor.seek = false;
},
- function (err) {
+ function(err)
+ {
//console.log ("Removing seek status from " + $scope.MontageMonitors[i].Monitor.Name);
$scope.MontageMonitors[i].Monitor.seek = false;
});
};
- $scope.moveFaster = function (mid) {
+ $scope.moveFaster = function(mid)
+ {
sendCmd(mid, 4);
};
- $scope.moveSlower = function (mid) {
+ $scope.moveSlower = function(mid)
+ {
sendCmd(mid, 5);
};
- $scope.movePlay = function (mid) {
+ $scope.movePlay = function(mid)
+ {
var m = -1;
- for (var i = 0; i < $scope.MontageMonitors.length; i++) {
- if ($scope.MontageMonitors[i].Monitor.Id == mid) {
+ for (var i = 0; i < $scope.MontageMonitors.length; i++)
+ {
+ if ($scope.MontageMonitors[i].Monitor.Id == mid)
+ {
m = i;
break;
}
}
- if (m != -1) {
+ if (m != -1)
+ {
$scope.MontageMonitors[m].Monitor.isPaused = false;
var cmd = 2;
NVRDataModel.debug("Sending CMD:" + cmd + " for monitor " + $scope.MontageMonitors[m].Monitor.Name);
@@ -162,20 +190,24 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
// Called when ion-footer collapses
// note that on init it is also called
//---------------------------------------
- $scope.footerExpand = function () {
+ $scope.footerExpand = function()
+ {
// console.log ("**************** EXPAND CALLED ***************");
$ionicSideMenuDelegate.canDragContent(false);
};
- $scope.footerCollapse = function () {
+ $scope.footerCollapse = function()
+ {
footerCollapse();
};
/* Note this is also called when the view is first loaded */
- function footerCollapse() {
- if (readyToRun == false) {
+ function footerCollapse()
+ {
+ if (readyToRun == false)
+ {
NVRDataModel.debug("fake call to footerCollapse - ignoring");
return;
}
-
+
if ($scope.MontageMonitors == undefined)
{
NVRDataModel.debug("montage array is undefined and not ready");
@@ -183,7 +215,8 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
}
$interval.cancel($rootScope.eventQueryInterval);
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kPleaseWait'),
noBackdrop: true,
duration: zm.httpTimeout
@@ -195,39 +228,37 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
NVRDataModel.stopNetwork("MontageHistory-footerCollapse");
var ld = NVRDataModel.getLogin();
$scope.sliderVal.realRate = $scope.sliderVal.rate * 100;
-
-
-
+
var TimeObjectFrom = moment($scope.datetimeValueFrom.value).format("YYYY-MM-DD HH:mm");
var TimeObjectTo = moment().format('YYYY-MM-DD HH:mm');
-
+
// At this point of time, we need to ensure From and To are changed to server time
//if (NVRDataModel.getLogin().useLocalTimeZone)
if (1)
{
var localtz = moment.tz.guess();
var servertz = NVRDataModel.getTimeZoneNow();
-
- NVRDataModel.log ("Local timezone conversion is on, converting from "+localtz+" to " +servertz);
- NVRDataModel.log ("Original From: " + TimeObjectFrom + " Original To: " + TimeObjectTo);
-
+
+ NVRDataModel.log("Local timezone conversion is on, converting from " + localtz + " to " + servertz);
+ NVRDataModel.log("Original From: " + TimeObjectFrom + " Original To: " + TimeObjectTo);
+
TimeObjectFrom = moment.tz(TimeObjectFrom, localtz).tz(servertz).format("YYYY-MM-DD HH:mm");
TimeObjectTo = moment.tz(TimeObjectTo, localtz).tz(servertz).format("YYYY-MM-DD HH:mm");
-
- NVRDataModel.log ("Converted From: " + TimeObjectFrom + " Converted To: " + TimeObjectTo);
-
+
+ NVRDataModel.log("Converted From: " + TimeObjectFrom + " Converted To: " + TimeObjectTo);
+
}
-
-
-
+
var apiurl;
// release all active streams
- for (var i = 0; i < $scope.MontageMonitors.length; i++) {
+ for (var i = 0; i < $scope.MontageMonitors.length; i++)
+ {
$scope.MontageMonitors[i].Monitor.selectStyle = "";
$scope.MontageMonitors[i].Monitor.eid = "-1";
// generate new connKeys if timeline changes
- if ($scope.MontageMonitors[i].Monitor.eventUrl != 'img/noevent.png') {
+ if ($scope.MontageMonitors[i].Monitor.eventUrl != 'img/noevent.png')
+ {
// this means this mid was showing a message, now we need to change it
// so kill prev. stream first
NVRDataModel.log("footerCollapse: Calling kill with " + $scope.MontageMonitors[i].Monitor.connKey + " for Monitor:" + $scope.MontageMonitors[i].Monitor.Name);
@@ -247,24 +278,30 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
// make sure there are no more than 5 active streams (noevent is ok)
$scope.currentLimit = $scope.monLimit;
//qHttp.get(apiurl)
- $http({
+ $http(
+ {
method: 'get',
url: apiurl
- }).then(function (succ) {
+ }).then(function(succ)
+ {
var data = succ.data;
var ld = NVRDataModel.getLogin();
NVRDataModel.debug("Got " + data.events.length + "new history events...");
var eid, mid, stime;
- for (i = 0; i < data.events.length; i++) {
+ for (i = 0; i < data.events.length; i++)
+ {
mid = data.events[i].Event.MonitorId;
eid = data.events[i].Event.Id;
stime = data.events[i].Event.StartTime;
// only take the first one for each monitor
- for (var j = 0; j < $scope.MontageMonitors.length; j++) {
+ for (var j = 0; j < $scope.MontageMonitors.length; j++)
+ {
$scope.MontageMonitors[j].Monitor.isPaused = false;
// that's the earliest match and play gapless from there
- if ($scope.MontageMonitors[j].Monitor.Id == mid) {
- if ($scope.MontageMonitors[j].Monitor.eventUrl == 'img/noevent.png') {
+ if ($scope.MontageMonitors[j].Monitor.Id == mid)
+ {
+ if ($scope.MontageMonitors[j].Monitor.eventUrl == 'img/noevent.png')
+ {
// console.log ("Old value of event url " + $scope.MontageMonitors[j].eventUrl);
//console.log ("ldurl is " + ld.streamingurl);
var bw = NVRDataModel.getBandwidth() == "lowbw" ? zm.eventMontageQualityLowBW : ld.montageHistoryQuality;
@@ -281,7 +318,6 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
// now lets get the API for that event for graphing
$scope.MontageMonitors[j].Monitor.noGraph = true;
-
}
}
}
@@ -290,9 +326,11 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
// in the above call, is possible some did not make the cut in the first page
NVRDataModel.log("Making sure all monitors have a fair chance...");
var promises = [];
- for (i = 0; i < $scope.MontageMonitors.length; i++) {
+ for (i = 0; i < $scope.MontageMonitors.length; i++)
+ {
//console.log("Fair chance check for " + $scope.MontageMonitors[i].Monitor.Name);
- if ($scope.MontageMonitors[i].Monitor.eventUrl == 'img/noevent.png') {
+ if ($scope.MontageMonitors[i].Monitor.eventUrl == 'img/noevent.png')
+ {
var indivGrab = ld.apiurl + "/events/index/MonitorId:" + $scope.MontageMonitors[i].Monitor.Id + "/StartTime >=:" + TimeObjectFrom + "/AlarmFrames >=:" + (ld.enableAlarmCount ? ld.minAlarmCount : 0) + ".json";
NVRDataModel.debug("Monitor " + $scope.MontageMonitors[i].Monitor.Id + ":" + $scope.MontageMonitors[i].Monitor.Name + " does not have events, trying " + indivGrab);
var p = getExpandedEvents(i, indivGrab);
@@ -305,13 +343,16 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
// At this stage, we have both a general events grab, and specific event grabs for MIDS that were empty
- function doPackery() {
+ function doPackery()
+ {
// $ionicLoading.hide();
//console.log("REDOING PACKERY & DRAG");
NVRDataModel.debug("Re-creating packery and draggy");
- if (pckry !== undefined) {
+ if (pckry !== undefined)
+ {
// remove current draggies
- draggies.forEach(function (drag) {
+ draggies.forEach(function(drag)
+ {
drag.destroy();
});
draggies = [];
@@ -319,27 +360,34 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
pckry.destroy();
initPackery();
- $rootScope.eventQueryInterval = $interval(function () {
+ $rootScope.eventQueryInterval = $interval(function()
+ {
checkAllEvents();
}.bind(this), zm.eventHistoryTimer);
}
}
- }, function (err) {
+ }, function(err)
+ {
NVRDataModel.debug("history ERROR:" + JSON.stringify(err));
});
- function getExpandedEvents(i, indivGrab) {
+ function getExpandedEvents(i, indivGrab)
+ {
var d = $q.defer();
var ld = NVRDataModel.getLogin();
// console.log ("Expanded API: " + indivGrab);
- $http({
+ $http(
+ {
method: 'get',
url: indivGrab
- }).then(function (succ) {
+ }).then(function(succ)
+ {
var data = succ.data;
// console.log ("EXPANDED DATA FOR MONITOR " + i + JSON.stringify(data));
- if (data.events.length > 0) {
- if (!NVRDataModel.isBackground()) {
+ if (data.events.length > 0)
+ {
+ if (!NVRDataModel.isBackground())
+ {
var bw = NVRDataModel.getBandwidth() == "lowbw" ? zm.eventMontageQualityLowBW : ld.montageHistoryQuality;
$scope.MontageMonitors[i].Monitor.eventUrl = ld.streamingurl + "/nph-zms?source=event&mode=jpeg&event=" + data.events[0].Event.Id + "&frame=1&replay=gapless&rate=" + $scope.sliderVal.realRate + "&connkey=" + $scope.MontageMonitors[i].Monitor.connKey + "&scale=" + bw + "&rand=" + $rootScope.rand;
//console.log ("SWITCHING TO " + $scope.MontageMonitors[i].eventUrl);
@@ -352,7 +400,9 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
$scope.MontageMonitors[i].Monitor.eventDuration = data.events[0].Event.Length;
//console.log(">>> Setting Event for " + $scope.MontageMonitors[i].Monitor.Name + " to " + data.events[0].Event.Id);
NVRDataModel.log("Found expanded event " + data.events[0].Event.Id + " for monitor " + $scope.MontageMonitors[i].Monitor.Id);
- } else {
+ }
+ else
+ {
// $scope.MontageMonitors[i].eventUrl="img/noevent.png";
// $scope.MontageMonitors[i].eventUrlTime = "";
// NVRDataModel.log ("Setting img src to null as data received in background");
@@ -361,7 +411,8 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
d.resolve(true);
return d.promise;
},
- function (err) {
+ function(err)
+ {
d.resolve(true);
return d.promise;
}
@@ -376,16 +427,19 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
// Its a 2 step process - get event Id then go a Event
// API call to get time stamp. Sucks
//---------------------------------------------------------
- function checkAllEvents() {
+ function checkAllEvents()
+ {
//console.log("Timer:Events are checked....");
- //if (pckry && !$scope.isDragabillyOn) pckry.shiftLayout();
+ //if (pckry && !$scope.isDragabillyOn) pckry.shiftLayout();
- for (var i = 0; i < $scope.MontageMonitors.length; i++) {
+ for (var i = 0; i < $scope.MontageMonitors.length; i++)
+ {
// don't check for monitors that are not shown
// because nph connkey won't exist and the response
// will fail
- if ($scope.MontageMonitors[i].Monitor.eventUrl != "" && $scope.MontageMonitors[i].Monitor.eventUrl != 'img/noevent.png' && $scope.MontageMonitors[i].Monitor.connKey != '' && $scope.MontageMonitors[i].Monitor.Function != 'None' && $scope.MontageMonitors[i].Monitor.listDisplay != 'noshow' && $scope.MontageMonitors[i].Monitor.Enabled != '0') {
+ if ($scope.MontageMonitors[i].Monitor.eventUrl != "" && $scope.MontageMonitors[i].Monitor.eventUrl != 'img/noevent.png' && $scope.MontageMonitors[i].Monitor.connKey != '' && $scope.MontageMonitors[i].Monitor.Function != 'None' && $scope.MontageMonitors[i].Monitor.listDisplay != 'noshow' && $scope.MontageMonitors[i].Monitor.Enabled != '0')
+ {
// NVRDataModel.debug("Checking event status for " + $scope.MontageMonitors[i].Monitor.Name + ":" + $scope.MontageMonitors[i].Monitor.eventUrl + ":" + $scope.MontageMonitors[i].Monitor.Function + ":" + $scope.MontageMonitors[i].Monitor.listDisplay);
// console.log ("Sending query 99 for " + $scope.MontageMonitors[i].Monitor.Name + " with ck="+$scope.MontageMonitors[i].Monitor.connKey);
controlEventStream('99', '', $scope.MontageMonitors[i].Monitor.connKey, i);
@@ -397,37 +451,45 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
// then it also calls an event API for the returned eid
// and stores its time in the montage monitors array
//--------------------------------------------------------------
- $scope.controlEventStream = function (cmd, disp, connkey, ndx) {
+ $scope.controlEventStream = function(cmd, disp, connkey, ndx)
+ {
controlEventStream(cmd, disp, connkey, ndx);
};
- function timedControlEventStream(mTime, cmd, disp, connkey, ndx) {
+ function timedControlEventStream(mTime, cmd, disp, connkey, ndx)
+ {
var mMtime = mTime || 2000;
NVRDataModel.debug("Deferring control " + cmd + " by " + mMtime);
- $timeout(function () {
+ $timeout(function()
+ {
subControlStream(cmd, connkey);
}, mMtime);
}
- function subControlStream(cmd, connkey) {
+ function subControlStream(cmd, connkey)
+ {
var loginData = NVRDataModel.getLogin();
var myauthtoken = $rootScope.authSession.replace("&auth=", "");
//&auth=
- var req = qHttp({
+ var req = qHttp(
+ {
method: 'POST',
/*timeout: 15000,*/
url: loginData.url + '/index.php',
- headers: {
+ headers:
+ {
'Content-Type': 'application/x-www-form-urlencoded', //'Accept': '*/*',
},
- transformRequest: function (obj) {
+ transformRequest: function(obj)
+ {
var str = [];
for (var p in obj) str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
var foo = str.join("&");
//console.log("****SUB RETURNING " + foo);
return foo;
},
- data: {
+ data:
+ {
view: "request",
request: "stream",
connkey: connkey,
@@ -436,20 +498,25 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
// pass: loginData.password
}
});
- req.then(function (succ) {
+ req.then(function(succ)
+ {
NVRDataModel.debug("subControl success:" + JSON.stringify(succ));
- }, function (err) {
+ }, function(err)
+ {
NVRDataModel.debug("subControl error:" + JSON.stringify(err));
});
}
- function controlEventStream(cmd, disp, connkey, ndx, extras) {
+ function controlEventStream(cmd, disp, connkey, ndx, extras)
+ {
// console.log("Command value " + cmd);
var d = $q.defer();
- if (disp) {
+ if (disp)
+ {
$ionicLoading.hide();
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kPleaseWait') + "...",
noBackdrop: true,
duration: zm.loadingTimeout,
@@ -482,14 +549,17 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
//console.log ("AUTH IS " + $rootScope.authSession);
var myauthtoken = $rootScope.authSession.replace("&auth=", "");
//&auth=
- var req = qHttp({
+ var req = qHttp(
+ {
method: 'POST',
/*timeout: 15000,*/
url: loginData.url + '/index.php',
- headers: {
+ headers:
+ {
'Content-Type': 'application/x-www-form-urlencoded', //'Accept': '*/*',
},
- transformRequest: function (obj) {
+ transformRequest: function(obj)
+ {
var str = [];
for (var p in obj) str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
var foo = str.join("&");
@@ -497,7 +567,8 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
//console.log("****RETURNING " + foo);
return foo;
},
- data: {
+ data:
+ {
view: "request",
request: "stream",
connkey: connkey,
@@ -506,31 +577,39 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
// pass: loginData.password
}
});
- req.then(function (succ) {
+ req.then(function(succ)
+ {
var resp = succ.data;
//console.log ("zms response: " + JSON.stringify(resp));
// move progress bar if event id is the same
- if (resp.result == "Ok" && ndx != -1 && (resp.status.event == $scope.MontageMonitors[ndx].Monitor.eid)) {
- if (!$scope.MontageMonitors[ndx].Monitor.seek) {
+ if (resp.result == "Ok" && ndx != -1 && (resp.status.event == $scope.MontageMonitors[ndx].Monitor.eid))
+ {
+ if (!$scope.MontageMonitors[ndx].Monitor.seek)
+ {
$scope.MontageMonitors[ndx].Monitor.sliderProgress.progress = resp.status.progress;
- } else {
+ }
+ else
+ {
NVRDataModel.debug("Skipping progress as seek is active for " + $scope.MontageMonitors[ndx].Monitor.Name);
}
}
- if (resp.result == "Ok" && ndx != -1 && ((resp.status.event != $scope.MontageMonitors[ndx].Monitor.eid) || $scope.MontageMonitors[ndx].Monitor.noGraph == true)) {
+ if (resp.result == "Ok" && ndx != -1 && ((resp.status.event != $scope.MontageMonitors[ndx].Monitor.eid) || $scope.MontageMonitors[ndx].Monitor.noGraph == true))
+ {
$scope.MontageMonitors[ndx].Monitor.noGraph = false;
// $scope.MontageMonitors[ndx].Monitor.sliderProgress.progress = 0;
NVRDataModel.debug("Fetching details, as event changed for " + $scope.MontageMonitors[ndx].Monitor.Name + " from " + $scope.MontageMonitors[ndx].Monitor.eid + " to " + resp.status.event);
var ld = NVRDataModel.getLogin();
var apiurl = ld.apiurl + "/events/" + resp.status.event + ".json";
//console.log ("API " + apiurl);
- qHttp({
+ qHttp(
+ {
method: 'get',
url: apiurl
- }).then(function (succ) {
+ }).then(function(succ)
+ {
var data = succ.data;
var currentEventTime = moment(data.event.Event.StartTime);
var maxTime = moment();
@@ -539,31 +618,36 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
NVRDataModel.debug("creating graph for " + $scope.MontageMonitors[ndx].Monitor.Name);
var framearray = {
labels: [],
- datasets: [{
+ datasets: [
+ {
backgroundColor: 'rgba(242, 12, 12, 0.5)',
borderColor: 'rgba(242, 12, 12, 0.5)',
data: [],
- }]
+ }]
};
framearray.labels = [];
var ld = NVRDataModel.getLogin();
//console.log(">>>>> GRAPH");
- for (i = 0; i < data.event.Frame.length; i++) {
+ for (i = 0; i < data.event.Frame.length; i++)
+ {
var ts = moment(data.event.Frame[i].TimeStamp).format(timeFormat);
//console.log ("pushing s:" + event.Frame[i].Score+" t:"+ts);
- framearray.datasets[0].data.push({
+ framearray.datasets[0].data.push(
+ {
x: ts,
y: data.event.Frame[i].Score
});
framearray.labels.push("");
}
- $timeout(function () {
+ $timeout(function()
+ {
drawGraph(framearray, $scope.MontageMonitors[ndx].Monitor.Id);
}, 100);
var element = angular.element(document.getElementById($scope.MontageMonitors[ndx].Monitor.Id + "-timeline"));
element.removeClass('animated flipInX');
element.addClass('animated flipOutX');
- $timeout(function () {
+ $timeout(function()
+ {
element.removeClass('animated flipOutX');
element.addClass('animated flipInX');
$scope.MontageMonitors[ndx].Monitor.eventUrlTime = data.event.Event.StartTime;
@@ -577,59 +661,71 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
//console.log(">>> Setting Event for " + $scope.MontageMonitors[ndx].Monitor.Name + " to " + data.event.Event.Id);
}, 700);
-
- }, function (err) {
+ }, function(err)
+ {
NVRDataModel.debug("skipping graph as detailed API failed for " + $scope.MontageMonitors[ndx].Monitor.Name);
$scope.MontageMonitors[ndx].Monitor.eventUrlTime = "-";
});
}
d.resolve(true);
return d.promise;
- }, function (err) {
+ }, function(err)
+ {
d.reject(false);
NVRDataModel.log("Error sending event command " + JSON.stringify(err), "error");
return d.promise;
});
return d.promise;
}
- $scope.isBackground = function () {
+ $scope.isBackground = function()
+ {
return NVRDataModel.isBackground();
};
//----------------------------------------------------------------
// Alarm notification handling
//----------------------------------------------------------------
- $scope.handleAlarms = function () {
+ $scope.handleAlarms = function()
+ {
$rootScope.isAlarm = !$rootScope.isAlarm;
- if (!$rootScope.isAlarm) {
+ if (!$rootScope.isAlarm)
+ {
$rootScope.alarmCount = "0";
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
- $state.go("events", {
+ $state.go("events",
+ {
"id": 0,
"playEvent": false
- }, {
+ },
+ {
reload: true
});
return;
}
};
- $scope.handleAlarmsWhileMinimized = function () {
+ $scope.handleAlarmsWhileMinimized = function()
+ {
$rootScope.isAlarm = !$rootScope.isAlarm;
$scope.minimal = !$scope.minimal;
NVRDataModel.debug("MontageHistoryCtrl: switch minimal is " + $scope.minimal);
ionic.Platform.fullScreen($scope.minimal, !$scope.minimal);
$interval.cancel(intervalHandle);
$interval.cancel($rootScope.eventQueryInterval);
- if (!$rootScope.isAlarm) {
+ if (!$rootScope.isAlarm)
+ {
$rootScope.alarmCount = "0";
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
- $state.go("events", {
+ $state.go("events",
+ {
"id": 0,
"playEvent": false,
- }, {
+ },
+ {
reload: true
});
return;
@@ -640,18 +736,24 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
// when app is in background. This is a problem with Android,
// for example
//-------------------------------------------------------------
- $scope.isBackground = function () {
+ $scope.isBackground = function()
+ {
//console.log ("Is background called from Montage and returned " +
//NVRDataModel.isBackground());
return NVRDataModel.isBackground();
};
- $scope.toggleControls = function () {
+ $scope.toggleControls = function()
+ {
$scope.showControls = !$scope.showControls;
};
- $scope.toggleSelectItem = function (ndx) {
- if ($scope.MontageMonitors[ndx].Monitor.selectStyle !== "undefined" && $scope.MontageMonitors[ndx].Monitor.selectStyle == "dragborder-selected") {
+ $scope.toggleSelectItem = function(ndx)
+ {
+ if ($scope.MontageMonitors[ndx].Monitor.selectStyle !== "undefined" && $scope.MontageMonitors[ndx].Monitor.selectStyle == "dragborder-selected")
+ {
$scope.MontageMonitors[ndx].Monitor.selectStyle = "";
- } else {
+ }
+ else
+ {
$scope.MontageMonitors[ndx].Monitor.selectStyle = "dragborder-selected";
}
//console.log ("Switched value to " + $scope.MontageMonitors[ndx].Monitor.selectStyle);
@@ -659,39 +761,49 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
//---------------------------------------------------------------------
// Called when you enable/disable dragging
//---------------------------------------------------------------------
- $scope.dragToggle = function () {
+ $scope.dragToggle = function()
+ {
dragToggle();
};
- function dragToggle() {
+ function dragToggle()
+ {
var i;
$scope.isDragabillyOn = !$scope.isDragabillyOn;
$ionicSideMenuDelegate.canDragContent($scope.isDragabillyOn ? false : true);
//$timeout(function(){pckry.reloadItems();},10);
NVRDataModel.debug("setting dragabilly to " + $scope.isDragabillyOn);
- if ($scope.isDragabillyOn) {
+ if ($scope.isDragabillyOn)
+ {
$scope.showSizeButtons = true;
$scope.dragBorder = "dragborder";
NVRDataModel.debug("Enabling drag for " + draggies.length + " items");
- for (i = 0; i < draggies.length; i++) {
+ for (i = 0; i < draggies.length; i++)
+ {
draggies[i].enable();
draggies[i].bindHandles();
}
// reflow and reload as some may be hidden
// $timeout(function(){pckry.reloadItems();$timeout(function(){pckry.layout();},300);},100);
- } else {
+ }
+ else
+ {
$scope.dragBorder = "";
NVRDataModel.debug("Disabling drag for " + draggies.length + " items");
- for (i = 0; i < draggies.length; i++) {
+ for (i = 0; i < draggies.length; i++)
+ {
draggies[i].disable();
draggies[i].unbindHandles();
}
- for (i = 0; i < $scope.MontageMonitors.length; i++) {
+ for (i = 0; i < $scope.MontageMonitors.length; i++)
+ {
$scope.MontageMonitors[i].Monitor.selectStyle = "";
}
// reflow and reload as some may be hidden
- $timeout(function () {
- $timeout(function () {
+ $timeout(function()
+ {
+ $timeout(function()
+ {
pckry.shiftLayout();
/*var positions = pckry.getShiftPositions('data-item-id');
//console.log ("POSITIONS MAP " + JSON.stringify(positions));
@@ -705,13 +817,16 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
//---------------------------------------------------------------------
// Show/Hide PTZ control in monitor view
//---------------------------------------------------------------------
- $scope.togglePTZ = function () {
+ $scope.togglePTZ = function()
+ {
$scope.showPTZ = !$scope.showPTZ;
};
- $scope.callback = function () {
+ $scope.callback = function()
+ {
// console.log("dragging");
};
- $scope.onDropComplete = function (index, obj, event) {
+ $scope.onDropComplete = function(index, obj, event)
+ {
//console.log("dragged");
var otherObj = $scope.MontageMonitors[index];
var otherIndex = $scope.MontageMonitors.indexOf(obj);
@@ -721,7 +836,8 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
//---------------------------------------------------------------------
// changes order of montage display
//---------------------------------------------------------------------
- $scope.toggleMontageDisplayOrder = function () {
+ $scope.toggleMontageDisplayOrder = function()
+ {
$scope.packMontage = !$scope.packMontage;
loginData.packMontage = $scope.packMontage;
NVRDataModel.setLogin(loginData);
@@ -733,7 +849,8 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
// So while this view, we DON'T want Android to keep sending 1 second
// refreshes to the server for images we are not seeing
//---------------------------------------------------------------------
- function onPause() {
+ function onPause()
+ {
NVRDataModel.debug("MontageHistoryCtrl: onpause called");
$interval.cancel($rootScope.eventQueryInterval);
$interval.cancel(intervalHandle);
@@ -741,21 +858,27 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
// FIXME: Do I need to setAwake(false) here?
}
- function onResume() {}
- $scope.openMenu = function () {
- $timeout(function () {
+ function onResume()
+ {}
+ $scope.openMenu = function()
+ {
+ $timeout(function()
+ {
$rootScope.stateofSlide = $ionicSideMenuDelegate.isOpen();
}, 500);
$ionicSideMenuDelegate.toggleLeft();
};
- $scope.$on('$destroy', function () {
+ $scope.$on('$destroy', function()
+ {
NVRDataModel.debug("Cancelling eventQueryInterval");
$interval.cancel($rootScope.eventQueryInterval);
});
- $scope.$on('$ionicView.loaded', function () {
+ $scope.$on('$ionicView.loaded', function()
+ {
//console.log("**VIEW ** MontageHistoryCtrl Loaded");
});
- $scope.$on('$ionicView.enter', function () {
+ $scope.$on('$ionicView.enter', function()
+ {
NVRDataModel.debug("**VIEW ** MontageHistory Ctrl Entered");
var ld = NVRDataModel.getLogin();
//console.log("Setting Awake to " + NVRDataModel.getKeepAwake());
@@ -763,7 +886,8 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
NVRDataModel.debug("query timer started");
$interval.cancel($rootScope.eventQueryInterval);
//console.log ("****************** TIMER STARTED INSIDE ENTER");
- $rootScope.eventQueryInterval = $interval(function () {
+ $rootScope.eventQueryInterval = $interval(function()
+ {
checkAllEvents();
}.bind(this), zm.eventHistoryTimer);
});
@@ -771,14 +895,17 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
console.log ("******** HISTORY UNLOADED KILLING WINDOW ************");
window.stop();
});*/
- $scope.$on('$ionicView.beforeEnter', function () {
+ $scope.$on('$ionicView.beforeEnter', function()
+ {
// NVRDataModel.log ("Before Enter History: initing connkeys");
});
- $scope.$on('$ionicView.beforeLeave', function () {
+ $scope.$on('$ionicView.beforeLeave', function()
+ {
//console.log("**VIEW ** Event History Ctrl Left, force removing modal");
if ($scope.modal) $scope.modal.remove();
NVRDataModel.log("BeforeLeave: Nullifying the streams...");
- for (i = 0; i < $scope.MontageMonitors.length; i++) {
+ for (i = 0; i < $scope.MontageMonitors.length; i++)
+ {
var element = document.getElementById("img-" + i);
/*if (element)
{
@@ -795,8 +922,10 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
NVRDataModel.log("MontageHistory:Stopping network pull...");
// make sure this is applied in scope digest to stop network pull
// thats why we are doing it beforeLeave
- for (i = 0; i < $scope.MontageMonitors.length; i++) {
- if ($scope.MontageMonitors[i].Monitor.connKey != '' && $scope.MontageMonitors[i].Monitor.eventUrl != 'img/noevent.png' && $scope.MontageMonitors[i].Monitor.Function != 'None' && $scope.MontageMonitors[i].Monitor.lisDisplay != 'noshow' && $scope.MontageMonitors[i].Monitor.Enabled != '0') {
+ for (i = 0; i < $scope.MontageMonitors.length; i++)
+ {
+ if ($scope.MontageMonitors[i].Monitor.connKey != '' && $scope.MontageMonitors[i].Monitor.eventUrl != 'img/noevent.png' && $scope.MontageMonitors[i].Monitor.Function != 'None' && $scope.MontageMonitors[i].Monitor.lisDisplay != 'noshow' && $scope.MontageMonitors[i].Monitor.Enabled != '0')
+ {
NVRDataModel.log("Before leave: Calling kill with " + $scope.MontageMonitors[i].Monitor.connKey);
var tmpCK = angular.copy($scope.MontageMonitors[i].Monitor.connKey);
timedControlEventStream(2500, 17, "", tmpCK, -1);
@@ -807,10 +936,12 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
NVRDataModel.log("Forcing a window.stop() here");
NVRDataModel.stopNetwork("MontageHistory-beforeLeave");
});
- $scope.$on('$ionicView.unloaded', function () {});
- $scope.sliderChanged = function (dirn) {
+ $scope.$on('$ionicView.unloaded', function() {});
+ $scope.sliderChanged = function(dirn)
+ {
//console.log("SLIDER CHANGED");
- if ($scope.sliderChanging) {
+ if ($scope.sliderChanging)
+ {
// console.log ("too fast my friend");
//$scope.slider.monsize = oldSliderVal;
// return;
@@ -818,19 +949,24 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
$scope.sliderChanging = true;
var somethingReset = false;
// this only changes items that are selected
- for (var i = 0; i < $scope.MontageMonitors.length; i++) {
+ for (var i = 0; i < $scope.MontageMonitors.length; i++)
+ {
var curVal = parseInt($scope.MontageMonitors[i].Monitor.gridScale);
curVal = curVal + (10 * dirn);
if (curVal < 10) curVal = 10;
if (curVal > 100) curVal = 100;
//console.log ("For Index: " + i + " From: " + $scope.MontageMonitors[i].Monitor.gridScale + " To: " + curVal);
- if ($scope.isDragabillyOn) {
+ if ($scope.isDragabillyOn)
+ {
// only do this for selected monitors
- if ($scope.MontageMonitors[i].Monitor.selectStyle == "dragborder-selected") {
+ if ($scope.MontageMonitors[i].Monitor.selectStyle == "dragborder-selected")
+ {
$scope.MontageMonitors[i].Monitor.gridScale = curVal;
somethingReset = true;
}
- } else {
+ }
+ else
+ {
$scope.MontageMonitors[i].Monitor.gridScale = curVal;
//somethingReset = true;
}
@@ -838,7 +974,8 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
// this changes all items if none were selected
if (!somethingReset && $scope.isDragabillyOn) // nothing was selected
{
- for (i = 0; i < $scope.MontageMonitors.length; i++) {
+ for (i = 0; i < $scope.MontageMonitors.length; i++)
+ {
var cv = parseInt($scope.MontageMonitors[i].Monitor.gridScale);
cv = cv + (10 * dirn);
if (cv < 10) cv = 10;
@@ -847,7 +984,8 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
}
}
//pckry.reloadItems();
- pckry.once('layoutComplete', function () {
+ pckry.once('layoutComplete', function()
+ {
/* $timeout(function () {
var positions = pckry.EHgetShiftPositions('eh-data-item-id');
//console.log ("POSITIONS MAP " + JSON.stringify(positions));
@@ -858,58 +996,77 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
$scope.sliderChanging = false;
}, zm.packeryTimer);*/
});
- if (!somethingReset) {
+ if (!somethingReset)
+ {
//console.log (">>>SOMETHING NOT RESET");
- $timeout(function () {
+ $timeout(function()
+ {
pckry.layout();
}, zm.packeryTimer);
- } else {
+ }
+ else
+ {
//console.log (">>>SOMETHING RESET");
- $timeout(function () {
+ $timeout(function()
+ {
layout(pckry);
}, zm.packeryTimer);
}
};
- function layout(pckry) {
+ function layout(pckry)
+ {
pckry.shiftLayout();
}
- $scope.resetSizes = function () {
+ $scope.resetSizes = function()
+ {
var somethingReset = false;
- for (var i = 0; i < $scope.MontageMonitors.length; i++) {
- if ($scope.isDragabillyOn) {
- if ($scope.MontageMonitors[i].Monitor.selectStyle == "dragborder-selected") {
+ for (var i = 0; i < $scope.MontageMonitors.length; i++)
+ {
+ if ($scope.isDragabillyOn)
+ {
+ if ($scope.MontageMonitors[i].Monitor.selectStyle == "dragborder-selected")
+ {
$scope.MontageMonitors[i].Monitor.gridScale = "50";
somethingReset = true;
}
- } else {
+ }
+ else
+ {
$scope.MontageMonitors[i].Monitor.gridScale = "50";
// somethingReset = true;
}
}
if (!somethingReset && $scope.isDragabillyOn) // nothing was selected
{
- for (i = 0; i < $scope.MontageMonitors.length; i++) {
+ for (i = 0; i < $scope.MontageMonitors.length; i++)
+ {
$scope.MontageMonitors[i].Monitor.gridScale = "50";
}
}
- $timeout(function () {
+ $timeout(function()
+ {
pckry.reloadItems();
- $timeout(function () {
+ $timeout(function()
+ {
pckry.layout();
}, zm.packeryTimer); // force here - no shiftlayout
}, 100);
};
- function isEmpty(obj) {
- for (var prop in obj) {
+ function isEmpty(obj)
+ {
+ for (var prop in obj)
+ {
return false;
}
return true;
}
// called by afterEnter to load Packery
- function initPackery() {
- $ionicLoading.show({
+ function initPackery()
+ {
+ $ionicLoading.show(
+ {
template: $translate.instant('kArrangingImages'),
noBackdrop: true,
duration: zm.loadingTimeout
@@ -920,40 +1077,44 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
var ld = NVRDataModel.getLogin();
var elem = angular.element(document.getElementById("mygrid"));
- pckry = new Packery('.grid', {
- itemSelector: '.grid-item',
- percentPosition: true,
- columnWidth: '.grid-sizer',
- gutter: 0,
- initLayout: true
+ pckry = new Packery('.grid',
+ {
+ itemSelector: '.grid-item',
+ percentPosition: true,
+ columnWidth: '.grid-sizer',
+ gutter: 0,
+ initLayout: true
- });
+ });
//console.log ("**** mygrid is " + JSON.stringify(elem));
- imagesLoaded(elem).on('progress', function (instance, img) {
+ imagesLoaded(elem).on('progress', function(instance, img)
+ {
var result = img.isLoaded ? 'loaded' : 'broken';
- NVRDataModel.debug( '~~loaded image is ' + result + ' for ' + img.img.src );
+ NVRDataModel.debug('~~loaded image is ' + result + ' for ' + img.img.src);
pckry.layout();
progressCalled = true;
// if (layouttype) $timeout (function(){layout(pckry);},100);
});
- imagesLoaded(elem).once('always', function () {
+ imagesLoaded(elem).once('always', function()
+ {
//console.log("******** ALL IMAGES LOADED");
$scope.$digest();
NVRDataModel.debug("All images loaded");
$ionicLoading.hide();
$scope.areImagesLoading = false;
-
-
- if (!progressCalled) {
+
+ if (!progressCalled)
+ {
NVRDataModel.log("*** PROGRESS WAS NOT CALLED");
pckry.reloadItems();
}
+ $timeout(function()
+ {
- $timeout(function () {
-
- pckry.getItemElements().forEach(function (itemElem) {
+ pckry.getItemElements().forEach(function(itemElem)
+ {
draggie = new Draggabilly(itemElem);
pckry.bindDraggabillyEvents(draggie);
draggies.push(draggie);
@@ -963,8 +1124,6 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
pckry.on('dragItemPositioned', itemDragged);
-
-
/*if (!isEmpty(positions)) {
NVRDataModel.log("Arranging as per packery grid");
@@ -986,26 +1145,24 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
}, 0);
}*/
-
-
- $timeout(function () {
+ $timeout(function()
+ {
NVRDataModel.log("Force calling resize");
pckry.layout();
$scope.packeryDone = true;
}, zm.packeryTimer); // don't ask
-
-
}, zm.packeryTimer);
-
});
- function itemDragged(item) {
+ function itemDragged(item)
+ {
NVRDataModel.debug("drag complete");
}
}
- $scope.$on('$ionicView.beforeEnter', function () {
+ $scope.$on('$ionicView.beforeEnter', function()
+ {
// This rand is really used to reload the monitor image in img-src so it is not cached
// I am making sure the image in montage view is always fresh
// I don't think I am using this anymore FIXME: check and delete if needed
@@ -1014,69 +1171,79 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
$scope.packeryDone = false;
readyToRun = false;
$scope.MontageMonitors = message;
-
doInitCode();
-
-
-
});
- $scope.reloadView = function () {
+ $scope.reloadView = function()
+ {
$rootScope.rand = Math.floor((Math.random() * 100000) + 1);
NVRDataModel.log("User action: image reload " + $rootScope.rand);
};
- $scope.doRefresh = function () {
+ $scope.doRefresh = function()
+ {
//console.log("***Pull to Refresh, recomputing Rand");
NVRDataModel.log("Reloading view for montage view, recomputing rand");
$rootScope.rand = Math.floor((Math.random() * 100000) + 1);
$scope.MontageMonitors = [];
imageLoadingDataShare.set(0);
var refresh = NVRDataModel.getMonitors(1);
- refresh.then(function (data) {
+ refresh.then(function(data)
+ {
$scope.MontageMonitors = data.data;
$scope.$broadcast('scroll.refreshComplete');
});
};
- function drawGraph(f, mid) {
+ function drawGraph(f, mid)
+ {
//console.log("Graphing on " + "eventchart-" + mid);
var cv = document.getElementById("eventchart-" + mid);
var ctx = cv.getContext("2d");
frameoptions = {
responsive: true,
legend: false,
- title: {
+ title:
+ {
display: false,
text: ""
},
- scales: {
- yAxes: [{
+ scales:
+ {
+ yAxes: [
+ {
display: false,
- scaleLabel: {
+ scaleLabel:
+ {
display: false,
labelString: 'value',
}
- }],
- xAxes: [{
+ }],
+ xAxes: [
+ {
type: 'time',
display: false,
- time: {
+ time:
+ {
format: timeFormat,
tooltipFormat: 'll HH:mm',
min: f.datasets[0].data[0].x,
max: f.datasets[0].data[f.datasets[0].data.length - 1].x,
- displayFormats: {}
+ displayFormats:
+ {}
},
- scaleLabel: {
+ scaleLabel:
+ {
display: false,
labelString: ''
}
}]
}
};
- $timeout(function () {
- var myChart = new Chart(ctx, {
+ $timeout(function()
+ {
+ var myChart = new Chart(ctx,
+ {
type: 'line',
data: f,
options: frameoptions,
@@ -1166,27 +1333,32 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
$scope.datetimeValueFrom.hrs = Math.round(moment.duration(moment().diff(moment($scope.datetimeValueFrom.value))).asHours());
commonCss = {
- background: {
+ background:
+ {
"background-color": "silver"
},
- before: {
+ before:
+ {
"background-color": "purple"
},
- default: {
+ default:
+ {
"background-color": "white"
}, // default value: 1px
- after: {
+ after:
+ {
"background-color": "green"
}, // zone after default value
- pointer: {
+ pointer:
+ {
"background-color": "red"
}, // circle pointer
- range: {
+ range:
+ {
"background-color": "red"
} // use it if double value
};
-
$scope.monitorSize = []; // array with montage sizes per monitor
$scope.scaleDirection = []; // 1 = increase -1 = decrease
// The difference between old and original is this:
@@ -1200,24 +1372,30 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
var montageOrder = []; // This array will keep the ordering in montage view
var hiddenOrder = []; // 1 = hide, 0 = don't hide
var tempMonitors = message;
- if (tempMonitors.length == 0) {
- $rootScope.zmPopup = $ionicPopup.alert({
+ if (tempMonitors.length == 0)
+ {
+ $rootScope.zmPopup = $ionicPopup.alert(
+ {
title: $translate.instant('kNoMonitors'),
- template: $translate.instant('kPleaseCheckCredentials')
+ template: $translate.instant('kPleaseCheckCredentials'),
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
});
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
$state.go("login");
return;
}
-
+
NVRDataModel.log("Inside MontageHistoryCtrl:We found " + $scope.MontageMonitors.length + " monitors");
// $scope.MontageMonitors = NVRDataModel.applyMontageMonitorPrefs(message, 1)[0];
var loginData = NVRDataModel.getLogin();
// init monitors
NVRDataModel.debug(">>Initializing connkeys and images...");
- for (i = 0; i < $scope.MontageMonitors.length; i++) {
+ for (i = 0; i < $scope.MontageMonitors.length; i++)
+ {
//$scope.MontageMonitors[i].Monitor.connKey='';
$scope.MontageMonitors[i].Monitor.eid = "-1";
$scope.MontageMonitors[i].Monitor.connKey = (Math.floor((Math.random() * 999999) + 1)).toString();
@@ -1237,19 +1415,26 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
// Handling of back button in case modal is open should
// close the modal
// --------------------------------------------------------
- $ionicPlatform.registerBackButtonAction(function (e) {
+ $ionicPlatform.registerBackButtonAction(function(e)
+ {
e.preventDefault();
- if ($scope.modal && $scope.modal.isShown()) {
+ if ($scope.modal && $scope.modal.isShown())
+ {
// switch off awake, as liveview is finished
NVRDataModel.debug("Modal is open, closing it");
NVRDataModel.setAwake(false);
$scope.modal.remove();
$scope.isModalActive = false;
- } else {
+ }
+ else
+ {
NVRDataModel.debug("Modal is closed, so toggling or exiting");
- if (!$ionicSideMenuDelegate.isOpenLeft()) {
+ if (!$ionicSideMenuDelegate.isOpenLeft())
+ {
$ionicSideMenuDelegate.toggleLeft();
- } else {
+ }
+ else
+ {
navigator.app.exitApp();
}
}
@@ -1260,13 +1445,15 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
$scope.LoginData = NVRDataModel.getLogin();
$scope.monLimit = $scope.LoginData.maxMontage;
$scope.currentLimit = $scope.LoginData.maxMontage;
- if ($rootScope.platformOS != 'ios') {
+ if ($rootScope.platformOS != 'ios')
+ {
NVRDataModel.log("Limiting montage to 5, thanks to Chrome's stupid connection limit");
$scope.currentLimit = 5;
$scope.monLimit = 5;
}
$rootScope.authSession = "undefined";
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kNegotiatingStreamAuth'),
animation: 'fade-in',
showBackdrop: true,
@@ -1277,23 +1464,27 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
ld = NVRDataModel.getLogin();
//console.log ("MONITORS " + JSON.stringify($scope.monitors));
$rootScope.validMonitorId = $scope.MontageMonitors[0].Monitor.Id;
- NVRDataModel.getAuthKey($rootScope.validMonitorId).then(function (success) {
+ NVRDataModel.getAuthKey($rootScope.validMonitorId).then(function(success)
+ {
$ionicLoading.hide();
//console.log(success);
$rootScope.authSession = success;
NVRDataModel.log("Stream authentication construction: " + $rootScope.authSession);
- $timeout(function () {
+ $timeout(function()
+ {
initPackery();
readyToRun = true;
footerCollapse();
}, zm.packeryTimer);
- }, function (error) {
+ }, function(error)
+ {
$ionicLoading.hide();
NVRDataModel.debug("MontageHistoryCtrl: Error in authkey retrieval " + error);
//$rootScope.authSession="";
NVRDataModel.log("MontageHistoryCtrl: Error returned Stream authentication construction. Retaining old value of: " + $rootScope.authSession);
- $timeout(function () {
+ $timeout(function()
+ {
initPackery();
readyToRun = true;
footerCollapse();
@@ -1301,4 +1492,4 @@ angular.module('zmApp.controllers').controller('zmApp.MontageHistoryCtrl', ['$sc
});
}
-}]); \ No newline at end of file
+}]);
diff --git a/www/js/NewsCtrl.js b/www/js/NewsCtrl.js
index 5328cf6b..e1e030ec 100644
--- a/www/js/NewsCtrl.js
+++ b/www/js/NewsCtrl.js
@@ -2,33 +2,38 @@
/* jslint browser: true*/
/* global cordova,StatusBar,angular,console,moment*/
-angular.module('zmApp.controllers').controller('zmApp.NewsCtrl', ['$scope', '$rootScope', '$ionicModal', 'NVRDataModel', '$ionicSideMenuDelegate', '$ionicHistory', '$state', '$http', 'zm', function ($scope, $rootScope, $ionicModal, NVRDataModel, $ionicSideMenuDelegate, $ionicHistory, $state, $http, zm) {
- $scope.openMenu = function () {
+angular.module('zmApp.controllers').controller('zmApp.NewsCtrl', ['$scope', '$rootScope', '$ionicModal', 'NVRDataModel', '$ionicSideMenuDelegate', '$ionicHistory', '$state', '$http', 'zm', function($scope, $rootScope, $ionicModal, NVRDataModel, $ionicSideMenuDelegate, $ionicHistory, $state, $http, zm)
+{
+ $scope.openMenu = function()
+ {
$ionicSideMenuDelegate.toggleLeft();
};
-
//----------------------------------------------------------------
// Alarm notification handling
//----------------------------------------------------------------
- $scope.handleAlarms = function () {
+ $scope.handleAlarms = function()
+ {
$rootScope.isAlarm = !$rootScope.isAlarm;
- if (!$rootScope.isAlarm) {
+ if (!$rootScope.isAlarm)
+ {
$rootScope.alarmCount = "0";
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
- $state.go("events", {
+ $state.go("events",
+ {
"id": 0,
- "playEvent":false
- }, {
+ "playEvent": false
+ },
+ {
reload: true
});
return;
}
};
-
//-------------------------------------------------------------------------
// Lets make sure we set screen dim properly as we enter
// The problem is we enter other states before we leave previous states
@@ -36,71 +41,92 @@ angular.module('zmApp.controllers').controller('zmApp.NewsCtrl', ['$scope', '$ro
// reset power state on exit as if it is called after we enter another
// state, that effectively overwrites current view power management needs
//------------------------------------------------------------------------
- $scope.$on('$ionicView.enter', function () {
+ $scope.$on('$ionicView.enter', function()
+ {
// console.log("**VIEW ** News Ctrl Entered");
NVRDataModel.setAwake(false);
-
});
- $scope.isUnread = function (itemdate) {
+ $scope.isUnread = function(itemdate)
+ {
var lastDate = NVRDataModel.getLatestBlogPostChecked();
+ //console.log ("BLOG DATE="+itemdate+" LAST DATE="+lastDate);
//get("latestBlogPostChecked");
if (!lastDate) return true;
var mLastDate = moment(lastDate);
var mItemDate = moment(itemdate);
//var unread = mItemDate.diff(mLastDate) >0) ? true:false;
//console.log (unread);
- return (mItemDate.diff(mLastDate) > 0) ? true : false;
-
+ return (mItemDate.diff(mLastDate, 'seconds') > 0) ? true : false;
};
- $scope.loadPost = function (item, itemdate) {
+ $scope.loadPost = function(item, itemdate)
+ {
var lastDate =
NVRDataModel.getLatestBlogPostChecked(); //zmStorageService.get("latestBlogPostChecked");
-
- if (!lastDate) {
+ if (!lastDate)
+ {
NVRDataModel.debug("First time checking blog posts, I see");
NVRDataModel.setLatestBlogPostChecked(itemdate);
//zmStorageService.set("latestBlogPostChecked", itemdate);
- } else {
+ }
+ else
+ {
NVRDataModel.debug("last post checked is " + lastDate);
NVRDataModel.debug("current post dated is " + itemdate);
var mLastDate = moment(lastDate);
var mItemDate = moment(itemdate);
- if (mItemDate.diff(mLastDate) > 0) {
+ if (mItemDate.diff(mLastDate, 'seconds') > 0)
+ {
NVRDataModel.debug("Updating lastDate to this post");
NVRDataModel.setLatestBlogPostChecked(itemdate); //zmStorageService.set("latestBlogPostChecked", itemdate);
- if (itemdate == $scope.newsItems[0].date) {
+ if (itemdate == $scope.newsItems[0].date)
+ {
// we are reading the latest post
$rootScope.newBlogPost = "";
}
}
}
+
window.open(item, '_blank', 'location=yes');
return false;
};
$scope.newsItems = [];
- $http.get(zm.blogUrl)
- .success(function (data) {
- //console.log ("Here2");
- // console.log (JSON.stringify(data));
- for (var i = 0; i < data.length; i++) {
- $scope.newsItems.push({
- title: data[i].title,
- url: data[i].url,
- date: data[i].date
+
+ $http.get(zm.blogUrl,
+ {transformResponse: function(d,h)
+ {
+ var trunc = "])}while(1);</x>";
+ d = d.substr(trunc.length);
+ return d;
+ }
+ })
+ .success(function(datastr)
+ {
+
+
+ // console.log ("DATA:"+data);
+ //
+ var data = JSON.parse(datastr);
+ for (var i = 0; i < data.payload.posts.length; i++)
+ {
+ $scope.newsItems.push(
+ {
+ title: data.payload.posts[i].title,
+ url: "https://medium.com/zmninja/"+data.payload.posts[i].uniqueSlug,
+ date: moment(data.payload.posts[i].createdAt).format("YYYY-MM-DD HH:mm:ss")
});
}
});
-}]); \ No newline at end of file
+}]);
diff --git a/www/js/PortalLoginCtrl.js b/www/js/PortalLoginCtrl.js
index f99932a2..95eb72d4 100644
--- a/www/js/PortalLoginCtrl.js
+++ b/www/js/PortalLoginCtrl.js
@@ -3,90 +3,108 @@
/*This is for the loop closure I am using in line 143 */
/* jslint browser: true*/
/* global vis,cordova,StatusBar,angular,console,moment */
-angular.module('zmApp.controllers').controller('zmApp.PortalLoginCtrl', ['$ionicPlatform', '$scope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$rootScope', '$http', '$q', '$state', '$ionicLoading', '$ionicPopover', '$ionicScrollDelegate', '$ionicModal', '$timeout', 'zmAutoLogin', '$ionicHistory', '$cordovaTouchID', 'EventServer', '$translate', function($ionicPlatform, $scope, zm, NVRDataModel, $ionicSideMenuDelegate, $rootScope, $http, $q, $state, $ionicLoading, $ionicPopover, $ionicScrollDelegate, $ionicModal, $timeout, zmAutoLogin, $ionicHistory, $cordovaTouchID, EventServer, $translate) {
-
+angular.module('zmApp.controllers').controller('zmApp.PortalLoginCtrl', ['$ionicPlatform', '$scope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$rootScope', '$http', '$q', '$state', '$ionicLoading', '$ionicPopover', '$ionicScrollDelegate', '$ionicModal', '$timeout', 'zmAutoLogin', '$ionicHistory', '$cordovaTouchID', 'EventServer', '$translate', function($ionicPlatform, $scope, zm, NVRDataModel, $ionicSideMenuDelegate, $rootScope, $http, $q, $state, $ionicLoading, $ionicPopover, $ionicScrollDelegate, $ionicModal, $timeout, zmAutoLogin, $ionicHistory, $cordovaTouchID, EventServer, $translate)
+{
$scope.$on('$ionicView.enter',
- function() {
+ function()
+ {
NVRDataModel.debug("Inside Portal login Enter handler");
loginData = NVRDataModel.getLogin();
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
-
$scope.pindata = {};
- if ($ionicSideMenuDelegate.isOpen()) {
+ if ($ionicSideMenuDelegate.isOpen())
+ {
$ionicSideMenuDelegate.toggleLeft();
NVRDataModel.debug("Sliding menu close");
}
-
$scope.pinPrompt = false; // if true, then PIN is displayed else skip
- if (NVRDataModel.isLoggedIn()) {
+ if (NVRDataModel.isLoggedIn())
+ {
NVRDataModel.log("User credentials are provided");
-
-
// You can login either via touch ID or typing in your code
- if ($ionicPlatform.is('ios') && loginData.usePin) {
+ if ($ionicPlatform.is('ios') && loginData.usePin)
+ {
$cordovaTouchID.checkSupport()
- .then(function() {
+ .then(function()
+ {
// success, TouchID supported
$cordovaTouchID.authenticate("")
- .then(function() {
+ .then(function()
+ {
NVRDataModel.log("Touch Success");
// Don't assign pin as it may be alphanum
unlock(true);
},
- function() {
+ function()
+ {
NVRDataModel.log("Touch Failed");
});
- }, function(error) {
+ }, function(error)
+ {
NVRDataModel.log("TouchID not supported");
});
- } else // touch was not used
+ }
+ else // touch was not used
{
NVRDataModel.log("not checking for touchID");
}
- if (loginData.usePin) {
+ if (loginData.usePin)
+ {
// this shows the pin prompt on screen
$scope.pinPrompt = true;
// dont call unlock, let the user type in code
- } else // no PIN Code so go directly to auth
+ }
+ else // no PIN Code so go directly to auth
{
unlock(true);
}
- } else // login creds are not present
+ }
+ else // login creds are not present
{
NVRDataModel.debug("PortalLogin: Not logged in, so going to login");
- if (NVRDataModel.isFirstUse()) {
+ if (NVRDataModel.isFirstUse())
+ {
NVRDataModel.debug("First use, showing warm and fuzzy...");
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableAnimate: true,
disableBack: true
});
$state.go('first-use');
return;
- } else {
- if (!$rootScope.userCancelledAuth) {
- $ionicHistory.nextViewOptions({
+ }
+ else
+ {
+ if (!$rootScope.userCancelledAuth)
+ {
+ $ionicHistory.nextViewOptions(
+ {
disableAnimate: true,
disableBack: true
});
- $state.go("login", {
+ $state.go("login",
+ {
"wizard": false
});
return;
- } else {
+ }
+ else
+ {
// do this only once - rest for next time
$rootScope.userCancelledAuth = false;
}
@@ -99,8 +117,10 @@ angular.module('zmApp.controllers').controller('zmApp.PortalLoginCtrl', ['$ionic
// remove status is pin is empty
//-------------------------------------------------------------------------------
- $scope.pinChange = function() {
- if ($scope.pindata.pin == null) {
+ $scope.pinChange = function()
+ {
+ if ($scope.pindata.pin == null)
+ {
$scope.pindata.status = "";
}
};
@@ -108,17 +128,18 @@ angular.module('zmApp.controllers').controller('zmApp.PortalLoginCtrl', ['$ionic
//-------------------------------------------------------------------------------
// unlock app if PIN is correct
//-------------------------------------------------------------------------------
- $scope.unlock = function() {
+ $scope.unlock = function()
+ {
// call with false meaning check for pin
unlock(false);
};
-
//------------------------------------------------------------------------
// Aaron Lager hack - can't figure out why he gets a 401 after
// successful login and then it works after resaving
//------------------------------------------------------------------------
- function tryLoggingSecondTimeHack() {
+ function tryLoggingSecondTimeHack()
+ {
var d = $q.defer();
zmAutoLogin.doLogin("<button class='button button-clear' style='line-height: normal; min-height: 0; min-width: 0;color:#fff;' ng-click='$root.cancelAuth()'><i class='ion-close-circled'></i>&nbsp;" + $translate.instant('kAuthenticating') + "...</button>")
@@ -126,32 +147,39 @@ angular.module('zmApp.controllers').controller('zmApp.PortalLoginCtrl', ['$ionic
{
NVRDataModel.debug("2nd auth login worked");
NVRDataModel.getAPIversion()
- .then(function(data) {
+ .then(function(data)
+ {
NVRDataModel.getKeyConfigParams(1);
NVRDataModel.log("2nd auth:Got API version: " + data);
$rootScope.apiVersion = data;
var ld = NVRDataModel.getLogin();
- if (NVRDataModel.versionCompare(data, zm.minAppVersion) == -1 && data != "0.0.0") {
+ if (NVRDataModel.versionCompare(data, zm.minAppVersion) == -1 && data != "0.0.0")
+ {
- $state.go('lowversion', {
+ $state.go('lowversion',
+ {
"ver": data
});
return;
}
- if (NVRDataModel.versionCompare(data, zm.recommendedAppVersion) == -1 && data != "0.0.0") {
+ if (NVRDataModel.versionCompare(data, zm.recommendedAppVersion) == -1 && data != "0.0.0")
+ {
- $state.go('importantmessage', {
+ $state.go('importantmessage',
+ {
"ver": data
});
return;
}
- if (data == "0.0.0") {
+ if (data == "0.0.0")
+ {
NVRDataModel.log("2nd Auth:API getVersion succeeded but returned 0.0.0 " + JSON.stringify(data));
NVRDataModel.displayBanner('error', ['ZoneMinder authentication failed']);
- $state.go("login", {
+ $state.go("login",
+ {
"wizard": false
});
return;
@@ -159,8 +187,6 @@ angular.module('zmApp.controllers').controller('zmApp.PortalLoginCtrl', ['$ionic
// coming here means continue
EventServer.refresh();
-
-
var statetoGo = $rootScope.lastState ? $rootScope.lastState : 'montage';
//NVRDataModel.debug ("logging state transition");
NVRDataModel.debug("2nd Auth: Transitioning state to: " +
@@ -169,16 +195,17 @@ angular.module('zmApp.controllers').controller('zmApp.PortalLoginCtrl', ['$ionic
return;
},
- function(error) {
+ function(error)
+ {
NVRDataModel.debug("2nd auth API failed, going to login");
d.reject("failed 2nd auth");
return (d.promise);
});
-
},
- function(error) {
+ function(error)
+ {
NVRDataModel.debug("2nd auth hack failed, going to login");
d.reject("failed 2nd auth");
return (d.promise);
@@ -187,14 +214,68 @@ angular.module('zmApp.controllers').controller('zmApp.PortalLoginCtrl', ['$ionic
return (d.promise);
}
- function unlock(idVerified) {
+ function evaluateTappedNotification()
+ {
+ if ($rootScope.tappedNotification)
+ {
+
+ var ld = NVRDataModel.getLogin();
+ NVRDataModel.log("Came via push tap. onTapScreen=" + ld.onTapScreen);
+ //console.log ("***** NOTIFICATION TAPPED ");
+ $rootScope.tappedNotification = 0;
+ $ionicHistory.nextViewOptions(
+ {
+ disableBack: true
+ });
+
+ if (ld.onTapScreen == $translate.instant('kTapMontage'))
+ {
+ NVRDataModel.debug("Going to montage");
+ $state.go("montage",
+ {},
+ {
+ reload: true
+ });
+
+ return;
+ }
+ else if (ld.onTapScreen == $translate.instant('kTapEvents'))
+ {
+ NVRDataModel.debug("Going to events");
+ $state.go("events",
+ {
+ "id": 0,
+ "playEvent": false
+ },
+ {
+ reload: true
+ });
+ return;
+ }
+ else // we go to live
+ {
+ NVRDataModel.debug("Going to live view ");
+ $state.go("monitors",
+ {},
+ {
+ reload: true
+ });
+ return;
+ }
+ }
+
+ }
+
+ function unlock(idVerified)
+ {
/*
idVerified == true means no pin check needed
== false means check PIN
*/
NVRDataModel.debug("unlock called with check PIN=" + idVerified);
- if (idVerified || ($scope.pindata.pin == loginData.pinCode)) {
+ if (idVerified || ($scope.pindata.pin == loginData.pinCode))
+ {
NVRDataModel.debug("PIN code entered is correct, or there is no PIN set");
$rootScope.rand = Math.floor((Math.random() * 100000) + 1);
zmAutoLogin.stop(); //safety
@@ -211,33 +292,40 @@ angular.module('zmApp.controllers').controller('zmApp.PortalLoginCtrl', ['$ionic
// $state.go("login" ,{"wizard": false});
//login was ok, so get API details
NVRDataModel.getAPIversion()
- .then(function(data) {
+ .then(function(data)
+ {
NVRDataModel.log("Got API version: " + data);
$rootScope.apiVersion = data;
var ld = NVRDataModel.getLogin();
- if (NVRDataModel.versionCompare(data, zm.minAppVersion) == -1 && data != "0.0.0") {
+ if (NVRDataModel.versionCompare(data, zm.minAppVersion) == -1 && data != "0.0.0")
+ {
- $state.go('lowversion', {
+ $state.go('lowversion',
+ {
"ver": data
});
return;
}
- if (NVRDataModel.versionCompare(data, zm.recommendedAppVersion) == -1 && data != "0.0.0") {
+ if (NVRDataModel.versionCompare(data, zm.recommendedAppVersion) == -1 && data != "0.0.0")
+ {
// console.log (">>>>>>>>>>>>> HERE AND VERSION SAYS " +NVRDataModel.versionCompare(data, zm.recommendedAppVersion));
//console.log ("GOING TO IMPORTANT");
- $state.go('importantmessage', {
+ $state.go('importantmessage',
+ {
"ver": data
});
return;
}
- if (data == "0.0.0") {
+ if (data == "0.0.0")
+ {
NVRDataModel.log("API getVersion succeeded but returned 0.0.0 " + JSON.stringify(data));
NVRDataModel.displayBanner('error', ['ZoneMinder authentication failed']);
- $state.go("login", {
+ $state.go("login",
+ {
"wizard": false
});
return;
@@ -246,32 +334,41 @@ angular.module('zmApp.controllers').controller('zmApp.PortalLoginCtrl', ['$ionic
// coming here means continue
// console.log (">>>>>>>>>>>>>>>>>>>>>>>>>NEVER");
EventServer.refresh();
+ if ($rootScope.tappedNotification != 1)
+ {
+ console.log ("NOTIFICATION TAPPED INSIDE CHECK IS "+$rootScope.tappedNotification);
+ var statetoGo = $rootScope.lastState ? $rootScope.lastState : 'montage';
+ NVRDataModel.debug("logging state transition");
+ NVRDataModel.debug("Transitioning state to: " +
+ statetoGo + " with param " + JSON.stringify($rootScope.lastStateParam));
+
+ $state.go(statetoGo, $rootScope.lastStateParam);
+ return;
-
-
- var statetoGo = $rootScope.lastState ? $rootScope.lastState : 'montage';
- NVRDataModel.debug("logging state transition");
- NVRDataModel.debug("Transitioning state to: " +
- statetoGo + " with param " + JSON.stringify($rootScope.lastStateParam));
-
-
- $state.go(statetoGo, $rootScope.lastStateParam);
- return;
+ }
+ else
+ evaluateTappedNotification();
+
},
- function(error) { // API Error
+ function(error)
+ { // API Error
NVRDataModel.log("API Error handler: going to login getAPI returned error: " + JSON.stringify(error));
//NVRDataModel.displayBanner('error', ['ZoneMinder authentication failed']);
NVRDataModel.debug("Doing the Aaron Hack after 1 sec....");
- $timeout(function() {
+ $timeout(function()
+ {
tryLoggingSecondTimeHack()
- .then(function success(s) {
+ .then(function success(s)
+ {
NVRDataModel.log("2nd time login hack worked!, nothing to do");
NVRDataModel.getTimeZone();
},
- function error(e) {
- $state.go("login", {
+ function error(e)
+ {
+ $state.go("login",
+ {
"wizard": false
});
});
@@ -281,71 +378,43 @@ angular.module('zmApp.controllers').controller('zmApp.PortalLoginCtrl', ['$ionic
});
-
- if ($rootScope.tappedNotification) {
-
-
- var ld = NVRDataModel.getLogin();
- NVRDataModel.log("Came via push tap. onTapScreen=" + ld.onTapScreen);
- //console.log ("***** NOTIFICATION TAPPED ");
- $rootScope.tappedNotification = 0;
- $ionicHistory.nextViewOptions({
- disableBack: true
- });
-
- if (ld.onTapScreen == $translate.instant('kTapMontage')) {
- NVRDataModel.debug("Going to montage");
- $state.go("montage", {}, {
- reload: true
- });
-
- return;
- } else if (ld.onTapScreen == $translate.instant('kTapEvents')) {
- NVRDataModel.debug("Going to events");
- $state.go("events", {
- "id": 0,
- "playEvent": false
- }, {
- reload: true
- });
- return;
- } else // we go to live
- {
- NVRDataModel.debug("Going to live view ");
- $state.go("monitors", {}, {
- reload: true
- });
- return;
- }
- }
-
+
},
// coming here means auth error
// so go back to login
- function(error) {
+ function(error)
+ {
NVRDataModel.debug("PortalLogin: error authenticating " +
JSON.stringify(error));
- if (!$rootScope.userCancelledAuth) {
+ if (!$rootScope.userCancelledAuth)
+ {
NVRDataModel.displayBanner('error', ['ZoneMinder authentication failed', 'Please check API settings']);
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableAnimate: true,
disableBack: true
});
- $state.go("login", {
+ $state.go("login",
+ {
"wizard": false
});
return;
- } else {
+ }
+ else
+ {
// if user cancelled auth I guess we go to login
$rootScope.userCancelledAuth = false;
- $state.go("login", {
+ $state.go("login",
+ {
"wizard": false
});
return;
}
});
- } else {
+ }
+ else
+ {
$scope.pindata.status = "Invalid PIN";
// wobble the input box on error
@@ -353,7 +422,8 @@ angular.module('zmApp.controllers').controller('zmApp.PortalLoginCtrl', ['$ionic
element.addClass("animated shake")
.one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend',
- function() {
+ function()
+ {
element.removeClass("animated shake");
});
}
@@ -367,6 +437,4 @@ angular.module('zmApp.controllers').controller('zmApp.PortalLoginCtrl', ['$ionic
var loginData;
$ionicSideMenuDelegate.canDragContent(true);
-
-
-}]); \ No newline at end of file
+}]);
diff --git a/www/js/StateCtrl.js b/www/js/StateCtrl.js
index 58f13c4b..4faec748 100644
--- a/www/js/StateCtrl.js
+++ b/www/js/StateCtrl.js
@@ -4,8 +4,9 @@
// controller for State View
-angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup', '$scope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$ionicLoading', '$ionicModal', '$state', '$http', '$rootScope', '$timeout', '$ionicHistory', '$translate', function (
- $ionicPopup, $scope, zm, NVRDataModel, $ionicSideMenuDelegate, $ionicLoading, $ionicModal, $state, $http, $rootScope, $timeout, $ionicHistory, $translate) {
+angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup', '$scope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$ionicLoading', '$ionicModal', '$state', '$http', '$rootScope', '$timeout', '$ionicHistory', '$translate', function(
+ $ionicPopup, $scope, zm, NVRDataModel, $ionicSideMenuDelegate, $ionicLoading, $ionicModal, $state, $http, $rootScope, $timeout, $ionicHistory, $translate)
+{
//----------------------------------------------------------------------
// Controller main
@@ -22,8 +23,6 @@ angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup'
$rootScope.zmPopup = "";
-
-
var loginData = NVRDataModel.getLogin();
var apiRun = loginData.apiurl + "/host/daemonCheck.json";
@@ -39,12 +38,14 @@ angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup'
// Let's stagger this by 500ms each to see if Chrome lets these through
// This may also help if your Apache is not configured to let multiple connections through
- $timeout(function () {
+ $timeout(function()
+ {
NVRDataModel.debug("invoking LoadStatus...");
getLoadStatus();
}, 2000);
- $timeout(function () {
+ $timeout(function()
+ {
NVRDataModel.debug("invoking CurrentState...");
getCurrentState();
}, 4000);
@@ -62,7 +63,8 @@ angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup'
// reset power state on exit as if it is called after we enter another
// state, that effectively overwrites current view power management needs
//------------------------------------------------------------------------
- $scope.$on('$ionicView.enter', function () {
+ $scope.$on('$ionicView.enter', function()
+ {
// console.log("**VIEW ** Montage Ctrl Entered");
NVRDataModel.setAwake(false);
});
@@ -71,19 +73,23 @@ angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup'
// This gets the current run state custom name
// if applicable
//---------------------------------------------------------
- function getCurrentState() {
+ function getCurrentState()
+ {
NVRDataModel.debug("StateCtrl: getting state using " + apiCurrentState);
$http.get(apiCurrentState)
.then(
- function (success) {
+ function(success)
+ {
NVRDataModel.debug("State results: " + JSON.stringify(success));
var customStateArray = success.data.states;
var i = 0;
var found = false;
$scope.allStateNames = [];
- for (i = 0; i < customStateArray.length; i++) {
+ for (i = 0; i < customStateArray.length; i++)
+ {
$scope.allStateNames.push(customStateArray[i].State.Name);
- if (customStateArray[i].State.IsActive == '1') {
+ if (customStateArray[i].State.IsActive == '1')
+ {
$scope.customState = customStateArray[i].State.Name;
found = true;
}
@@ -91,7 +97,8 @@ angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup'
if (!found) $scope.customState = "";
},
- function (error) {
+ function(error)
+ {
NVRDataModel.debug("StateCtrl: Error retrieving state list " + JSON.stringify(error));
$scope.customState = "";
@@ -100,21 +107,25 @@ angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup'
}
-
//----------------------------------------------------------------
// Alarm notification handling
//----------------------------------------------------------------
- $scope.handleAlarms = function () {
+ $scope.handleAlarms = function()
+ {
$rootScope.isAlarm = !$rootScope.isAlarm;
- if (!$rootScope.isAlarm) {
+ if (!$rootScope.isAlarm)
+ {
$rootScope.alarmCount = "0";
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
- $state.go("events", {
+ $state.go("events",
+ {
"id": 0,
- "playEvent":false
- }, {
+ "playEvent": false
+ },
+ {
reload: true
});
}
@@ -123,69 +134,78 @@ angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup'
//---------------------------------------------------------
// Allows the user to select a custom run state
//---------------------------------------------------------
- $scope.selectCustomState = function () {
+ $scope.selectCustomState = function()
+ {
$scope.myopt = {
selectedState: ""
};
//console.log(JSON.stringify($scope.allStateNames));
NVRDataModel.log("List of custom states: " + JSON.stringify($scope.allStateNames));
- $rootScope.zmPopup = $ionicPopup.show({
+ $rootScope.zmPopup = $ionicPopup.show(
+ {
scope: $scope,
template: '<ion-radio-fix ng-repeat="item in allStateNames" ng-value="item" ng-model="myopt.selectedState"> {{item}} </ion-radio-fix>',
-
title: $translate.instant('kSelectRunState'),
subTitle: $translate.instant('kCurrentState') + $scope.customState ? ($translate.instant('kCurrentState') + ": " + $scope.customState) : "",
buttons: [
+ {
+ text: $translate.instant('kButtonCancel'),
+ onTap: function(e)
{
- text: $translate.instant('kButtonCancel'),
- onTap: function (e) {
- return "CANCEL";
- }
+ return "CANCEL";
+ }
- },
+ },
+ {
+ text: $translate.instant('kButtonOk'),
+ onTap: function(e)
{
- text: $translate.instant('kButtonOk'),
- onTap: function (e) {
- return "OK";
+ return "OK";
- }
- }
- ]
+ }
+ }]
});
// It seems invoking a popup within a popup handler
// causes issues. Doing this outside due to that reason
- $rootScope.zmPopup.then(function (res) {
+ $rootScope.zmPopup.then(function(res)
+ {
// console.log("GOT : " + JSON.stringify(res));
- if (res == "OK") {
+ if (res == "OK")
+ {
if ($scope.myopt.selectedState != "")
controlZM($scope.myopt.selectedState);
}
});
};
-
//----------------------------------------------------------------------
// returns disk space in gigs taken up by events
//----------------------------------------------------------------------
- function getDiskStatus() {
+ function getDiskStatus()
+ {
NVRDataModel.debug("StateCtrl/getDiskStatus: " + apiDisk);
$http.get(apiDisk)
.then(
- function (success) {
+ function(success)
+ {
NVRDataModel.debug("StateCtrl/getDiskStatus: success");
NVRDataModel.debug("Disk results: " + JSON.stringify(success));
var obj = success.data.usage;
- if (obj.Total.space != undefined) {
+ if (obj.Total.space != undefined)
+ {
$scope.zmDisk = parseFloat(obj.Total.space).toFixed(1).toString() + "G";
- } else {
+ }
+ else
+ {
$scope.zmDisk = "unknown";
NVRDataModel.log("Error retrieving disk space, API returned null for obj.Total.space");
}
},
- function (error) {
+ function(error)
+ {
$scope.zmDisk = "unknown";
// console.log("ERROR:" + JSON.stringify(error));
NVRDataModel.log("Error retrieving DiskStatus: " + JSON.stringify(error), "error");
@@ -196,14 +216,17 @@ angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup'
//----------------------------------------------------------------------
// returns ZM running status
//----------------------------------------------------------------------
- function getRunStatus() {
+ function getRunStatus()
+ {
NVRDataModel.debug("StateCtrl/getRunStatus: " + apiRun);
$http.get(apiRun)
.then(
- function (success) {
+ function(success)
+ {
NVRDataModel.debug("StateCtrl/getRunStatus: success");
NVRDataModel.debug("Run results: " + JSON.stringify(success));
- switch (success.data.result) {
+ switch (success.data.result)
+ {
case 1:
$scope.zmRun = $translate.instant('kZMRunning');
$scope.color = 'color:green;';
@@ -219,10 +242,10 @@ angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup'
break;
}
-
// console.log("X"+success.data.result+"X");
},
- function (error) {
+ function(error)
+ {
//console.log("ERROR in getRun: " + JSON.stringify(error));
NVRDataModel.log("Error getting RunStatus " + JSON.stringify(error), "error");
$scope.color = 'color:red;';
@@ -232,25 +255,26 @@ angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup'
}
-
//----------------------------------------------------------------------
// gets ZM load - max[0], avg[1], min[2]
//----------------------------------------------------------------------
- function getLoadStatus() {
+ function getLoadStatus()
+ {
NVRDataModel.debug("StateCtrl/getLoadStatus: " + apiLoad);
$http.get(apiLoad)
.then(
- function (success) {
+ function(success)
+ {
NVRDataModel.debug("Load results: " + JSON.stringify(success));
//console.log(JSON.stringify(success));
// load returns 3 params - one in the middle is avg.
NVRDataModel.debug("StateCtrl/getLoadStatus: success");
$scope.zmLoad = success.data.load[1];
-
// console.log("X"+success.data.result+"X");
},
- function (error) {
+ function(error)
+ {
//console.log("ERROR in getLoad: " + JSON.stringify(error));
NVRDataModel.log("Error retrieving loadStatus " + JSON.stringify(error), "error");
$scope.zmLoad = 'undetermined';
@@ -258,17 +282,15 @@ angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup'
);
}
-
//----------------------------------------------------------------------
// start/stop/restart ZM
//----------------------------------------------------------------------
- function performZMoperation(str) {
-
+ function performZMoperation(str)
+ {
NVRDataModel.debug("inside performZMoperation with " + str);
-
$scope.zmRun = "...";
$scope.color = 'color:orange;';
$scope.customState = "";
@@ -276,10 +298,12 @@ angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup'
inProgress = 1;
$http.post(apiExec + str + ".json")
.then(
- function (success) {
+ function(success)
+ {
NVRDataModel.debug("StateCtrl/controlZM: returned success");
inProgress = 0;
- switch (str) {
+ switch (str)
+ {
case "stop":
$scope.zmRun = $translate.instant('kZMStopped');
$scope.color = 'color:red;';
@@ -293,7 +317,8 @@ angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup'
}
},
- function (error) {
+ function(error)
+ {
//if (error.status) // it seems to return error with status 0 if ok
// {
//console.log("ERROR in Change State:" + JSON.stringify(error));
@@ -306,13 +331,17 @@ angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup'
});
}
-
- function controlZM(str) {
- if (inProgress) {
+ function controlZM(str)
+ {
+ if (inProgress)
+ {
NVRDataModel.debug("StateCtrl/controlZM: operation in progress");
- $ionicPopup.alert({
+ $ionicPopup.alert(
+ {
title: $translate.instant('kOperationInProgressTitle'),
- template: $translate.instant('kOperationInProgressBody') + '...'
+ template: $translate.instant('kOperationInProgressBody') + '...',
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
});
return;
}
@@ -320,43 +349,45 @@ angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup'
var statesearch = "startstoprestart";
var promptstring = $translate.instant('kStateAreYouSure') + str + ' Zoneminder?';
- if (statesearch.indexOf(str) == -1) {
+ if (statesearch.indexOf(str) == -1)
+ {
promptstring = "Are you sure you want to change state to " + str;
}
-
- $rootScope.zmPopup = $ionicPopup.show({
- title: 'Please Confirm',
+ $rootScope.zmPopup = $ionicPopup.show(
+ {
+ title: $translate.instant('kPleaseConfirm'),
template: promptstring,
buttons: [
+ {
+ text: $translate.instant('kButtonCancel'),
+ type: 'button-positive'
+ },
+ {
+ text: $translate.instant('kButtonOk'),
+ type: 'button-assertive',
+ onTap: function(e)
{
- text: 'Cancel',
- type: 'button-positive'
- },
- {
- text: 'Yes',
- type: 'button-assertive',
- onTap: function (e) {
- performZMoperation(str);
- }
+ performZMoperation(str);
}
- ]
+ }]
});
-
}
// Binder so template can call controlZM
- $scope.controlZM = function (str) {
+ $scope.controlZM = function(str)
+ {
controlZM(str);
};
-
- $scope.openMenu = function () {
+ $scope.openMenu = function()
+ {
$ionicSideMenuDelegate.toggleLeft();
};
- $scope.$on('$ionicView.leave', function () {
+ $scope.$on('$ionicView.leave', function()
+ {
console.log("**VIEW ** State Ctrl Left");
// FIXME not the best way...
// If the user exits a view before its complete,
@@ -364,8 +395,8 @@ angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup'
inProgress = 0;
});
-
- $scope.doRefresh = function () {
+ $scope.doRefresh = function()
+ {
console.log("***Pull to Refresh");
NVRDataModel.debug("StateCtrl/refresh: calling getRun/Load/Disk/CurrentState");
getRunStatus();
@@ -376,4 +407,4 @@ angular.module('zmApp.controllers').controller('zmApp.StateCtrl', ['$ionicPopup'
};
-}]); \ No newline at end of file
+}]);
diff --git a/www/js/TimelineCtrl.js b/www/js/TimelineCtrl.js
index 523c6d5d..101e7166 100644
--- a/www/js/TimelineCtrl.js
+++ b/www/js/TimelineCtrl.js
@@ -9,40 +9,48 @@
// I've disabled pan and zoom and used buttons instead
// also limits # of items to maxItems
-
// FIXME: too much redundant code between EventCtrl and Timeline
// Move to ModalCtrl and see if it works
-angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPlatform', '$scope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$rootScope', '$http', '$q', 'message', '$state', '$ionicLoading', '$ionicPopover', '$ionicScrollDelegate', '$ionicModal', '$timeout', '$ionicContentBanner', '$ionicHistory', '$sce', '$stateParams', '$translate', '$ionicPopup', '$interval', function($ionicPlatform, $scope, zm, NVRDataModel, $ionicSideMenuDelegate, $rootScope, $http, $q, message, $state, $ionicLoading, $ionicPopover, $ionicScrollDelegate, $ionicModal, $timeout, $ionicContentBanner, $ionicHistory, $sce, $stateParams, $translate, $ionicPopup, $interval) {
+angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPlatform', '$scope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$rootScope', '$http', '$q', 'message', '$state', '$ionicLoading', '$ionicPopover', '$ionicScrollDelegate', '$ionicModal', '$timeout', '$ionicContentBanner', '$ionicHistory', '$sce', '$stateParams', '$translate', '$ionicPopup', '$interval', function($ionicPlatform, $scope, zm, NVRDataModel, $ionicSideMenuDelegate, $rootScope, $http, $q, message, $state, $ionicLoading, $ionicPopover, $ionicScrollDelegate, $ionicModal, $timeout, $ionicContentBanner, $ionicHistory, $sce, $stateParams, $translate, $ionicPopup, $interval)
+{
//console.log("Inside Timeline controller");
- $scope.openMenu = function() {
+ $scope.openMenu = function()
+ {
$ionicSideMenuDelegate.toggleLeft();
};
//---------------------------------------f-------------------------
// Alarm notification handling
//----------------------------------------------------------------
- $scope.handleAlarms = function() {
+ $scope.handleAlarms = function()
+ {
$rootScope.isAlarm = !$rootScope.isAlarm;
- if (!$rootScope.isAlarm) {
+ if (!$rootScope.isAlarm)
+ {
$rootScope.alarmCount = "0";
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
- $state.go("events", {
+ $state.go("events",
+ {
"id": 0,
"playEvent": false
- }, {
+ },
+ {
reload: true
});
return;
}
};
- $scope.leftButtons = [{
+ $scope.leftButtons = [
+ {
type: 'button-icon icon ion-navicon',
- tap: function(e) {
+ tap: function(e)
+ {
$scope.toggleMenu();
}
}];
@@ -50,7 +58,8 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
//-----------------------------------------------------------
// Used to display date range for timeline
//-----------------------------------------------------------
- $scope.prettify = function(str) {
+ $scope.prettify = function(str)
+ {
if (NVRDataModel.getLogin().useLocalTimeZone)
return moment.tz(str, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format('MMMM Do YYYY, ' + NVRDataModel.getTimeFormat());
else
@@ -60,7 +69,8 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
//-----------------------------------------------------------
// used for playback when you tap on a timeline event
//-----------------------------------------------------------
- $scope.calcMsTimer = function(frames, len) {
+ $scope.calcMsTimer = function(frames, len)
+ {
var myframes, mylen;
myframes = parseFloat(frames);
mylen = parseFloat(len);
@@ -69,8 +79,8 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
return (Math.round(1000 / (myframes / mylen)));
};
-
- $scope.toggleMinAlarmFrameCount = function() {
+ $scope.toggleMinAlarmFrameCount = function()
+ {
// console.log("Toggling");
var ld = NVRDataModel.getLogin();
@@ -78,7 +88,6 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
NVRDataModel.setLogin(ld);
-
drawGraph(curFromDate, curToDate, curCount);
};
@@ -86,29 +95,36 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
//-----------------------------------------------------------
// Move/Zoom are used to move the timeline around
//-----------------------------------------------------------
- function move(percentage) {
+ function move(percentage)
+ {
var range = timeline.getWindow();
var interval = range.end - range.start;
- timeline.setWindow({
+ timeline.setWindow(
+ {
start: range.start.valueOf() - interval * percentage,
end: range.end.valueOf() - interval * percentage
});
}
-
+ // helps to navigate to current time quickly
+ // after a night of heavy navigation
+ $scope.gotoNow = function()
+ {
+ timeline.moveTo(timeline.getCurrentTime());
+ };
- $scope.move = function(percentage) {
+ $scope.move = function(percentage)
+ {
move(percentage);
};
-
-
//-----------------------------------------
// Move by X days
//-----------------------------------------
- $scope.moveDays = function(d) {
+ $scope.moveDays = function(d)
+ {
var range = timeline.getWindow();
var ds = moment(range.start);
if (d > 0)
@@ -122,7 +138,6 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
fromDate = ds.format("YYYY-MM-DD HH:mm:ss");
toDate = es.format("YYYY-MM-DD HH:mm:ss");
-
$scope.fromDate = fromDate;
$scope.toDate = toDate;
$rootScope.customTimelineRange = false;
@@ -131,35 +146,34 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
};
-
- function eventDetails(ev) {
+ function eventDetails(ev)
+ {
$scope.event = ev;
- $ionicModal.fromTemplateUrl('templates/timeline-modal.html', {
+ $ionicModal.fromTemplateUrl('templates/timeline-modal.html',
+ {
scope: $scope, // give ModalCtrl access to this scope
animation: 'slide-in-up',
id: 'analyze',
})
- .then(function(modal) {
+ .then(function(modal)
+ {
$scope.modal = modal;
-
-
-
$scope.modal.show();
});
}
-
-
//--------------------------------------------------------
// To show a modal dialog with the event tapped on in timeline
// FIXME : code repeat from Events
//--------------------------------------------------------
- function openModal(event) {
+ function openModal(event)
+ {
- if ($scope.modalFromTimelineIsOpen == true) {
+ if ($scope.modalFromTimelineIsOpen == true)
+ {
// don't know why but some conflict from angular to timeline lib
// results in double modals at times
NVRDataModel.log(">>-- duplicate modal detected, preventing");
@@ -177,15 +191,18 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
//prepareModalEvent(event.Event.Id);
- $ionicModal.fromTemplateUrl('templates/events-modal.html', {
+ $ionicModal.fromTemplateUrl('templates/events-modal.html',
+ {
scope: $scope, // give ModalCtrl access to this scope
animation: 'slide-in-up',
id: 'footage'
})
- .then(function(modal) {
+ .then(function(modal)
+ {
$scope.modal = modal;
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kPleaseWait') + "...",
noBackdrop: true,
duration: 10000,
@@ -196,8 +213,6 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
var ld = NVRDataModel.getLogin();
-
-
});
}
@@ -206,14 +221,16 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
//We need to destroy because we are instantiating
// it on open
//--------------------------------------------------------
- $scope.closeModal = function() {
+ $scope.closeModal = function()
+ {
$scope.modalFromTimelineIsOpen = false;
// $interval.cancel(eventsInterval);
//$interval.cancel(segmentHandle);
NVRDataModel.debug("TimelineCtrl:Close & Destroy Modal");
NVRDataModel.stopNetwork("TimelineCtrl: closeModal");
NVRDataModel.setAwake(false);
- if ($scope.modal !== undefined) {
+ if ($scope.modal !== undefined)
+ {
$scope.modal.remove();
}
@@ -227,11 +244,11 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
};*/
-
//-------------------------------------------------------------------------
// called when user switches to background
//-------------------------------------------------------------------------
- function onPause() {
+ function onPause()
+ {
NVRDataModel.debug("TimelineCtrl:onpause called");
$interval.cancel(updateInterval);
// console.log("*** Moving to Background ***"); // Handle the pause event
@@ -240,37 +257,39 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
}
-
//--------------------------------------------------------
// This function is called by the graph ontapped function
// which in turn calls openModal
//--------------------------------------------------------
- function showEvent(event) {
+ function showEvent(event)
+ {
// in context of angular
- $timeout(function() {
+ $timeout(function()
+ {
openModal(event);
});
}
- $rootScope.$on('tz-updated', function() {
+ $rootScope.$on('tz-updated', function()
+ {
$scope.tzAbbr = NVRDataModel.getTimeZoneNow();
NVRDataModel.debug("Timezone API updated timezone to " + NVRDataModel.getTimeZoneNow());
});
-
-
//-------------------------------------------------
// Make sure we delete the timeline
// This may be redundant as the root view gets
// destroyed but no harm
//-------------------------------------------------
- $scope.$on('$ionicView.leave', function() {
+ $scope.$on('$ionicView.leave', function()
+ {
- if (timeline) {
+ if (timeline)
+ {
$interval.cancel(updateInterval);
timeline.destroy();
console.log("**Destroying Timeline");
@@ -278,7 +297,6 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
}
});
-
/*$scope.$on('$ionicView.enter', function() {
@@ -286,7 +304,8 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
});*/
- $scope.$on('$ionicView.beforeEnter', function() {
+ $scope.$on('$ionicView.beforeEnter', function()
+ {
//$ionicHistory.clearCache();
//$ionicHistory.clearHistory();
@@ -305,16 +324,19 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
// graph range
//-------------------------------------------------
- $scope.$on('$ionicView.afterEnter', function() {
+ $scope.$on('$ionicView.afterEnter', function()
+ {
- $scope.monitors = message;
- console.log("***AFTER ENTER");
+ $scope.monitors = message;
+ //console.log("***AFTER ENTER");
- $scope.follow = { 'time': NVRDataModel.getLogin().followTimeLine };
+ $scope.follow = {
+ 'time': NVRDataModel.getLogin().followTimeLine
+ };
$interval.cancel(updateInterval);
- // Make sure sliding for menu is disabled so it
+ // Make sure sliding for menu is disabled so it
// does not interfere with graph panning
$ionicSideMenuDelegate.canDragContent(false);
var ld = NVRDataModel.getLogin();
@@ -333,11 +355,13 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
$scope.modalFromTimelineIsOpen = false;
//var tempMon = message;
-
// lets timeline.onget the abbreviated version of TZ to display
- if (NVRDataModel.getLogin().useLocalTimeZone) {
+ if (NVRDataModel.getLogin().useLocalTimeZone)
+ {
$scope.tzAbbr = moment().tz(moment.tz.guess()).zoneAbbr();
- } else {
+ }
+ else
+ {
$scope.tzAbbr = moment().tz(NVRDataModel.getTimeZoneNow()).zoneAbbr();
}
@@ -350,61 +374,91 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
$scope.monitors = iMon[0];
} else*/
+ //console.log ("MONITORS:"+JSON.stringify($scope.monitors));
-
-
- console.log ("MONITORS:"+JSON.stringify($scope.monitors));
-
- if ($rootScope.customTimelineRange) {
+ if ($rootScope.customTimelineRange)
+ {
$scope.currentMode = 'custom';
- console.log("***** CUSTOM RANGE");
+ //console.log("***** CUSTOM RANGE");
if (moment($rootScope.fromString).isValid() &&
- moment($rootScope.toString).isValid()) {
+ moment($rootScope.toString).isValid())
+ {
// console.log("FROM & TO IS CUSTOM");
fromDate = $rootScope.fromString;
toDate = $rootScope.toString;
$scope.fromDate = fromDate;
$scope.toDate = toDate;
drawGraph(fromDate, toDate, maxItems);
- } else {
- console.log ("From:"+$rootScope.fromString + " To:"+$rootScope.toString);
- console.log("FROM & TO IS CUSTOM INVALID");
- fromDate = moment().startOf('day').format("YYYY-MM-DD HH:mm:ss");
- toDate = moment().endOf('day').format("YYYY-MM-DD HH:mm:ss");
+ }
+ else
+ {
+ console.log("From:" + $rootScope.fromString + " To:" + $rootScope.toString);
+ //console.log("FROM & TO IS CUSTOM INVALID");
+
+ if (NVRDataModel.getLogin().useLocalTimeZone)
+ {
+ fromDate = moment().startOf('day').format("YYYY-MM-DD HH:mm:ss");
+ toDate = moment().endOf('day').format("YYYY-MM-DD HH:mm:ss");
+ }
+ else
+ {
+ fromDate = moment().tz(NVRDataModel.getTimeZoneNow()).startOf('day').format("YYYY-MM-DD HH:mm:ss");
+ toDate = moment().tz(NVRDataModel.getTimeZoneNow()).endOf('day').format("YYYY-MM-DD HH:mm:ss");
+ }
+
drawGraph(fromDate, toDate, maxItems);
}
- } else {
+ }
+ else
+ {
$scope.currentMode = 'day';
- fromDate = moment().startOf('day').format("YYYY-MM-DD HH:mm:ss");
- toDate = moment().endOf('day').format("YYYY-MM-DD HH:mm:ss");
+
+ if (NVRDataModel.getLogin().useLocalTimeZone)
+ {
+ fromDate = moment().startOf('day').format("YYYY-MM-DD HH:mm:ss");
+ toDate = moment().endOf('day').format("YYYY-MM-DD HH:mm:ss");
+ }
+ else
+ {
+ fromDate = moment().tz(NVRDataModel.getTimeZoneNow()).startOf('day').format("YYYY-MM-DD HH:mm:ss");
+ toDate = moment().tz(NVRDataModel.getTimeZoneNow()).endOf('day').format("YYYY-MM-DD HH:mm:ss");
+ }
drawGraph(fromDate, toDate, maxItems);
}
- $ionicPopover.fromTemplateUrl('templates/timeline-popover.html', {
+ $ionicPopover.fromTemplateUrl('templates/timeline-popover.html',
+ {
scope: $scope,
- }).then(function(popover) {
+ }).then(function(popover)
+ {
$scope.popover = popover;
});
-
// --------------------------------------------------------
// Handling of back button in case modal is open should
// close the modal
// --------------------------------------------------------
- $ionicPlatform.registerBackButtonAction(function(e) {
+ $ionicPlatform.registerBackButtonAction(function(e)
+ {
e.preventDefault();
- if ($scope.modal != undefined && $scope.modal.isShown()) {
+ if ($scope.modal != undefined && $scope.modal.isShown())
+ {
// switch off awake, as liveview is finished
NVRDataModel.debug("Modal is open, closing it");
NVRDataModel.setAwake(false);
$scope.modal.remove();
- } else {
+ }
+ else
+ {
NVRDataModel.debug("Modal is closed, so toggling or exiting");
- if (!$ionicSideMenuDelegate.isOpenLeft()) {
+ if (!$ionicSideMenuDelegate.isOpenLeft())
+ {
$ionicSideMenuDelegate.toggleLeft();
- } else {
+ }
+ else
+ {
navigator.app.exitApp();
}
@@ -412,14 +466,12 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
}, 1000);
-
});
//-------------------------------------------------
// Controller main
//-------------------------------------------------
-
var graphIndex;
var updateInterval;
var lastTimeForEvent;
@@ -436,36 +488,28 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
var curFromDate, curToDate, curCount;
-
document.addEventListener("pause", onPause, false);
// FIXME: Timeline awake to avoid graph redrawing
NVRDataModel.setAwake(NVRDataModel.getKeepAwake());
-
-
-
-
-
// fromDate and toDate will be used to plot the range for the graph
// We start in day mode
- var fromDate = moment().startOf('day').format("YYYY-MM-DD HH:mm:ss");
- var toDate = moment().endOf('day').format("YYYY-MM-DD HH:mm:ss");
-
+ //
+ var fromDate, toDate;
+ fromDate = moment().tz(NVRDataModel.getLogin().useLocalTimeZone ? NVRDataModel.getLocalTimeZoneNow() : NVRDataModel.getTimeZoneNow()).startOf('day').format("YYYY-MM-DD HH:mm:ss");
+ toDate = moment().tz(NVRDataModel.getLogin().useLocalTimeZone ? NVRDataModel.getLocalTimeZoneNow() : NVRDataModel.getTimeZoneNow()).endOf('day').format("YYYY-MM-DD HH:mm:ss");
$scope.fromDate = fromDate;
$scope.toDate = toDate;
-
// maxItems will be ignored during timeline draw if its desktop
var maxItemsConf;
var ld = NVRDataModel.getLogin();
var maxItems;
-
-
//flat colors for graph - https://flatuicolors.com http://www.flatuicolorpicker.com
var colors = ['#3498db', '#E57373', '#EB974E', '#95A5A6', '#e74c3c', '#03C9A9', ];
@@ -473,8 +517,6 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
container = angular.element(document.getElementById('visualization'));
var timeline;
-
-
//console.log ("RETURNING MONITORS " + JSON.stringify($scope.monitors));
//$scope.monitors = message;
@@ -483,8 +525,6 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
$scope.navControls = false;
var navControls = false;
-
-
//drawGraph(fromDate, toDate, maxItems);
//dummyDrawGraph(fromDate, toDate,maxItems);
@@ -492,42 +532,53 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
// Rest graph to sane state after you went
// wild zooming and panning :-)
//-------------------------------------------------
- $scope.fit = function() {
+ $scope.fit = function()
+ {
timeline.fit();
};
- $scope.toggleNav = function() {
- if (navControls == true) {
+ $scope.toggleNav = function()
+ {
+ if (navControls == true)
+ {
navControls = !navControls;
// $scope.navControls = navControls;
// give out animation time
- $timeout(function() {
+ $timeout(function()
+ {
$scope.navControls = navControls;
}, 2000);
- } else {
+ }
+ else
+ {
navControls = !navControls;
$scope.navControls = navControls;
}
var element = angular.element(document.getElementById("timeline-ctrl"));
- if (navControls) {
+ if (navControls)
+ {
element.removeClass("animated bounceOutLeft");
element.addClass("animated bounceInRight");
- } else {
+ }
+ else
+ {
element.removeClass("animated bounceInRight");
element.addClass("animated bounceOutLeft");
}
};
- function shortenTime(str) {
+ function shortenTime(str)
+ {
if (NVRDataModel.getLogin().useLocalTimeZone)
return moment.tz(str, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format(NVRDataModel.getTimeFormat());
else
return moment(str).format(NVRDataModel.getTimeFormat());
}
- $scope.toggleFollowTime = function() {
+ $scope.toggleFollowTime = function()
+ {
/*if ($scope.currentMode != 'day') {
$rootScope.zmPopup = $ionicPopup.alert({
title: $translate.instant('kError'),
@@ -545,8 +596,8 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
// so we can redraw the graph
//-------------------------------------------------
-
- $scope.buttonClicked = function(index) {
+ $scope.buttonClicked = function(index)
+ {
//console.log (index);
if (index == 0) //month
{
@@ -560,30 +611,33 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
$scope.fromDate = fromDate;
$scope.toDate = toDate;
drawGraph(fromDate, toDate, maxItems);
- } else if (index == 1) //week
+ }
+ else if (index == 1) //week
{
$scope.follow.time = NVRDataModel.getLogin().followTimeLine;
$scope.currentMode = "week";
$rootScope.customTimelineRange = false;
NVRDataModel.log("Week view");
- toDate = moment().format("YYYY-MM-DD HH:mm:ss");
- fromDate = moment().subtract(1, 'week').startOf('day').format("YYYY-MM-DD HH:mm:ss");
+ toDate = moment().tz(NVRDataModel.getLogin().useLocalTimeZone ? NVRDataModel.getLocalTimeZoneNow() : NVRDataModel.getTimeZoneNow()).format("YYYY-MM-DD HH:mm:ss");
+ fromDate = moment().tz(NVRDataModel.getLogin().useLocalTimeZone ? NVRDataModel.getLocalTimeZoneNow() : NVRDataModel.getTimeZoneNow()).subtract(1, 'week').startOf('day').format("YYYY-MM-DD HH:mm:ss");
$scope.fromDate = fromDate;
$scope.toDate = toDate;
drawGraph(fromDate, toDate, maxItems);
- } else if (index == 2) //day
+ }
+ else if (index == 2) //day
{
$scope.currentMode = "day";
$rootScope.customTimelineRange = false;
NVRDataModel.log("Day view");
//toDate = moment().format("YYYY-MM-DD HH:mm:ss");
- fromDate = moment().startOf('day').format("YYYY-MM-DD HH:mm:ss");
- toDate = moment().endOf('day').format("YYYY-MM-DD HH:mm:ss");
+ fromDate = moment().tz(NVRDataModel.getLogin().useLocalTimeZone ? NVRDataModel.getLocalTimeZoneNow() : NVRDataModel.getTimeZoneNow()).startOf('day').format("YYYY-MM-DD HH:mm:ss");
+ toDate = moment().tz(NVRDataModel.getLogin().useLocalTimeZone ? NVRDataModel.getLocalTimeZoneNow() : NVRDataModel.getTimeZoneNow()).endOf('day').format("YYYY-MM-DD HH:mm:ss");
$scope.fromDate = fromDate;
$scope.toDate = toDate;
drawGraph(fromDate, toDate, maxItems);
- } else // custom
+ }
+ else // custom
{
$scope.follow.time = NVRDataModel.getLogin().followTimeLine;
$scope.currentMode = "custom";
@@ -594,12 +648,12 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
};
-
/**
* [processNewEvents is called every X seconds when dynamic update is on. X = 10 for now]
* @return {[type]}
*/
- function processNewEvents() {
+ function processNewEvents()
+ {
//safeguard in the event http calls are still going on
if (!$scope.follow.time || isProcessNewEventsWaiting) return;
@@ -607,16 +661,23 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
var ld = NVRDataModel.getLogin();
// check for last 2 minutes to account for late DB updates and what not. 5 mins was likely enough
- var from = moment(lastTimeForEvent).subtract(2, 'minutes').locale('en').format("YYYY-MM-DD HH:mm:ss");
- var to = moment().locale('en').format("YYYY-MM-DD HH:mm:ss");
- lastTimeForEvent = moment();
+ //
+
+ // make sure these are server time
+ var from = moment(lastTimeForEvent).tz(NVRDataModel.getTimeZoneNow());
+ from = from.subtract(2, 'minutes').locale('en').format("YYYY-MM-DD HH:mm:ss");
+
+ var to = moment(lastTimeForEvent).tz(NVRDataModel.getTimeZoneNow());
+ to = to.locale('en').format("YYYY-MM-DD HH:mm:ss");
+
+ lastTimeForEvent = moment().tz(NVRDataModel.getLogin().useLocalTimeZone ? NVRDataModel.getLocalTimeZoneNow() : NVRDataModel.getTimeZoneNow());
// FIXME: totally ignoring event pages - hoping it wont be more than 100 or 150 whatever
// the events per page limit is. Why? laziness.
//
- var completedEvents = ld.apiurl + '/events/index/StartTime >=:' + from;
+ var completedEvents = ld.apiurl + '/events/index/EndTime >=:' + from;
// we can add alarmCount as this is really for completed events
- completedEvents = completedEvents + "/AlarmFrames >=:" + (ld.enableAlarmCount ? ld.minAlarmCount : 0);
+ //completedEvents = completedEvents + "/AlarmFrames >=:" + (ld.enableAlarmCount ? ld.minAlarmCount : 0);
completedEvents = completedEvents + ".json";
@@ -624,11 +685,14 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
// as it turns out various events get stored withn null and never recover
// so, lets limiy to 15 m
//
-
- var st = moment().subtract (10,'minutes').locale('en').format("YYYY-MM-DD HH:mm:ss");
- var ongoingEvents = ld.apiurl + '/events/index/StartTime >=:'+st+'/EndTime =:.json';
+
+ var st = moment(lastTimeForEvent).tz(NVRDataModel.getTimeZoneNow());
+ st = st.subtract(10, 'minutes').locale('en').format("YYYY-MM-DD HH:mm:ss");
+ var ongoingEvents = ld.apiurl + '/events/index/StartTime >=:' + st + '/EndTime =:.json';
//NVRDataModel.debug("Getting incremental events using: " + completedEvents);
+ NVRDataModel.debug("Completed events API:" + completedEvents);
+ NVRDataModel.debug("Ongoing events API:+" + ongoingEvents);
isProcessNewEventsWaiting = true;
@@ -636,75 +700,103 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
var $httpOngoing = $http.get(ongoingEvents);
$q.all([$httpApi, $httpOngoing])
- .then(function(dataarray) {
+ .then(function(dataarray)
+ {
var myevents = dataarray[0].data.events;
- if (dataarray.length > 1) {
+ if (dataarray.length > 1)
+ {
myevents = myevents.concat(dataarray[1].data.events);
-
+
}
$scope.newEvents = '';
var localNewEvents = '';
//console.log ("GOT "+JSON.stringify(data));
- for (var j = 0; j < myevents.length; j++) {
+ for (var j = 0; j < myevents.length; j++)
+ {
- // get rid of the moment js deprecation notice
- myevents[j].Event.StartTime = moment.tz(myevents[j].Event.StartTime, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format('YYYY-MM-DD HH:mm:ss');
+ // these are all in server timezone but no TZ
+
+ myevents[j].Event.StartTime = moment.tz(myevents[j].Event.StartTime, NVRDataModel.getTimeZoneNow()).format('YYYY-MM-DD HH:mm:ss');
+
+ myevents[j].Event.EndTime = moment.tz(myevents[j].Event.EndTime, NVRDataModel.getTimeZoneNow()).format('YYYY-MM-DD HH:mm:ss');
- myevents[j].Event.EndTime = moment.tz(myevents[j].Event.EndTime, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format('YYYY-MM-DD HH:mm:ss');
-
var itm = graphData.get(myevents[j].Event.Id);
- if (itm) {
- console.log(myevents[j].Event.Id + " already exists, updating params");
+ if (itm)
+ {
+ // console.log(myevents[j].Event.Id + " already exists, updating params");
- var content = "<span class='my-vis-font'>" + myevents[j].Event.Notes + " " + $translate.instant('kRecordingProgress') + "</span>";
+ var content = "<span class='my-vis-font'>" + "(" + myevents[j].Event.Id + ")" + myevents[j].Event.Notes + " " + $translate.instant('kRecordingProgress') + "</span>";
var style;
var recordingInProgress = false;
if (moment(myevents[j].Event.EndTime).isValid()) // recording over
{
-
- content = "<span class='my-vis-font'>" + "( <i class='ion-android-notifications'></i>" + myevents[j].Event.AlarmFrames + ") " + myevents[j].Event.Notes + "</span>";
+ //console.log ("EVENT "+myevents[j].Event.Id+" emded at "+myevents[j].Event.EndTime);
+
+ content = "<span class='my-vis-font'>" + "( <i class='ion-android-notifications'></i>" + myevents[j].Event.AlarmFrames + ") " + " (" + myevents[j].Event.Id + ") " + myevents[j].Event.Notes + "</span>";
style = "background-color:" + colors[parseInt(myevents[j].Event.MonitorId) % colors.length] +
";border-color:" + colors[parseInt(myevents[j].Event.MonitorId) % colors.length];
- } else // still recording
+ }
+ else // still recording
{
- myevents[j].Event.EndTime = moment.tz(moment(), NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format('YYYY-MM-DD HH:mm:ss');
+
+ var tze;
+ tze = moment().tz(NVRDataModel.getTimeZoneNow());
+
+ myevents[j].Event.EndTime = tze.format('YYYY-MM-DD HH:mm:ss');
+
+ //console.log ("END TIME = "+ myevents[j].Event.EndTime);
+
style = "background-color:orange";
recordingInProgress = true;
}
- if (moment(options.max).isBefore(moment())) {
- // console.log("Adjusting Range to fit in new event");
- options.max = moment().add('1', 'hours').locale('en').format("YYYY-MM-DD HH:mm:ss");
- timeline.setOptions(options);
- }
- // data.update({id: 2, group: 1});
- // update end time - is it needed to be updated?
- //
-
// right at this point we need to decide if we keep or remove this event
//
- if (ld.enableAlarmCount && ld.minAlarmCount > myevents[j].Event.AlarmFrames && !recordingInProgress) {
+ if (ld.enableAlarmCount && ld.minAlarmCount > myevents[j].Event.AlarmFrames && !recordingInProgress)
+ {
// remove
- NVRDataModel.debug("Removing Event:" + myevents[j].Event.Id + "as it doesn't have" + myevents[j].Event.AlarmFrames + " alarm frames");
+ NVRDataModel.debug("Removing Event:" + myevents[j].Event.Id + "as it doesn't have " + myevents[j].Event.AlarmFrames + " alarm frames");
+ // var old = timeline.getWindow();
graphData.remove(myevents[j].Event.Id);
- } else {
- graphData.update({
+ // timeline.setWindow (old.start, old.end);
+ }
+ else
+ {
+
+ var tzs1, tze1;
+ if (NVRDataModel.getLogin().useLocalTimeZone)
+ {
+ tzs1 = moment.tz(myevents[j].Event.StartTime, NVRDataModel.getTimeZoneNow()).tz(NVRDataModel.getLocalTimeZoneNow());
+ tze1 = moment.tz(myevents[j].Event.EndTime, NVRDataModel.getTimeZoneNow()).tz(NVRDataModel.getLocalTimeZoneNow());
+ }
+ else
+ {
+ tzs1 = moment.tz(myevents[j].Event.StartTime, NVRDataModel.getTimeZoneNow());
+ tze1 = moment.tz(myevents[j].Event.EndTime, NVRDataModel.getTimeZoneNow());
+ }
+
+ //tzs1 = tzs1.format("YYYY-MM-DD HH:mm:ss");
+ //tze1 = tze1.format("YYYY-MM-DD HH:mm:ss");
+
+ NVRDataModel.debug("Updating Event:" + myevents[j].Event.Id + "StartTime:" + tzs1.format() + " EndTime:" + tze1.format());
+ graphData.update(
+ {
id: myevents[j].Event.Id,
content: content,
- start:moment.tz(myevents[j].Event.StartTime,NVRDataModel.getTimeZoneNow()),
- // start: myevents[j].Event.StartTime,
- // end: myevents[j].Event.EndTime,
- end:moment.tz(myevents[j].Event.EndTime,NVRDataModel.getTimeZoneNow()),
- group: myevents[j].Event.MonitorId,
+ start: tzs1,
+ // start: myevents[j].Event.StartTime,
+ // end: myevents[j].Event.EndTime,
+ end: tze1,
+ //group: myevents[j].Event.MonitorId,
//type: "range",
style: style,
myframes: myevents[j].Event.Frames,
@@ -716,36 +808,39 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
});
- timeline.focus(myevents[j].Event.Id);
+ //timeline.focus(myevents[j].Event.Id);
+ //
+ timeline.moveTo(timeline.getCurrentTime());
+ //console.log ("Focus EID="+myevents[j].Event.Id);
localNewEvents = localNewEvents + NVRDataModel.getMonitorName(myevents[j].Event.MonitorId) + '@' + shortenTime(myevents[j].Event.StartTime) + ' (' + myevents[j].Event.Id + '),';
-
}
-
-
- } else { // event is new
+ }
+ else
+ { // event is new
var isBeingRecorded = false;
var idfound = false;
- for (var ii = 0; ii < $scope.monitors.length; ii++) {
- if ($scope.monitors[ii].Monitor.Id == myevents[j].Event.MonitorId && NVRDataModel.isNotHidden(myevents[j].Event.MonitorId)) {
+ for (var ii = 0; ii < $scope.monitors.length; ii++)
+ {
+ if ($scope.monitors[ii].Monitor.Id == myevents[j].Event.MonitorId && NVRDataModel.isNotHidden(myevents[j].Event.MonitorId))
+ {
idfound = true;
break;
}
}
- if (idfound) {
-
-
+ if (idfound)
+ {
myevents[j].Event.MonitorName = NVRDataModel.getMonitorName(myevents[j].Event.MonitorId);
-
myevents[j].Event.streamingURL = NVRDataModel.getStreamingURL(myevents[j].Event.MonitorId);
myevents[j].Event.baseURL = NVRDataModel.getBaseURL(myevents[j].Event.MonitorId);
myevents[j].Event.imageMode = NVRDataModel.getImageMode(myevents[j].Event.MonitorId);
- if (NVRDataModel.getLogin().url != myevents[j].Event.baseURL) {
+ if (NVRDataModel.getLogin().url != myevents[j].Event.baseURL)
+ {
myevents[j].Event.baseURL = NVRDataModel.getLogin().url;
}
@@ -754,13 +849,6 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
// console.log (JSON.stringify(myevents[j]));
myevents[j].Event.DefaultVideo = "";
- if (NVRDataModel.getLogin().useLocalTimeZone) {
- //console.log ("CHANGING TZ");
- myevents[j].Event.StartTime = moment.tz(myevents[j].Event.StartTime, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format('YYYY-MM-DD HH:mm:ss');
- //2016-08-15 17:40:00
- myevents[j].Event.EndTime = moment.tz(myevents[j].Event.EndTime, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format('YYYY-MM-DD HH:mm:ss');
- }
-
// now lets make sure we don't infinitely increase
if (graphIndex >= curCount)
@@ -768,7 +856,8 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
{
var mv = graphData.min('id');
//console.log("MIN="+JSON.stringify(mv));
- if (mv) {
+ if (mv)
+ {
graphData.remove(mv.id);
graphIndex--;
NVRDataModel.debug("Removed Event " + mv.id + " to make space");
@@ -777,49 +866,70 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
}
// since this is a new add its possible dates are not defined
- if (!moment(myevents[j].Event.StartTime).isValid()) {
- NVRDataModel.debug("Event:" + myevents[j].Event.Id + "-Invalid Start time - this should really not happen ");
+ if (!moment(myevents[j].Event.StartTime).isValid())
+ {
+ NVRDataModel.log("Event:" + myevents[j].Event.Id + "-Invalid Start time - this should really not happen ");
}
+ if (!moment(myevents[j].Event.EndTime).isValid())
+ {
+ var t1 = moment().tz(NVRDataModel.getTimeZoneNow());
+
+ myevents[j].Event.EndTime = t1.format('YYYY-MM-DD HH:mm:ss');
+
+ NVRDataModel.debug("Event:" + myevents[j].Event.Id + "-End time is invalid, setting to current time");
- if (!moment(myevents[j].Event.EndTime).isValid()) {
- // NVRDataModel.debug ("Event:" + myevents[j].Event.Id +"-End time is invalid, likely recording, so fixing" );
- myevents[j].Event.EndTime = moment.tz(moment(), NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format('YYYY-MM-DD HH:mm:ss');
isBeingRecorded = true;
}
-
// if range doesn't allow for current time, we need to fix that
- if (moment(options.max).isBefore(moment())) {
+ /*if (moment(options.max).isBefore(moment())) {
// console.log("Adjusting Range to fit in new event");
options.max = moment().add('1', 'hours').locale('en').format("YYYY-MM-DD HH:mm:ss");
timeline.setOptions(options);
- }
+ }*/
var eventText = "<span class='my-vis-font'>" + "( <i class='ion-android-notifications'></i>" + (myevents[j].Event.AlarmFrames || ' unknown ') + ") " + myevents[j].Event.Notes + "</span>";
- if (isBeingRecorded) {
- eventText = "<span class='my-vis-font'>" + myevents[j].Event.Notes + " " + $translate.instant('kRecordingProgress') + "</span>";
+ if (isBeingRecorded)
+ {
+ eventText = "<span class='my-vis-font'>" + "(" + myevents[j].Event.Id + ") " + myevents[j].Event.Notes + " " + $translate.instant('kRecordingProgress') + "</span>";
}
// since we concated, its possible events may be repeated
- if (!graphData.get(myevents[j].Event.Id)) {
- NVRDataModel.debug(">>> "+myevents[j].Event.Id + " at " + myevents[j].Event.StartTime + " New event updating graph");
+ if (!graphData.get(myevents[j].Event.Id))
+ {
localNewEvents = localNewEvents + NVRDataModel.getMonitorName(myevents[j].Event.MonitorId) + '@' + shortenTime(myevents[j].Event.StartTime) + ' (' + myevents[j].Event.Id + '),';
-
-
- graphData.add({
+ var tzs2, tze2;
+ if (NVRDataModel.getLogin().useLocalTimeZone)
+ {
+ tzs2 = moment.tz(myevents[j].Event.StartTime, NVRDataModel.getTimeZoneNow()).tz(NVRDataModel.getTimeZoneNow()).tz(NVRDataModel.getLocalTimeZoneNow());
+ tze2 = moment.tz(myevents[j].Event.EndTime, NVRDataModel.getTimeZoneNow()).tz(NVRDataModel.getLocalTimeZoneNow());
+ }
+ else
+ {
+ tzs2 = moment.tz(myevents[j].Event.StartTime, NVRDataModel.getTimeZoneNow());
+ tze2 = moment.tz(myevents[j].Event.EndTime, NVRDataModel.getTimeZoneNow());
+ }
+
+ //tzs2 = tzs2.format("YYYY-MM-DD HH:mm:ss");
+ //tze2 = tze2.format("YYYY-MM-DD HH:mm:ss");
+
+ NVRDataModel.debug(">>> " + myevents[j].Event.Id + " New event updating graph " + " from:" + tzs2.format() + " to:" + tze2.format());
+
+ graphData.add(
+ {
id: myevents[j].Event.Id,
content: eventText,
- start:moment.tz(myevents[j].Event.StartTime,NVRDataModel.getTimeZoneNow()),
+ start: tzs2,
//start: myevents[j].Event.StartTime,
- // end: myevents[j].Event.EndTime,
- end:moment.tz(myevents[j].Event.EndTime,NVRDataModel.getTimeZoneNow()),
+ // end: myevents[j].Event.EndTime,
+ end: tze2,
group: myevents[j].Event.MonitorId,
style: "background-color:orange",
//type: "range",
@@ -833,21 +943,21 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
});
graphIndex++;
+ //timeline.focus(myevents[j].Event.Id);
+ timeline.moveTo(timeline.getCurrentTime());
}
//options.max = moment(fromDate).locale('en').format("YYYY-MM-DD HH:mm:ss");
- timeline.focus(myevents[j].Event.Id);
-
} //idfound
-
} // new event
} // for j
// At this stage, see if we need to display new events
- if (localNewEvents.length > 0) {
+ if (localNewEvents.length > 0)
+ {
localNewEvents = $translate.instant('kLatestEvents') + ':' + localNewEvents;
localNewEvents = localNewEvents.slice(0, -1);
$scope.newEvents = localNewEvents;
@@ -855,28 +965,25 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
isProcessNewEventsWaiting = false;
},
- function(err) {
+ function(err)
+ {
NVRDataModel.debug("Error getting incremental timeline data");
isProcessNewEventsWaiting = false;
});
-
-
-
// check all events that started 10+10 seconds ago
-
}
-
//-------------------------------------------------
// This function draws the graph
//-------------------------------------------------
- function drawGraph(fromDate, toDate, count) {
+ function drawGraph(fromDate, toDate, count)
+ {
- console.log ("INSIDE DRAW");
+ console.log("INSIDE DRAW");
$scope.newEvents = "";
// we only need this for day mode
@@ -886,7 +993,6 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
curToDate = toDate;
curCount = count;
-
var isFirstItem = true;
var fromDateNoLang = moment(fromDate).locale('en').format("YYYY-MM-DD HH:mm:ss");
@@ -894,7 +1000,8 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
//latestDateDrawn =toDateNoLang;
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kLoadingGraph') + "...",
animation: 'fade-in',
showBackdrop: true,
@@ -907,40 +1014,70 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
$scope.graphLoaded = false;
NVRDataModel.debug("TimelineCtrl/drawgraph: graphLoaded:" + $scope.graphLoaded);
- if (timeline) {
+ if (timeline)
+ {
NVRDataModel.debug("TimelineCtrl/drawgraph: destroying timeline as it exists");
timeline.destroy();
}
-
groups = new vis.DataSet();
graphData = new vis.DataSet();
//console.log ("AFTER VIS");
+ var tzs, tze;
+ // lets scope the time graph to either local or remote time zone
+
+ if (NVRDataModel.getLogin().useLocalTimeZone)
+ {
+ tzs = moment.tz(fromDate, NVRDataModel.getTimeZoneNow()).tz(NVRDataModel.getLocalTimeZoneNow());
+ tze = moment.tz(toDate, NVRDataModel.getTimeZoneNow()).tz(NVRDataModel.getLocalTimeZoneNow());
+ }
+ else
+ {
+ tzs = moment.tz(fromDate, NVRDataModel.getTimeZoneNow());
+ tze = moment.tz(toDate, NVRDataModel.getTimeZoneNow());
+ }
+
+ //tzs = tzs.format("YYYY-MM-DD HH:mm:ss");
+ //tze = tze.format("YYYY-MM-DD HH:mm:ss");
options = {
showCurrentTime: true,
editable: false,
+ moment: function(date)
+ {
+
+ //var t;
+ if (NVRDataModel.getLogin().useLocalTimeZone)
+ //if (0)
+ return moment.tz(date, NVRDataModel.getTimeZoneNow()).tz(NVRDataModel.getLocalTimeZoneNow());
+ else
+ // typecast to server time zone - its in server time anyway
+ return moment.tz(date, NVRDataModel.getTimeZoneNow());
+ },
//throttleRedraw: 100,
moveable: true,
zoomable: true,
selectable: true,
- start: moment.tz(fromDate,NVRDataModel.getTimeZoneNow()),
- end: moment.tz(toDate,NVRDataModel.getTimeZoneNow()),
+ start: tzs,
+ end: tze,
orientation: 'top',
- min: moment.tz(fromDate,NVRDataModel.getTimeZoneNow()),
- max: moment.tz(toDate,NVRDataModel.getTimeZoneNow()),
- zoomMin: 1 * 60 * 1000, // 1 min
+ min: tzs,
+ //max: tze,
+ zoomMin: 5 * 60 * 1000, // 1 min
stack: false,
- format: {
- minorLabels: {
+ format:
+ {
+ minorLabels:
+ {
minute: NVRDataModel.getTimeFormat(),
hour: NVRDataModel.getTimeFormat(),
second: 's',
},
- majorLabels: {
+ majorLabels:
+ {
second: "D MMM " + NVRDataModel.getTimeFormat(),
}
},
@@ -949,11 +1086,11 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
graphIndex = 1; // will be used for graph ID
-
//console.log ("**NOLANG" + fromDateNoLang + " " + toDateNoLang);
NVRDataModel.getEventsPages(0, fromDateNoLang, toDateNoLang)
- .then(function(data) {
+ .then(function(data)
+ {
var pages = data.pageCount || 1;
var itemsPerPage = parseInt(data.limit);
var iterCount;
@@ -967,7 +1104,8 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
// for dynamic binding which was easier, but due to performance reasons
// I am waiting for the full data to load before I draw
var promises = [];
- while ((pages > 0) && (iterCount > 0)) {
+ while ((pages > 0) && (iterCount > 0))
+ {
var promise = NVRDataModel.getEvents(0, pages, "none", fromDateNoLang, toDateNoLang);
promises.push(promise);
pages--;
@@ -976,13 +1114,16 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
}
$q.all(promises)
- .then(function(data) {
+ .then(function(data)
+ {
NVRDataModel.debug("TimelineCtrl/drawgraph: all pages of graph data received");
graphIndex = 0;
NVRDataModel.log("Creating " + $scope.monitors.length + " groups for the graph");
// create groups
- for (var g = 0; g < $scope.monitors.length; g++) {
- groups.add({
+ for (var g = 0; g < $scope.monitors.length; g++)
+ {
+ groups.add(
+ {
id: $scope.monitors[g].Monitor.Id,
//mid: $scope.monitors[g].Monitor.Id,
content: NVRDataModel.getMonitorName($scope.monitors[g].Monitor.Id),
@@ -992,28 +1133,32 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
NVRDataModel.getMonitorName($scope.monitors[g].Monitor.Id));
}
-
-
- for (var j = 0; j < data.length; j++) {
+ for (var j = 0; j < data.length; j++)
+ {
var myevents = data[j];
- if (graphIndex > count) {
+ if (graphIndex > count)
+ {
NVRDataModel.log("Exiting page count graph - reached limit of " + count);
break;
}
- for (var i = 0; i < myevents.length; i++) {
+ for (var i = 0; i < myevents.length; i++)
+ {
// make sure group id exists before adding
var idfound = true;
var ld = NVRDataModel.getLogin();
- if (ld.persistMontageOrder) {
+ if (ld.persistMontageOrder)
+ {
idfound = false;
- for (var ii = 0; ii < $scope.monitors.length; ii++) {
- if ($scope.monitors[ii].Monitor.Id == myevents[i].Event.MonitorId && NVRDataModel.isNotHidden(myevents[i].Event.MonitorId)) {
+ for (var ii = 0; ii < $scope.monitors.length; ii++)
+ {
+ if ($scope.monitors[ii].Monitor.Id == myevents[i].Event.MonitorId && NVRDataModel.isNotHidden(myevents[i].Event.MonitorId))
+ {
idfound = true;
//console.log ("****************** ID MATCH " + graphIndex);
@@ -1028,7 +1173,8 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
myevents[i].Event.streamingURL = NVRDataModel.getStreamingURL(myevents[i].Event.MonitorId);
myevents[i].Event.baseURL = NVRDataModel.getBaseURL(myevents[i].Event.MonitorId);
myevents[i].Event.imageMode = NVRDataModel.getImageMode(myevents[i].Event.MonitorId);
- if (NVRDataModel.getLogin().url != myevents[i].Event.baseURL) {
+ if (NVRDataModel.getLogin().url != myevents[i].Event.baseURL)
+ {
//NVRDataModel.debug ("Multi server, changing base");
myevents[i].Event.baseURL = NVRDataModel.getLogin().url;
@@ -1037,34 +1183,38 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
// console.log ("***** MULTISERVER BASE URL FOR EVENTS " + myevents[i].Event.baseURL);
-
-
-
- if (idfound) {
+ if (idfound)
+ {
if (typeof myevents[i].Event.DefaultVideo === 'undefined')
// console.log (JSON.stringify(myevents[i]));
myevents[i].Event.DefaultVideo = "";
- if (NVRDataModel.getLogin().useLocalTimeZone) {
- //console.log ("CHANGING TZ");
- myevents[i].Event.StartTime = moment.tz(myevents[i].Event.StartTime, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format('YYYY-MM-DD HH:mm:ss');
- //2016-08-15 17:40:00
- myevents[i].Event.EndTime = moment.tz(myevents[i].Event.EndTime, NVRDataModel.getTimeZoneNow()).tz(moment.tz.guess()).format('YYYY-MM-DD HH:mm:ss');
- }
-
+ //console.log ("ADDING "+myevents[i].Event.StartTime+"->"+myevents[i].Event.EndTime);
- console.log ("ADDING "+myevents[i].Event.StartTime+"->"+myevents[i].Event.EndTime);
+ var tzs, tze;
+ if (NVRDataModel.getLogin().useLocalTimeZone)
+ {
+ tzs = moment.tz(myevents[i].Event.StartTime, NVRDataModel.getTimeZoneNow()).tz(NVRDataModel.getLocalTimeZoneNow());
+ tze = moment.tz(myevents[i].Event.EndTime, NVRDataModel.getTimeZoneNow()).tz(NVRDataModel.getLocalTimeZoneNow());
+ }
+ else
+ {
+ tzs = moment.tz(myevents[i].Event.StartTime, NVRDataModel.getTimeZoneNow());
+ tze = moment.tz(myevents[i].Event.EndTime, NVRDataModel.getTimeZoneNow());
+ }
- graphData.add({
+ //console.log ("ADDED "+tzs+" " +tze);
+ graphData.add(
+ {
//id: graphIndex,
id: myevents[i].Event.Id,
- content: "<span class='my-vis-font'>" + "( <i class='ion-android-notifications'></i>" + myevents[i].Event.AlarmFrames + ") " + myevents[i].Event.Notes + "</span>",
+ content: "<span class='my-vis-font'>" + "( <i class='ion-android-notifications'></i>" + myevents[i].Event.AlarmFrames + ") " + "(" + myevents[j].Event.Id + ") " + myevents[i].Event.Notes + "</span>",
- start:moment.tz(myevents[i].Event.StartTime,NVRDataModel.getTimeZoneNow()),
+ start: tzs,
//start: myevents[i].Event.StartTime,
//end: myevents[i].Event.EndTime,
- end:moment.tz(myevents[i].Event.EndTime,NVRDataModel.getTimeZoneNow()),
+ end: tze,
group: myevents[i].Event.MonitorId,
//type: "range",
style: "background-color:" + colors[parseInt(myevents[i].Event.MonitorId) % colors.length] +
@@ -1078,13 +1228,14 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
});
graphIndex++;
- } else {
+ }
+ else
+ {
//console.log ("SKIPPED GRAPH ID " + graphIndex);
}
-
-
- if (graphIndex > count) {
+ if (graphIndex > count)
+ {
NVRDataModel.log("Exiting event graph - reached limit of " + count);
break;
@@ -1093,7 +1244,7 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
}
}
- console.log(">>>>> CREATING NEW TIMELINE with "+JSON.stringify(options));
+ console.log(">>>>> CREATING NEW TIMELINE with " + JSON.stringify(options));
timeline = new vis.Timeline(container[0], null, options);
// console.log ("GRAPH DATA");
timeline.setItems(graphData);
@@ -1103,14 +1254,14 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
if (NVRDataModel.getLogin().timelineScale == -1)
{
- // console.log ("SCALE NOT FOUND");
-
- timeline.fit();
+ // console.log ("SCALE NOT FOUND");
+
+ timeline.fit();
}
else
{
- timeline.fit();
-
+ timeline.fit();
+
/*var d = NVRDataModel.getLogin().timelineScale;
console.log ("SCALE FOUND "+d+" SECONDS");
var w = timeline.getWindow();
@@ -1127,13 +1278,12 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
console.log ("Start="+s+" End="+e);
$timeout (function() {timeline.setWindow(s,e);},1000);*/
-
}
-
- lastTimeForEvent = moment();
- updateInterval = $interval(function() {
+ lastTimeForEvent = moment().tz(NVRDataModel.getLogin().useLocalTimeZone ? NVRDataModel.getLocalTimeZoneNow() : NVRDataModel.getTimeZoneNow());
+ updateInterval = $interval(function()
+ {
processNewEvents();
}.bind(this), 10 * 1000);
@@ -1158,8 +1308,8 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
});*/
- timeline.on('rangechanged', function(s)
- {
+ timeline.on('rangechanged', function(s)
+ {
///console.log ("Range Changed:"+JSON.stringify(s));
if (s.byUser)
{
@@ -1168,24 +1318,25 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
//console.log ("start:"+w.start+" end:"+w.end);
var a = moment(w.start);
var b = moment(w.end);
- var d = b.diff(a,'seconds');
+ var d = b.diff(a, 'seconds');
var ld = NVRDataModel.getLogin();
ld.timelineScale = d;
NVRDataModel.setLogin(ld);
//console.log ("Stored user scale of "+d+" seconds");
}
-
-
- });
-
- timeline.on('click', function(prop) {
+ });
+ timeline.on('click', function(prop)
+ {
- $timeout(function() {
- if (dblclick) {
+ $timeout(function()
+ {
+ if (dblclick)
+ {
//console.log ("IGNORING CLICK AS DBL CLICK");
- $timeout(function() {
+ $timeout(function()
+ {
dblclick = false;
}, 400);
return;
@@ -1197,25 +1348,31 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
// console.log ( "I GOT " + properties);
var itm = prop.item;
//console.log ("ITEM CLICKED " + itm);
- if (itm && !isNaN(itm)) {
+ if (itm && !isNaN(itm))
+ {
NVRDataModel.debug("TimelineCtrl/drawGraph:You clicked on item " + itm);
var item = graphData.get(itm);
NVRDataModel.debug("TimelineCtrl/drawGraph: clicked item details:" + JSON.stringify(item));
showEvent(item.myevent);
-
- } else {
+ }
+ else
+ {
NVRDataModel.debug("exact match not found, guessing item with co-ordinates X=" + prop.x + " group=" + prop.group);
- if (prop.group) {
+ if (prop.group)
+ {
var visible = timeline.getVisibleItems();
NVRDataModel.debug("Visible items=" + JSON.stringify(visible));
var closestItem = null;
var minDist = 99999;
var _item;
- for (var x = 0; x < visible.length; x++) {
+ for (var x = 0; x < visible.length; x++)
+ {
_item = timeline.itemSet.items[x];
- if (_item.data.group == prop.group) {
- if (Math.abs(_item.left - prop.x) < minDist) {
+ if (_item.data.group == prop.group)
+ {
+ if (Math.abs(_item.left - prop.x) < minDist)
+ {
closestItem = _item;
minDist = Math.abs(_item.left - prop.x);
NVRDataModel.debug("Temporary closest " + _item.left);
@@ -1225,13 +1382,17 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
}
- if (closestItem != null) {
+ if (closestItem != null)
+ {
NVRDataModel.log("Closest item " + closestItem.left + " group: " + closestItem.data.group);
showEvent(closestItem.data.myevent);
- } else {
+ }
+ else
+ {
NVRDataModel.log("Did not find a visible item match");
}
- } else // no group row tapped, do nothing
+ }
+ else // no group row tapped, do nothing
{
/*$ionicLoading.show({
@@ -1249,31 +1410,38 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
});
- timeline.on('doubleClick', function(prop) {
+ timeline.on('doubleClick', function(prop)
+ {
//console.log ("DOUBLE");
dblclick = true;
var itm = prop.item;
//console.log ("ITEM CLICKED " + itm);
- if (itm && !isNaN(itm)) {
+ if (itm && !isNaN(itm))
+ {
NVRDataModel.debug("TimelineCtrl/drawGraph:You clicked on item " + itm);
var item = graphData.get(itm);
NVRDataModel.debug("TimelineCtrl/drawGraph: clicked item details:" + JSON.stringify(item));
eventDetails(item.myevent);
-
- } else {
+ }
+ else
+ {
NVRDataModel.debug("exact match not found, guessing item with co-ordinates X=" + prop.x + " group=" + prop.group);
- if (prop.group) {
+ if (prop.group)
+ {
var visible = timeline.getVisibleItems();
NVRDataModel.debug("Visible items=" + JSON.stringify(visible));
var closestItem = null;
var minDist = 99999;
var _item;
- for (var x = 0; x < visible.length; x++) {
+ for (var x = 0; x < visible.length; x++)
+ {
_item = timeline.itemSet.items[x];
- if (_item.data.group == prop.group) {
- if (Math.abs(_item.left - prop.x) < minDist) {
+ if (_item.data.group == prop.group)
+ {
+ if (Math.abs(_item.left - prop.x) < minDist)
+ {
closestItem = _item;
minDist = Math.abs(_item.left - prop.x);
NVRDataModel.debug("Temporary closest " + _item.left);
@@ -1283,10 +1451,13 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
}
NVRDataModel.log("Closest item " + closestItem.left + " group: " + closestItem.data.group);
- if (closestItem != null) {
+ if (closestItem != null)
+ {
NVRDataModel.log("Closest item " + closestItem.left + " group: " + closestItem.data.group);
showEvent(closestItem.data.myevent);
- } else {
+ }
+ else
+ {
NVRDataModel.log("Did not find a visible item match");
}
}
@@ -1296,7 +1467,8 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
});
},
- function(error) {
+ function(error)
+ {
NVRDataModel.displayBanner('error', 'Timeline error', 'Please try again');
}
@@ -1305,7 +1477,6 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
});
}
-
$scope.radialMenuOptions = {
content: '',
//size: 'small',
@@ -1313,19 +1484,23 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
background: '#982112',
isOpen: true,
toggleOnClick: false,
- button: {
+ button:
+ {
cssClass: 'fa fa-compress fa-2x',
size: 'small',
- onclick: function() {
+ onclick: function()
+ {
//console.log("fitting");
timeline.fit();
}
},
- items: [{
+ items: [
+ {
content: '',
cssClass: 'fa fa-minus-circle',
empty: false,
- onclick: function() {
+ onclick: function()
+ {
//zoom(0.2);
timeline.zoomOut(0.2);
}
@@ -1345,16 +1520,18 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
cssClass: 'fa fa-chevron-circle-up',
empty: false,
- onclick: function() {
-
+ onclick: function()
+ {
move(0.2);
}
- }, {
+ },
+ {
content: 'D',
empty: true,
- onclick: function() {
+ onclick: function()
+ {
// console.log('About');
}
},
@@ -1372,7 +1549,8 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
content: '',
cssClass: 'fa fa-plus-circle',
empty: false,
- onclick: function() {
+ onclick: function()
+ {
//zoom(-0.2);
timeline.zoomIn(0.2);
@@ -1391,7 +1569,8 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
{
content: 'H',
empty: true,
- onclick: function() {
+ onclick: function()
+ {
// console.log('About');
}
},
@@ -1400,12 +1579,12 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
content: '',
cssClass: 'fa fa-chevron-circle-up',
empty: false,
- onclick: function() {
+ onclick: function()
+ {
move(-0.2);
}
},
-
{
content: '',
cssClass: 'fa fa-chevron-circle-up',
@@ -1418,15 +1597,12 @@ angular.module('zmApp.controllers').controller('zmApp.TimelineCtrl', ['$ionicPla
{
content: 'K',
empty: true,
- onclick: function() {
+ onclick: function()
+ {
//console.log('About');
}
},
]
};
-
-
-
-
}]);
diff --git a/www/js/TimelineModalCtrl.js b/www/js/TimelineModalCtrl.js
index 6531117e..506c6efa 100644
--- a/www/js/TimelineModalCtrl.js
+++ b/www/js/TimelineModalCtrl.js
@@ -3,13 +3,8 @@
/* jslint browser: true*/
/* global saveAs, cordova,StatusBar,angular,console,ionic, moment, vis , Chart, DJS*/
-
-
-
-angular.module('zmApp.controllers').controller('TimelineModalCtrl', ['$scope', '$rootScope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$q', '$sce', 'carouselUtils', '$ionicPopup', '$translate', function ($scope, $rootScope, zm, NVRDataModel, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $stateParams, $ionicHistory, $ionicScrollDelegate, $q, $sce, carouselUtils, $ionicPopup, $translate) {
-
-
-
+angular.module('zmApp.controllers').controller('TimelineModalCtrl', ['$scope', '$rootScope', 'zm', 'NVRDataModel', '$ionicSideMenuDelegate', '$timeout', '$interval', '$ionicModal', '$ionicLoading', '$http', '$state', '$stateParams', '$ionicHistory', '$ionicScrollDelegate', '$q', '$sce', 'carouselUtils', '$ionicPopup', '$translate', function($scope, $rootScope, zm, NVRDataModel, $ionicSideMenuDelegate, $timeout, $interval, $ionicModal, $ionicLoading, $http, $state, $stateParams, $ionicHistory, $ionicScrollDelegate, $q, $sce, carouselUtils, $ionicPopup, $translate)
+{
var Graph2d;
var tcGraph;
@@ -29,64 +24,68 @@ angular.module('zmApp.controllers').controller('TimelineModalCtrl', ['$scope', '
//$scope.graphType = "all";
$scope.errorDetails = "";
-
-
//----------------------------------------------------------------
// Alarm notification handling
//----------------------------------------------------------------
- $scope.handleAlarms = function () {
+ $scope.handleAlarms = function()
+ {
$rootScope.isAlarm = !$rootScope.isAlarm;
- if (!$rootScope.isAlarm) {
+ if (!$rootScope.isAlarm)
+ {
$rootScope.alarmCount = "0";
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
- $state.go("events", {
+ $state.go("events",
+ {
"id": 0,
- "playEvent":false
- }, {
+ "playEvent": false
+ },
+ {
reload: true
});
return;
}
};
-
-
//-------------------------------------------------------
// we use this to reload the connkey if authkey changed
//------------------------------------------------------
-
- $rootScope.$on("auth-success", function () {
+ $rootScope.$on("auth-success", function()
+ {
NVRDataModel.debug("EventModalCtrl: Re-login detected, resetting everything & re-generating connkey");
-
});
-
- $scope.scrollUp = function () {
+ $scope.scrollUp = function()
+ {
//console.log ("SWIPE UP");
$ionicScrollDelegate.$getByHandle("timeline-modal-delegate").scrollTop(true);
};
- $scope.scrollDown = function () {
+ $scope.scrollDown = function()
+ {
//console.log ("SWIPE DOWN");
$ionicScrollDelegate.$getByHandle("timeline-modal-delegate").scrollBottom(true);
};
+ $scope.switchType = function()
+ {
- $scope.switchType = function () {
-
- if ($scope.graphType == $translate.instant('kGraphAll')) {
+ if ($scope.graphType == $translate.instant('kGraphAll'))
+ {
current_data = onlyalarm_data;
$scope.graphType = $translate.instant('kGraphAlarmed');
NVRDataModel.debug("Alarm array has " + onlyalarm_data.labels.length + " frames");
btype = 'bar';
//console.log (JSON.stringify(onlyalarm_data));
- } else {
+ }
+ else
+ {
current_data = data;
// tcGraph.data =
$scope.graphType = $translate.instant('kGraphAll');
@@ -99,8 +98,8 @@ angular.module('zmApp.controllers').controller('TimelineModalCtrl', ['$scope', '
ld.timelineModalGraphType = $scope.graphType;
NVRDataModel.setLogin(ld);
-
- $timeout(function () {
+ $timeout(function()
+ {
/*
if ($scope.graphType == 'alarmed')
@@ -110,44 +109,44 @@ angular.module('zmApp.controllers').controller('TimelineModalCtrl', ['$scope', '
tcGraph.update();*/
tcGraph.destroy();
console.log("GRAPH TYPE IS " + btype);
- tcGraph = new Chart(ctx, {
+ tcGraph = new Chart(ctx,
+ {
type: btype,
data: current_data,
options: options
});
});
-
-
};
-
//-------------------------------------------------------
// Tapping on a frame shows this image
//------------------------------------------------------
- $scope.showImage = function (p, r, f, fid, e, imode, id) {
+ $scope.showImage = function(p, r, f, fid, e, imode, id)
+ {
var img;
console.log("Image Mode " + imode);
if (imode == 'path')
img = "<img width='100%' ng-src='" + p + "/index.php?view=image&path=" + r + f + "'>";
- else {
+ else
+ {
img = "<img width='100%' ng-src='" + p + "/index.php?view=image&fid=" + id + "'>";
// console.log ("IS MULTISERVER SO IMAGE IS " + img);
}
- $rootScope.zmPopup = $ionicPopup.alert({
+ $rootScope.zmPopup = $ionicPopup.alert(
+ {
title: 'frame:' + fid + '/Event:' + e,
template: img,
- cssClass: 'popup95'
+ cssClass: 'popup95',
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
});
};
-
-
-
-
- $scope.$on('modal.removed', function (e, m) {
+ $scope.$on('modal.removed', function(e, m)
+ {
if (m.id != 'analyze')
return;
@@ -160,7 +159,8 @@ angular.module('zmApp.controllers').controller('TimelineModalCtrl', ['$scope', '
// init drawing here
//------------------------------------------------------
- $scope.$on('modal.shown', function (e, m) {
+ $scope.$on('modal.shown', function(e, m)
+ {
if (m.id != 'analyze')
return;
@@ -171,7 +171,8 @@ angular.module('zmApp.controllers').controller('TimelineModalCtrl', ['$scope', '
$scope.dataReady = false;
NVRDataModel.getKeyConfigParams(0)
- .then(function (data) {
+ .then(function(data)
+ {
//console.log ("***GETKEY: " + JSON.stringify(data));
eventImageDigits = parseInt(data);
NVRDataModel.log("Image padding digits reported as " + eventImageDigits);
@@ -184,41 +185,42 @@ angular.module('zmApp.controllers').controller('TimelineModalCtrl', ['$scope', '
//$scope.eventdetails = JSON.stringify($scope.event);
});
-
//-------------------------------------------------------
// okay, really init drawing here
//------------------------------------------------------
- function processEvent() {
+ function processEvent()
+ {
var eid = $scope.event.Event.Id;
//eid = 22302;
var ld = NVRDataModel.getLogin();
var apiurl = ld.apiurl + "/events/" + eid + ".json";
NVRDataModel.log("Getting " + apiurl);
$http.get(apiurl)
- .then(function (success) {
+ .then(function(success)
+ {
//$scope.eventdetails = JSON.stringify(success);
drawGraphTC(success.data);
},
- function (error) {
+ function(error)
+ {
$scope.errorDetails = $translate.instant('kGraphError');
NVRDataModel.log("Error in timeline frames " + JSON.stringify(error));
});
}
-
//-------------------------------------------------------
// I was kidding, this is where it really is drawn
// scout's promise
//------------------------------------------------------
- function drawGraphTC(event) {
+ function drawGraphTC(event)
+ {
$scope.eid = event.event.Event.Id;
$scope.alarm_images = [];
-
data = {
labels: [],
datasets: [
@@ -239,9 +241,9 @@ angular.module('zmApp.controllers').controller('TimelineModalCtrl', ['$scope', '
data: [],
frames: []
- },
+ },
- ]
+ ]
};
onlyalarm_data = {
@@ -255,24 +257,28 @@ angular.module('zmApp.controllers').controller('TimelineModalCtrl', ['$scope', '
hoverBorderColor: 'rgba(249, 105, 14,1.0)',
data: [],
frames: []
- },
+ },
- ]
+ ]
};
// Chart.js Options
options = {
legend: false,
- scales: {
- yAxes: [{
- ticks: {
+ scales:
+ {
+ yAxes: [
+ {
+ ticks:
+ {
// beginAtZero:true,
min: -1,
},
- }],
- xAxes: [{
+ }],
+ xAxes: [
+ {
display: false
- }]
+ }]
},
responsive: true,
@@ -281,10 +287,11 @@ angular.module('zmApp.controllers').controller('TimelineModalCtrl', ['$scope', '
scaleGridLineColor: "rgba(0,0,0,.05)",
scaleGridLineWidth: 1,
-
- hover: {
+ hover:
+ {
mode: 'single',
- onHover: function (obj) {
+ onHover: function(obj)
+ {
if (obj.length > 0)
tapOrHover(obj[0]._index);
}
@@ -300,13 +307,14 @@ angular.module('zmApp.controllers').controller('TimelineModalCtrl', ['$scope', '
// NVRDataModel.log ("Changing graph width to " + $scope.graphWidth);
- for (var i = 0; i < event.event.Frame.length; i++) {
-
+ for (var i = 0; i < event.event.Frame.length; i++)
+ {
data.labels.push(event.event.Frame[i].TimeStamp);
//data.labels.push(' ');
data.datasets[0].data.push(event.event.Frame[i].Score);
- data.datasets[0].frames.push({
+ data.datasets[0].frames.push(
+ {
x: event.event.Frame[i].TimeStamp,
y: event.event.Frame[i].Score,
eid: event.event.Event.Id,
@@ -319,12 +327,14 @@ angular.module('zmApp.controllers').controller('TimelineModalCtrl', ['$scope', '
});
- if (event.event.Frame[i].Type == "Alarm") {
+ if (event.event.Frame[i].Type == "Alarm")
+ {
onlyalarm_data.labels.push(event.event.Frame[i].TimeStamp);
//data.labels.push(' ');
onlyalarm_data.datasets[0].data.push(event.event.Frame[i].Score);
- onlyalarm_data.datasets[0].frames.push({
+ onlyalarm_data.datasets[0].frames.push(
+ {
x: event.event.Frame[i].TimeStamp,
y: event.event.Frame[i].Score,
eid: event.event.Event.Id,
@@ -345,39 +355,48 @@ angular.module('zmApp.controllers').controller('TimelineModalCtrl', ['$scope', '
cv = document.getElementById("tcchart");
ctx = cv.getContext("2d");
- if (NVRDataModel.getLogin().timelineModalGraphType == $translate.instant('kGraphAll')) {
+ if (NVRDataModel.getLogin().timelineModalGraphType == $translate.instant('kGraphAll'))
+ {
btype = 'line';
current_data = data;
- } else {
+ }
+ else
+ {
btype = 'bar';
current_data = onlyalarm_data;
}
- $timeout(function () {
- tcGraph = new Chart(ctx, {
+ $timeout(function()
+ {
+ tcGraph = new Chart(ctx,
+ {
type: btype,
data: current_data,
options: options
});
});
- cv.onclick = function (e) {
+ cv.onclick = function(e)
+ {
var b = tcGraph.getElementAtEvent(e);
- if (b.length > 0) {
+ if (b.length > 0)
+ {
tapOrHover(b[0]._index);
}
};
}
- function tapOrHover(ndx) {
-
- $timeout(function () {
+ function tapOrHover(ndx)
+ {
+ $timeout(function()
+ {
//console.log ("You tapped " + ndx);
$scope.alarm_images = [];
$scope.playbackURL = $scope.event.Event.baseURL;
var items = current_data.datasets[0].frames[ndx];
- $scope.alarm_images.push({
+ $scope.alarm_images.push(
+ {
relativePath: items.relativePath,
fid: items.fid,
id: items.id,
@@ -390,13 +409,12 @@ angular.module('zmApp.controllers').controller('TimelineModalCtrl', ['$scope', '
}
-
-
//--------------------------------------------------------
// utility function
//--------------------------------------------------------
- function computeRelativePath(event) {
+ function computeRelativePath(event)
+ {
var relativePath = "";
var loginData = NVRDataModel.getLogin();
var str = event.Event.StartTime;
@@ -421,7 +439,8 @@ angular.module('zmApp.controllers').controller('TimelineModalCtrl', ['$scope', '
// utility function
//--------------------------------------------------------
- function computeBasePath(event) {
+ function computeBasePath(event)
+ {
var basePath = "";
var loginData = NVRDataModel.getLogin();
var str = event.Event.StartTime;
@@ -443,32 +462,31 @@ angular.module('zmApp.controllers').controller('TimelineModalCtrl', ['$scope', '
return basePath;
}
-
function humanizeTime(str)
{
return moment.tz(str, NVRDataModel.getTimeZoneNow()).fromNow();
-
-
+
}
- function padToN(number, digits) {
+ function padToN(number, digits)
+ {
var i;
var stringMax = "";
var stringLeading = "";
- for (i = 1; i <= digits; i++) {
+ for (i = 1; i <= digits; i++)
+ {
stringMax = stringMax + "9";
if (i != digits) stringLeading = stringLeading + "0";
}
var numMax = parseInt(stringMax);
- if (number <= numMax) {
+ if (number <= numMax)
+ {
number = (stringLeading + number).slice(-digits);
}
//console.log ("PADTON: returning " + number);
return number;
}
-
-
-}]); \ No newline at end of file
+}]);
diff --git a/www/js/WizardCtrl.js b/www/js/WizardCtrl.js
index 610dfcc5..537a6839 100644
--- a/www/js/WizardCtrl.js
+++ b/www/js/WizardCtrl.js
@@ -2,28 +2,33 @@
/* jslint browser: true*/
/* global cordova,StatusBar,angular,console, Masonry, URI */
-angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$rootScope', '$ionicModal', 'NVRDataModel', '$ionicSideMenuDelegate', '$ionicHistory', '$state', '$ionicPopup', 'SecuredPopups', '$http', '$q', 'zm', '$ionicLoading', 'WizardHandler', '$translate', function ($scope, $rootScope, $ionicModal, NVRDataModel, $ionicSideMenuDelegate, $ionicHistory, $state, $ionicPopup, SecuredPopups, $http, $q, zm, $ionicLoading, WizardHandler, $translate) {
- $scope.openMenu = function () {
+angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$rootScope', '$ionicModal', 'NVRDataModel', '$ionicSideMenuDelegate', '$ionicHistory', '$state', '$ionicPopup', 'SecuredPopups', '$http', '$q', 'zm', '$ionicLoading', 'WizardHandler', '$translate', function($scope, $rootScope, $ionicModal, NVRDataModel, $ionicSideMenuDelegate, $ionicHistory, $state, $ionicPopup, SecuredPopups, $http, $q, zm, $ionicLoading, WizardHandler, $translate)
+{
+ $scope.openMenu = function()
+ {
$ionicSideMenuDelegate.toggleLeft();
};
-
//--------------------------------------------------------------------------
// logs into ZM
//--------------------------------------------------------------------------
- function login(u, zmu, zmp) {
+ function login(u, zmu, zmp)
+ {
var d = $q.defer();
- $http({
+ $http(
+ {
method: 'POST',
//withCredentials: true,
url: u,
- headers: {
+ headers:
+ {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json',
},
- transformRequest: function (obj) {
+ transformRequest: function(obj)
+ {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" +
@@ -32,24 +37,29 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
return params;
},
- data: {
+ data:
+ {
username: zmu,
password: zmp,
action: "login",
view: "console"
}
})
- .success(function (data, status, headers) {
+ .success(function(data, status, headers)
+ {
//console.log("LOOKING FOR " + zm.loginScreenString);
//console.log("DATA RECEIVED " + JSON.stringify(data));
- if (data.indexOf(zm.loginScreenString) == -1) {
+ if (data.indexOf(zm.loginScreenString) == -1)
+ {
$scope.wizard.loginURL = $scope.wizard.fqportal;
$scope.wizard.portalValidText = $translate.instant('kPortal') + ": " + $scope.wizard.loginURL;
$scope.wizard.portalColor = "#16a085";
d.resolve(true);
return d.promise;
- } else {
+ }
+ else
+ {
//console.log("************ERROR");
$scope.wizard.portalValidText = $translate.instant('kPortalDetectionFailed');
$scope.wizard.portalColor = "#e74c3c";
@@ -57,7 +67,8 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
return d.promise;
}
})
- .error(function (error) {
+ .error(function(error)
+ {
//console.log("************ERROR");
$scope.wizard.portalValidText = $translate.instant('kPortalDetectionFailed');
$scope.wizard.portalColor = "#e74c3c";
@@ -75,38 +86,47 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
// monitors configured, cgi-bin won't work
//--------------------------------------------------------------------------
- function getFirstMonitor() {
+ function getFirstMonitor()
+ {
var d = $q.defer();
$http.get($scope.wizard.apiURL + "/monitors.json")
- .then(function (success) {
+ .then(function(success)
+ {
// console.log("getfirst monitor success: " + JSON.stringify(success));
- if (success.data.monitors.length > 0) {
+ if (success.data.monitors.length > 0)
+ {
var foundMid = -1;
- for (var i = 0; i < success.data.monitors.length; i++) {
+ for (var i = 0; i < success.data.monitors.length; i++)
+ {
if (success.data.monitors[i].Monitor.Function != 'None' &&
- success.data.monitors[i].Monitor.Enabled == '1') {
+ success.data.monitors[i].Monitor.Enabled == '1')
+ {
foundMid = success.data.monitors[i].Monitor.Id;
break;
}
}
- if (foundMid != -1) {
+ if (foundMid != -1)
+ {
NVRDataModel.debug("zmWizard - getFirstMonitor returned " + foundMid);
d.resolve(foundMid);
return d.promise;
- } else {
+ }
+ else
+ {
d.reject(false);
return d.promise;
}
-
-
- } else {
+ }
+ else
+ {
d.reject(false);
return d.promise;
}
},
- function (error) {
+ function(error)
+ {
//console.log("getfirst monitor error: " + JSON.stringify(error));
d.reject(false);
return d.promise;
@@ -120,22 +140,28 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
// through multiple options - not the same as fallback
//--------------------------------------------------------------------------
- function findFirstReachableUrl(urls, tail) {
+ function findFirstReachableUrl(urls, tail)
+ {
var d = $q.defer();
- if (urls.length > 0) {
+ if (urls.length > 0)
+ {
var t = "";
if (tail) t = tail;
//$ionicLoading.show({template: 'trying ' + urls[0].server});
NVRDataModel.log("zmWizard test.." + urls[0] + t);
- return $http.get(urls[0] + t).then(function () {
+ return $http.get(urls[0] + t).then(function()
+ {
NVRDataModel.log("Success: on " + urls[0] + t);
//$ionicLoading.hide();
return urls[0];
- }, function (err) {
+ }, function(err)
+ {
NVRDataModel.log("zmWizard:Failed on " + urls[0] + t + " with error " + JSON.stringify(err));
return findFirstReachableUrl(urls.slice(1), tail);
});
- } else {
+ }
+ else
+ {
// $ionicLoading.hide();
NVRDataModel.log("zmWizard: findFirst returned no success");
d.reject("No reachable URL");
@@ -151,7 +177,8 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
// removes proto scheme from string
//--------------------------------------------------------------------------
- function stripProto(u) {
+ function stripProto(u)
+ {
if (u.indexOf('://') != -1)
return u.substr(u.indexOf('://') + 3);
else
@@ -162,7 +189,8 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
// tries to detect cgi-bin
//--------------------------------------------------------------------------
- function detectcgi() {
+ function detectcgi()
+ {
var d = $q.defer();
var c = URI.parse($scope.wizard.loginURL);
var p1, p2;
@@ -184,12 +212,11 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
var a2 = baseUri + "/cgi-bin-zm"; //fedora/centos/rhel
var a1 = baseUri + "/cgi-bin"; // doofus
-
- var urls = [a1, a2, a3, a4,a5];
-
+ var urls = [a1, a2, a3, a4, a5];
NVRDataModel.getPathZms() // what does ZM have stored in PATH_ZMS?
- .then(function (data) {
+ .then(function(data)
+ {
// remove zms or nph-zms
var path = data.trim();
path = path.replace("/nph-zms", "");
@@ -198,58 +225,71 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
NVRDataModel.log("zmWizard: getPathZMS succeeded, adding " + baseUri + path + " to things to try");
continueCgi(urls);
},
- function (error) {
+ function(error)
+ {
NVRDataModel.log("zmWizard: getPathZMS failed, but continuing...");
continueCgi(urls);
});
// Well, PATH_ZMS or not, lets call this function and brute force it
- function continueCgi(urls) {
- $ionicLoading.show({
+ function continueCgi(urls)
+ {
+ $ionicLoading.show(
+ {
template: $translate.instant('kDiscovering') + "...",
noBackdrop: true,
duration: zm.httpTimeout
});
getFirstMonitor()
- .then(function (success) {
+ .then(function(success)
+ {
$ionicLoading.hide();
var tail = "/nph-zms?mode=single&monitor=" + success;
- if ($scope.wizard.useauth && $scope.wizard.usezmauth) {
+ if ($scope.wizard.useauth && $scope.wizard.usezmauth)
+ {
var ck = Math.floor(Math.random() * (50000 - 10000 + 1)) + 10000;
NVRDataModel.getAuthKey(success, ck)
- .then(function (success) {
- if (success == "") {
+ .then(function(success)
+ {
+ if (success == "")
+ {
NVRDataModel.log("getAuthKey returned null, so going user=&pwd= way");
tail += "&user=" + $scope.wizard.zmuser + "&pass=" + $scope.wizard.zmpassword;
- } else {
+ }
+ else
+ {
tail += success;
}
NVRDataModel.log("auth computed is : " + tail);
proceedwithCgiAfterAuth(urls, tail);
},
- function (error) {
+ function(error)
+ {
NVRDataModel.log("Should never come here, getAuthKey doesn't return error");
});
-
//console.log ("****CDING " + tail);
- } else // no auth case
+ }
+ else // no auth case
{
proceedwithCgiAfterAuth(urls, tail);
}
- function proceedwithCgiAfterAuth(urls, tail) {
+ function proceedwithCgiAfterAuth(urls, tail)
+ {
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kDiscovering') + "...",
noBackdrop: true,
duration: zm.httpTimeout
});
findFirstReachableUrl(urls, tail)
- .then(function (success) {
+ .then(function(success)
+ {
$ionicLoading.hide();
NVRDataModel.log("Valid cgi-bin found with: " + success);
$scope.wizard.streamingURL = success;
@@ -259,7 +299,8 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
return d.promise;
},
- function (error) {
+ function(error)
+ {
$ionicLoading.hide();
//console.log("No cgi-bin found: " + error);
$scope.wizard.streamingValidText = $translate.instant('kPortalCgiBinFailed');
@@ -269,7 +310,8 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
});
}
},
- function (error) {
+ function(error)
+ {
$ionicLoading.hide();
$scope.wizard.streamingValidText = $translate.instant('kPortalCgiBinFailed') + " -" + $translate.instant('kPortalNoMonitorFound');
$scope.wizard.streamingColor = "#e74c3c";
@@ -285,12 +327,12 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
}
-
//--------------------------------------------------------------------------
// Finds an appropriate API to use
//--------------------------------------------------------------------------
- function detectapi() {
+ function detectapi()
+ {
var u = $scope.wizard.loginURL;
var d = $q.defer();
var api1 = u + "/api";
@@ -304,24 +346,22 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
if (c.port) api2 += ":" + c.port;
api2 += "/api";
-
-
// lets try both /zm/api and /api. What else is there?
var apilist = [api1, api2, api3];
findFirstReachableUrl(apilist, '/host/getVersion.json')
- .then(function (success) {
+ .then(function(success)
+ {
NVRDataModel.log("Valid API response found with:" + success);
$scope.wizard.apiURL = success;
-
-
$scope.wizard.apiValidText = "API: " + $scope.wizard.apiURL;
$scope.wizard.apiColor = "#16a085";
d.resolve(true);
return d.promise;
},
- function (error) {
+ function(error)
+ {
//console.log("No APIs found: " + error);
$scope.wizard.apiValidText = $translate.instant('kPortalAPIFailed');
$scope.wizard.apiColor = "#e74c3c";
@@ -336,18 +376,21 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
// logs out of ZM
//--------------------------------------------------------------------------
-
- function logout(u) {
+ function logout(u)
+ {
var d = $q.defer();
- $http({
+ $http(
+ {
method: 'POST',
url: u,
- headers: {
+ headers:
+ {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json',
},
- transformRequest: function (obj) {
+ transformRequest: function(obj)
+ {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" +
@@ -356,23 +399,25 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
return params;
},
- data: {
+ data:
+ {
action: "logout",
view: "login"
}
})
- .then(function (success) {
+ .then(function(success)
+ {
$rootScope.zmCookie = "";
//console.log("ZMlogout success, cookie removed");
d.resolve(true);
return d.promise;
- }, function (error) {
+ }, function(error)
+ {
//console.log("ZMlogout success");
d.resolve(true);
return d.promise;
});
-
return d.promise;
}
@@ -382,7 +427,8 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
// get back to it
//--------------------------------------------------------------------------
- $scope.enterResults = function () {
+ $scope.enterResults = function()
+ {
$scope.portalValidText = "";
$scope.apiValidateText = "";
$scope.streamingValidateText = "";
@@ -393,7 +439,8 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
// tries to log into the portal and then discover api and cgi-bin
//--------------------------------------------------------------------------
- function validateData() {
+ function validateData()
+ {
$rootScope.authSession = 'undefined';
$rootScope.zmCookie = '';
@@ -415,7 +462,8 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
$scope.wizard.serverName += "-" + c.port;
var b = "";
- if ($scope.wizard.useauth && $scope.wizard.usebasicauth) {
+ if ($scope.wizard.useauth && $scope.wizard.usebasicauth)
+ {
b = $scope.wizard.basicuser + ":" + $scope.wizard.basicpassword + "@";
//console.log("B=" + b);
}
@@ -423,8 +471,8 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
if (c.port) u += ":" + c.port;
if (c.path) u += c.path;
-
- if (u.slice(-1) == '/') {
+ if (u.slice(-1) == '/')
+ {
u = u.slice(0, -1);
}
@@ -438,7 +486,8 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
var zmu = "x";
var zmp = "x";
- if ($scope.wizard.usezmauth) {
+ if ($scope.wizard.usezmauth)
+ {
zmu = $scope.wizard.zmuser;
zmp = $scope.wizard.zmpassword;
}
@@ -446,59 +495,69 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
// logout first for the adventurers amongst us who must
// use it even after logging in
NVRDataModel.log("zmWizard: logging out");
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kCleaningUp') + "...",
noBackdrop: true,
duration: zm.httpTimeout
});
logout(u)
- .then(function (ans) {
+ .then(function(ans)
+ {
// login now
$ionicLoading.hide();
NVRDataModel.log("zmWizard: logging in with " + u + " " + zmu);
// The logic will be:
// Login then do an api detect and cgi-detect together
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kDiscoveringPortal') + "...",
noBackdrop: true,
duration: zm.httpTimeout
});
login(u, zmu, zmp)
- .then(function (success) {
+ .then(function(success)
+ {
$ionicLoading.hide();
NVRDataModel.log("zmWizard: login succeeded");
// API Detection
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kDiscoveringAPI') + "...",
noBackdrop: true,
duration: zm.httpTimeout
});
detectapi()
- .then(function (success) {
+ .then(function(success)
+ {
$ionicLoading.hide();
NVRDataModel.log("zmWizard: API succeeded");
- $ionicLoading.show({
+ $ionicLoading.show(
+ {
template: $translate.instant('kDiscoveringCGI') + "...",
noBackdrop: true,
duration: zm.httpTimeout
});
// CGI detection
detectcgi()
- .then(function (success) {
+ .then(function(success)
+ {
$ionicLoading.hide();
// return true here because we want to progress
return d.resolve(true);
},
- function (error) {
+ function(error)
+ {
$ionicLoading.hide();
// return true here because we want to progress
return d.resolve(true);
});
},
- function (error) {
+ function(error)
+ {
$ionicLoading.hide();
NVRDataModel.log("zmWizard: api failed");
@@ -506,11 +565,11 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
return d.resolve(true);
});
-
},
// if login failed, don't progress in the wizard
- function (error) {
+ function(error)
+ {
$ionicLoading.hide();
NVRDataModel.log("zmWizard: login failed");
$scope.wizard.portalValidText = $translate.instant('kPortalLoginUnsuccessful');
@@ -519,49 +578,57 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
});
-
}); //finally
return d.promise;
}
-
//--------------------------------------------------------------------------
// checks for a protocol
//--------------------------------------------------------------------------
- function checkscheme(url) {
+ function checkscheme(url)
+ {
- if ((!/^(f|ht)tps?:\/\//i.test(url)) && (url != "")) {
+ if ((!/^(f|ht)tps?:\/\//i.test(url)) && (url != ""))
+ {
return false;
- } else
+ }
+ else
return true;
}
-
-
//--------------------------------------------------------------------------
// exit validator for auth wizard
//--------------------------------------------------------------------------
- $scope.exitAuth = function () {
+ $scope.exitAuth = function()
+ {
NVRDataModel.log("Wizard: validating auth syntax");
- if ($scope.wizard.useauth) {
- if (!$scope.wizard.usezmauth && !$scope.wizard.usebasicauth) {
- $rootScope.zmPopup = SecuredPopups.show('show', {
+ if ($scope.wizard.useauth)
+ {
+ if (!$scope.wizard.usezmauth && !$scope.wizard.usebasicauth)
+ {
+ $rootScope.zmPopup = SecuredPopups.show('show',
+ {
title: $translate.instant('kError'),
template: $translate.instant('kOneAuth'),
- buttons: [{
+ buttons: [
+ {
text: $translate.instant('kButtonOk')
}]
});
return false;
}
- if ($scope.wizard.usezmauth) {
- if ((!$scope.wizard.zmuser) || (!$scope.wizard.zmpassword)) {
- $rootScope.zmPopup = SecuredPopups.show('show', {
+ if ($scope.wizard.usezmauth)
+ {
+ if ((!$scope.wizard.zmuser) || (!$scope.wizard.zmpassword))
+ {
+ $rootScope.zmPopup = SecuredPopups.show('show',
+ {
title: $translate.instant('kError'),
template: $translate.instant('kValidNameZMAuth'),
- buttons: [{
+ buttons: [
+ {
text: $translate.instant('kButtonOk')
}]
@@ -570,12 +637,16 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
}
}
- if ($scope.wizard.usebasicauth) {
- if ((!$scope.wizard.basicuser) || (!$scope.wizard.basicpassword)) {
- $rootScope.zmPopup = SecuredPopups.show('show', {
+ if ($scope.wizard.usebasicauth)
+ {
+ if ((!$scope.wizard.basicuser) || (!$scope.wizard.basicpassword))
+ {
+ $rootScope.zmPopup = SecuredPopups.show('show',
+ {
title: $translate.instant('kError'),
template: $translate.instant('kValidNameBasicAuth'),
- buttons: [{
+ buttons: [
+ {
text: $translate.instant('kButtonOk')
}]
@@ -596,14 +667,18 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
// validator for portal url wizard
//--------------------------------------------------------------------------
- $scope.exitPortal = function () {
+ $scope.exitPortal = function()
+ {
NVRDataModel.log("Wizard: validating portal url syntax");
- if (!$scope.wizard.portalurl) {
- $rootScope.zmPopup = SecuredPopups.show('show', {
+ if (!$scope.wizard.portalurl)
+ {
+ $rootScope.zmPopup = SecuredPopups.show('show',
+ {
title: $translate.instant('kError'),
template: $translate.instant('kPortalEmpty'),
- buttons: [{
+ buttons: [
+ {
text: $translate.instant('kButtonOk')
}]
@@ -611,12 +686,15 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
return false;
}
- if (!checkscheme($scope.wizard.portalurl)) {
+ if (!checkscheme($scope.wizard.portalurl))
+ {
- $scope.portalproto = [{
+ $scope.portalproto = [
+ {
text: "http",
value: "http://"
- }, {
+ },
+ {
text: "https",
value: "https://"
}];
@@ -624,15 +702,16 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
proto: ""
};
-
-
- $rootScope.zmPopup = $ionicPopup.show({
+ $rootScope.zmPopup = $ionicPopup.show(
+ {
title: $translate.instant('kPortalNoProto'),
scope: $scope,
template: $translate.instant('kPortalPleaseSelect') + ': <ion-radio-fix ng-repeat="item in portalproto" ng-value="item.value" ng-model="myproto.proto">{{item.text}}</ion-radio-fix>',
- buttons: [{
+ buttons: [
+ {
text: $translate.instant('kButtonOk'),
- onTap: function (e) {
+ onTap: function(e)
+ {
NVRDataModel.debug("Protocol selected:" + $scope.myproto.proto);
$scope.wizard.portalurl = $scope.myproto.proto + stripProto($scope.wizard.portalurl);
}
@@ -649,11 +728,14 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
var c = URI.parse($scope.wizard.portalurl);
- if (!c.scheme) {
- $rootScope.zmPopup = SecuredPopups.show('show', {
+ if (!c.scheme)
+ {
+ $rootScope.zmPopup = SecuredPopups.show('show',
+ {
title: $translate.instant('kError'),
template: $translate.instant('kPortalInvalidUrl'),
- buttons: [{
+ buttons: [
+ {
text: $translate.instant('kButtonOk')
}]
@@ -661,7 +743,6 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
return false;
}
-
if (c.userinfo) // basic auth stuff in here, take it out and put it into the next screen
{
$scope.wizard.useauth = true;
@@ -682,19 +763,21 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
//--------------------------------------------------------------------------
// part of auth wizard - toggles display of auth components
//--------------------------------------------------------------------------
- $scope.toggleAuth = function () {
+ $scope.toggleAuth = function()
+ {
- if (!$scope.wizard.useauth) {
+ if (!$scope.wizard.useauth)
+ {
$scope.wizard.usebasicauth = false;
$scope.wizard.usezmauth = false;
}
};
-
//--------------------------------------------------------------------------
// global tip toggler for all wizard steps
//--------------------------------------------------------------------------
- $scope.toggleTip = function () {
+ $scope.toggleTip = function()
+ {
$scope.wizard.tipshow = !$scope.wizard.tipshow;
if ($scope.wizard.tipshow)
$scope.wizard.tiptext = $translate.instant('kHideTip');
@@ -702,12 +785,15 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
$scope.wizard.tiptext = $translate.instant('kShowTip');
};
- $scope.gotoLoginState = function () {
+ $scope.gotoLoginState = function()
+ {
$rootScope.wizard = angular.copy($scope.wizard);
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableBack: true
});
- $state.go("login", {
+ $state.go("login",
+ {
"wizard": true
});
return;
@@ -716,7 +802,8 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
//--------------------------------------------------------------------------
// initial
//--------------------------------------------------------------------------
- $scope.$on('$ionicView.beforeEnter', function () {
+ $scope.$on('$ionicView.beforeEnter', function()
+ {
//console.log("**VIEW ** Help Ctrl Entered");
var monId = -1;
@@ -744,9 +831,8 @@ angular.module('zmApp.controllers').controller('zmApp.WizardCtrl', ['$scope', '$
streamingColor: "",
serverName: "",
-
};
});
-}]); \ No newline at end of file
+}]);
diff --git a/www/js/app.js b/www/js/app.js
index 660eace4..7027ff42 100644
--- a/www/js/app.js
+++ b/www/js/app.js
@@ -1,6 +1,6 @@
/* jshint -W041, -W093 */
/* jslint browser: true*/
-/* global cordova,StatusBar,angular,console,alert,PushNotification, moment ,ionic, URI,Packery, ConnectSDK, CryptoJS, ContactFindOptions, localforage,$, Connection, MobileAccessibility */
+/* global cordova,StatusBar,angular,console,alert,PushNotification, moment ,ionic, URI,Packery, ConnectSDK, CryptoJS, ContactFindOptions, localforage,$, Connection, MobileAccessibility, hello */
// For desktop versions, this is replaced
// with actual app version from config.xml by the
@@ -11,36 +11,33 @@
var appVersion = "0.0.0";
-
-
// core app start stuff
angular.module('zmApp', [
- 'ionic',
- 'ion-datetime-picker',
- 'ngIOS9UIWebViewPatch',
- 'zmApp.controllers',
- 'fileLogger',
- 'angular-carousel',
- 'angularAwesomeSlider',
- 'com.2fdevs.videogular',
- 'com.2fdevs.videogular.plugins.controls',
- 'com.2fdevs.videogular.plugins.overlayplay',
- 'ionic-native-transitions',
- 'mgo-angular-wizard',
- 'pascalprecht.translate',
- 'jett.ionic.scroll.sista'
-
-
-
+ 'ionic',
+ 'ion-datetime-picker',
+ 'ngIOS9UIWebViewPatch',
+ 'zmApp.controllers',
+ 'fileLogger',
+ 'angular-carousel',
+ 'angularAwesomeSlider',
+ 'com.2fdevs.videogular',
+ 'com.2fdevs.videogular.plugins.controls',
+ 'com.2fdevs.videogular.plugins.overlayplay',
+ 'ionic-native-transitions',
+ 'mgo-angular-wizard',
+ 'pascalprecht.translate',
+ 'jett.ionic.scroll.sista',
+ 'uk.ac.soton.ecs.videogular.plugins.cuepoints'
- ])
+])
// ------------------------------------------
// Various constants central repository
// Feel free to change them as you see fit
//------------------------------------------------
-.constant('zm', {
+.constant('zm',
+{
minAppVersion: '1.28.107', // if ZM is less than this, the app won't work
recommendedAppVersion: '1.29',
minEventServerVersion: '0.9',
@@ -75,13 +72,13 @@ angular.module('zmApp', [
monitorRunningColor: '#4CAF50',
monitorErrorColor: '#795548',
montageScaleFrequency: 300,
- eventsListDetailsHeight: 200.0,
- eventsListScrubHeight: 300,
+ eventsListDetailsHeight: 230.0,
+ eventsListScrubHeight: 330,
loginScreenString: "var currentView = 'login'", // Isn't there a better way?
desktopUrl: "/zm",
desktopApiUrl: "/api/zm",
latestRelease: "https://api.github.com/repos/pliablepixels/zmNinja/releases/latest",
- blogUrl: "http://pliablepixels.github.io/feed.json",
+ blogUrl: "https://medium.com/zmninja/latest?format=json",
nphSwitchTimer: 3000,
eventHistoryTimer: 5000,
eventPlaybackQuery: 3000,
@@ -96,23 +93,29 @@ angular.module('zmApp', [
eventSingleImageQualityLowBW: 70,
monSingleImageQualityLowBW: 70,
montageQualityLowBW: 50,
- eventMontageQualityLowBW: 50
-
+ eventMontageQualityLowBW: 50,
+ maxGifCount:60,
+ maxGifCount2:100,
+ maxGifWidth:800.0,
+ quantSample:15,
})
-
// filter for montage iteration
-.filter('onlyEnabled', function () {
+.filter('onlyEnabled', function()
+{
// Create the return function and set the required parameter name to **input**
- return function (input) {
+ return function(input)
+ {
var out = [];
- angular.forEach(input, function (item) {
+ angular.forEach(input, function(item)
+ {
- if ((item.Monitor.Function != 'None') && (item.Monitor.Enabled != '0')) {
+ if ((item.Monitor.Function != 'None') && (item.Monitor.Enabled != '0'))
+ {
out.push(item);
}
@@ -124,16 +127,20 @@ angular.module('zmApp', [
})
// filter for EH iteration
-.filter('onlyEnabledAndEventHas', function () {
+.filter('onlyEnabledAndEventHas', function()
+{
// Create the return function and set the required parameter name to **input**
- return function (input) {
+ return function(input)
+ {
var out = [];
- angular.forEach(input, function (item) {
+ angular.forEach(input, function(item)
+ {
- if ((item.Monitor.Function != 'None') && (item.Monitor.Enabled != '0') && (item.Monitor.eventUrl != 'img/noevent.png')) {
+ if ((item.Monitor.Function != 'None') && (item.Monitor.Enabled != '0') && (item.Monitor.eventUrl != 'img/noevent.png'))
+ {
out.push(item);
}
@@ -144,20 +151,23 @@ angular.module('zmApp', [
})
-
// credit https://gist.github.com/Zren/beaafd64f395e23f4604
-.directive('mouseWheelScroll', function ($timeout) {
+.directive('mouseWheelScroll', function($timeout)
+{
return {
restrict: 'A',
- link: function ($scope, $element, $attrs) {
+ link: function($scope, $element, $attrs)
+ {
var onMouseWheel, scrollCtrl;
scrollCtrl = $element.controller('$ionicScroll');
//console.log(scrollCtrl);
- if (!scrollCtrl) {
+ if (!scrollCtrl)
+ {
return console.error('mouseWheelScroll must be attached to a $ionicScroll controller.');
}
- onMouseWheel = function (e) {
+ onMouseWheel = function(e)
+ {
return scrollCtrl.scrollBy(0, -e.wheelDeltaY, false);
};
return scrollCtrl.element.addEventListener('wheel', onMouseWheel);
@@ -170,15 +180,17 @@ angular.module('zmApp', [
// (keeps reading data). Hence not using it now
//credit: http://stackoverflow.com/questions/34958575/intercepting-img-src-via-http-interceptor-as-well-as-not-lose-the-ability-to-kee
.directive('httpSrc', [
- '$http', 'imageLoadingDataShare', 'NVRDataModel',
- function ($http, imageLoadingDataShare, NVRDataModel) {
+ '$http', 'imageLoadingDataShare', 'NVRDataModel',
+ function($http, imageLoadingDataShare, NVRDataModel)
+ {
var directive = {
link: postLink,
restrict: 'A'
};
return directive;
- function postLink(scope, element, attrs) {
+ function postLink(scope, element, attrs)
+ {
//console.log ("HELLO NEW");
var requestConfig = {
method: 'GET',
@@ -187,26 +199,29 @@ angular.module('zmApp', [
cache: 'true'
};
- function base64Img(data) {
+ function base64Img(data)
+ {
var arr = new Uint8Array(data);
var raw = '';
var i, j, subArray, chunk = 5000;
- for (i = 0, j = arr.length; i < j; i += chunk) {
+ for (i = 0, j = arr.length; i < j; i += chunk)
+ {
subArray = arr.subarray(i, i + chunk);
raw += String.fromCharCode.apply(null, subArray);
}
return btoa(raw);
}
- attrs.$observe('httpSrc', function (newValue) {
+ attrs.$observe('httpSrc', function(newValue)
+ {
requestConfig.url = newValue;
//console.log ("requestConfig is " + JSON.stringify(requestConfig));
imageLoadingDataShare.set(1);
$http(requestConfig)
- .success(function (data) {
+ .success(function(data)
+ {
//console.log ("Inside HTTP after Calling " + requestConfig.url);
//console.log ("data got " + JSON.stringify(data));
-
var b64 = base64Img(data);
attrs.$set('src', "data:image/jpeg;base64," + b64);
imageLoadingDataShare.set(0);
@@ -217,16 +232,17 @@ angular.module('zmApp', [
}
])
-
//------------------------------------------------------------------
// switch between collection repeat or ng-repeat
//-------------------------------------------------------------------
-.directive('repeatsmart', function ($compile, $rootScope) {
+.directive('repeatsmart', function($compile, $rootScope)
+{
return {
restrict: 'A',
priority: 2000,
terminal: true,
- link: function (scope, element) {
+ link: function(scope, element)
+ {
var repeatDirective = ($rootScope.platformOS == 'desktop') ? 'ng-repeat' : 'collection-repeat';
//console.log("*********** REPEAT SCROLL IS " + repeatDirective);
@@ -237,26 +253,27 @@ angular.module('zmApp', [
};
})
-
//------------------------------------------------------------------
// I use this factory to share data between carousel and lazy load
// carousel will not progress autoslide till imageLoading is 0 or -1
//-------------------------------------------------------------------
-.factory('imageLoadingDataShare', function () {
+.factory('imageLoadingDataShare', function()
+{
var imageLoading = 0; // 0 = not loading, 1 = loading, -1 = error;
return {
- 'set': function (val) {
+ 'set': function(val)
+ {
imageLoading = val;
//console.log ("** IMAGE LOADING **"+val);
},
- 'get': function () {
+ 'get': function()
+ {
return imageLoading;
}
};
})
-
/*.factory('qHttp', function($q, $http) {
//credit: http://stackoverflow.com/a/29719693
var queue = $q.when();
@@ -269,33 +286,39 @@ angular.module('zmApp', [
};
})*/
-
//credit: http://stackoverflow.com/a/14468276
-.factory('qHttp', function ($q, $http) {
+.factory('qHttp', function($q, $http)
+{
var queue = [];
- var execNext = function () {
+ var execNext = function()
+ {
var task = queue[0];
//console.log ("qHTTP>>> Executing:"+JSON.stringify(task.c)+">>> pending:"+queue.length);
- $http(task.c).then(function (data) {
+ $http(task.c).then(function(data)
+ {
queue.shift();
task.d.resolve(data);
if (queue.length > 0) execNext();
- }, function (err) {
+ }, function(err)
+ {
queue.shift();
task.d.reject(err);
if (queue.length > 0) execNext();
});
};
- return function (config) {
+ return function(config)
+ {
var d = $q.defer();
//config.headers.push({'X-qHttp':'enabled'});
- queue.push({
+ queue.push(
+ {
c: config,
d: d
});
- if (queue.length === 1) {
+ if (queue.length === 1)
+ {
execNext();
}
//else
@@ -308,7 +331,8 @@ angular.module('zmApp', [
.factory('SecuredPopups', [
'$ionicPopup',
'$q',
- function ($ionicPopup, $q) {
+ function($ionicPopup, $q)
+ {
var firstDeferred = $q.defer();
firstDeferred.resolve();
@@ -319,26 +343,32 @@ angular.module('zmApp', [
var closeAndOpen = false;
return {
- 'show': function (method, object) {
+ 'show': function(method, object)
+ {
var deferred = $q.defer();
var closeMethod = null;
deferred.promise.isOpen = false;
- deferred.promise.close = function () {
- if (deferred.promise.isOpen && angular.isFunction(closeMethod)) {
+ deferred.promise.close = function()
+ {
+ if (deferred.promise.isOpen && angular.isFunction(closeMethod))
+ {
closeMethod();
}
};
- if (closeAndOpen && lastPopupPromise.isOpen) {
+ if (closeAndOpen && lastPopupPromise.isOpen)
+ {
lastPopupPromise.close();
}
- lastPopupPromise.then(function () {
+ lastPopupPromise.then(function()
+ {
deferred.promise.isOpen = true;
var popupInstance = $ionicPopup[method](object);
closeMethod = popupInstance.close;
- popupInstance.then(function (res) {
+ popupInstance.then(function(res)
+ {
deferred.promise.isOpen = false;
deferred.resolve(res);
});
@@ -352,7 +382,6 @@ angular.module('zmApp', [
}
])
-
//------------------------------------------------------------------
// this directive will be called any time an image completes loading
// via img tags where this directive is added (I am using this in
@@ -360,48 +389,53 @@ angular.module('zmApp', [
// downloading from ZM
//------------------------------------------------------------------
-.directive('imageonload', function () {
+.directive('imageonload', function()
+{
return {
restrict: 'A',
- link: function (scope, element, attrs) {
- element.bind('load', function () {
+ link: function(scope, element, attrs)
+ {
+ element.bind('load', function()
+ {
//call the function that was passed
scope.$apply(attrs.imageonload);
});
}
-
};
})
-
-
//--------------------------------------------------------------------------------------------
// This directive is adapted from https://github.com/paveisistemas/ionic-image-lazy-load
// I've removed lazyLoad and only made it show a spinner when an image is loading
//--------------------------------------------------------------------------------------------
.directive('imageSpinnerSrc', ['$document', '$compile', 'imageLoadingDataShare', '$timeout',
- function ($document, $compile, imageLoadingDataShare, $timeout) {
+ function($document, $compile, imageLoadingDataShare, $timeout)
+ {
return {
restrict: 'A',
- scope: {
+ scope:
+ {
imageSpinnerBackgroundImage: "@imageSpinnerBackgroundImage"
},
- link: function ($scope, $element, $attributes) {
+ link: function($scope, $element, $attributes)
+ {
/*if ($attributes.imageSpinnerLoader) {
var loader = $compile('<div class="image-loader-container"><ion-spinner class="image-loader" icon="' + $attributes.imageSpinnerLoader + '"></ion-spinner></div>')($scope);
$element.after(loader);
}*/
- if ($attributes.imageSpinnerLoader) {
+ if ($attributes.imageSpinnerLoader)
+ {
var loader = $compile('<div class="image-loader-container"><ion-spinner class="image-loader" icon="' + 'bubbles' + '"></ion-spinner></div>')($scope);
$element.after(loader);
}
imageLoadingDataShare.set(1);
loadImage();
- $attributes.$observe('imageSpinnerSrc', function (value) {
+ $attributes.$observe('imageSpinnerSrc', function(value)
+ {
//console.log ("DIRECTIVE SOURCE CHANGED");
imageLoadingDataShare.set(1);
loadImage();
@@ -409,30 +443,34 @@ angular.module('zmApp', [
});
-
// show an image-missing image
- $element.bind('error', function () {
+ $element.bind('error', function()
+ {
// console.log ("DIRECTIVE: IMAGE ERROR");
loader.remove();
-
var url = 'img/novideo.png';
$element.prop('src', url);
imageLoadingDataShare.set(0);
});
- function waitForFrame1() {
+ function waitForFrame1()
+ {
ionic.DomUtil.requestAnimationFrame(
- function () {
+ function()
+ {
imageLoadingDataShare.set(0);
//console.log ("IMAGE LOADED");
});
}
- function loadImage() {
- $element.bind("load", function (e) {
- if ($attributes.imageSpinnerLoader) {
+ function loadImage()
+ {
+ $element.bind("load", function(e)
+ {
+ if ($attributes.imageSpinnerLoader)
+ {
//console.log ("DIRECTIVE: IMAGE LOADED");
loader.remove();
//imageLoadingDataShare.set(0);
@@ -442,20 +480,21 @@ angular.module('zmApp', [
// to render - hoping this will improve tear
// of images
ionic.DomUtil.requestAnimationFrame(
- function () {
+ function()
+ {
waitForFrame1();
});
}
});
-
-
-
- if ($scope.imageSpinnerBackgroundImage == "true") {
+ if ($scope.imageSpinnerBackgroundImage == "true")
+ {
var bgImg = new Image();
- bgImg.onload = function () {
- if ($attributes.imageSpinnerLoader) {
+ bgImg.onload = function()
+ {
+ if ($attributes.imageSpinnerLoader)
+ {
loader.remove();
}
// set style attribute on element (it will load image)
@@ -467,29 +506,29 @@ angular.module('zmApp', [
};
-
bgImg.src = $attributes.imageSpinnerSrc;
- } else {
+ }
+ else
+ {
$element[0].src = $attributes.imageSpinnerSrc; // set src attribute on element (it will load image)
}
}
- function isInView() {
+ function isInView()
+ {
return true;
}
- $element.on('$destroy', function () {
+ $element.on('$destroy', function() {
});
-
}
};
- }])
-
-
+ }
+])
//------------------------------------------------------------------
// In Android, HTTP requests seem to get stuck once in a while
@@ -499,23 +538,25 @@ angular.module('zmApp', [
// That way the user can try again, and won't get stuck
// Also remember you need to add it to .config
//------------------------------------------------------------------
-.factory('timeoutHttpIntercept', ['$rootScope', '$q', 'zm', '$injector', function ($rootScope, $q, zm, $injector) {
+.factory('timeoutHttpIntercept', ['$rootScope', '$q', 'zm', '$injector', function($rootScope, $q, zm, $injector)
+{
$rootScope.zmCookie = "";
return {
-
-
- 'request': function (config) {
+ 'request': function(config)
+ {
+ if (!config) return config;
+ if (!config.url) return config;
// NOTE ON TIMEOUTS: As of Oct 10 2016, it seems
// the Http queue often gets messed up when there is a timeout
// and the # of requests are plentiful. I'm going to disable it and see
-
// console.log (">>>>"+config.url);
// handle basic auth properly
- if (config.url.indexOf("@") > -1) {
+ if (config.url.indexOf("@") > -1)
+ {
//console.log ("HTTP basic auth INTERCEPTOR URL IS " + config.url);
var components = URI.parse(config.url);
// console.log ("Parsed data is " + JSON.stringify(components));
@@ -529,9 +570,12 @@ angular.module('zmApp', [
//console.log (">>>>>>>>>>>>> INTERCEPT OBJECT " + JSON.stringify(config));
- if ($rootScope.zmCookie) {
+ if ($rootScope.zmCookie)
+ {
config.headers.Cookie = "ZMSESSID=" + $rootScope.zmCookie;
- } else {
+ }
+ else
+ {
// console.log ("No cookie present in " + config.url);
}
@@ -540,31 +584,38 @@ angular.module('zmApp', [
(config.url.indexOf("daemonCheck.json") > -1) ||
(config.url.indexOf("getLoad.json") > -1))
-
{
// these can take time, so lets bump up timeout
config.timeout = zm.largeHttpTimeout;
- } else if ((config.url.indexOf("view=view_video") > -1) ||
- config.url.indexOf(".mp4") > -1) {
+ }
+ else if ((config.url.indexOf("view=view_video") > -1) ||
+ config.url.indexOf(".mp4") > -1)
+ {
// console.log(">>> skipping timers for MP4");
// put a timeout for zms urls
- } else if (config.url.indexOf("zms?") > -1) {
+ }
+ else if (config.url.indexOf("zms?") > -1)
+ {
// config.timeout = zm.httpTimeout;
}
return config;
},
- 'response': function (response) {
+ 'response': function(response)
+ {
var cookies = response.headers("Set-Cookie");
- if (cookies != null) {
+ if (cookies != null)
+ {
var zmSess = cookies.match("ZMSESSID=(.*?);");
- if (zmSess) {
- if (zmSess[1]) {
+ if (zmSess)
+ {
+ if (zmSess[1])
+ {
// console.log ("***** SETTING COOKIE TO " + zmCookie);
$rootScope.zmCookie = zmSess[1];
@@ -576,48 +627,53 @@ angular.module('zmApp', [
return response;
}
-
};
}])
-
//-----------------------------------------------------------------
// This service automatically checks for new versions every 24 hrs
//------------------------------------------------------------------
-.factory('zmCheckUpdates', function ($interval, $http, zm, $timeout, $localstorage, NVRDataModel, $rootScope) {
+.factory('zmCheckUpdates', function($interval, $http, zm, $timeout, $localstorage, NVRDataModel, $rootScope, $translate)
+{
var zmUpdateHandle;
var zmUpdateVersion = "";
- function start() {
+ function start()
+ {
checkUpdate();
$interval.cancel(zmUpdateHandle);
- zmUpdateHandle = $interval(function () {
+ zmUpdateHandle = $interval(function()
+ {
checkUpdate();
}, zm.updateCheckInterval);
-
- function checkUpdate() {
+ function checkUpdate()
+ {
var lastdateString = NVRDataModel.getLastUpdateCheck();
var lastdate;
- if (!lastdateString) {
+ if (!lastdateString)
+ {
lastdate = moment().subtract(2, 'day');
- } else {
+ }
+ else
+ {
lastdate = moment(lastdateString);
}
var timdiff = moment().diff(lastdate, 'hours');
- if (timdiff < 24) {
+ if (timdiff < 24)
+ {
NVRDataModel.log("Checked for update " + timdiff + " hours ago. Not checking again");
return;
}
NVRDataModel.log("Checking for new version updates...");
-
$http.get(zm.latestRelease)
- .then(function (success) {
+ .then(function(success)
+ {
NVRDataModel.setLastUpdateCheck(moment().toISOString());
// $localstorage.set("lastUpdateCheck", moment().toISOString());
@@ -625,13 +681,17 @@ angular.module('zmApp', [
var res = success.data.tag_name.match("v(.*)");
zmUpdateVersion = res[1];
var currentVersion = NVRDataModel.getAppVersion();
- if ($rootScope.platformOS == "desktop") {
+ if ($rootScope.platformOS == "desktop")
+ {
zmUpdateVersion = zmUpdateVersion + "D";
}
//if (NVRDataModel.getAppVersion() != zmUpdateVersion) {
- if (NVRDataModel.versionCompare(NVRDataModel.getAppVersion(), zmUpdateVersion) == -1) {
+ if (NVRDataModel.versionCompare(NVRDataModel.getAppVersion(), zmUpdateVersion) == -1)
+ {
$rootScope.newVersionAvailable = "v" + zmUpdateVersion + " available";
- } else {
+ }
+ else
+ {
$rootScope.newVersionAvailable = "";
}
NVRDataModel.debug("current version: " + currentVersion + " & available version " + zmUpdateVersion);
@@ -641,43 +701,67 @@ angular.module('zmApp', [
});
NVRDataModel.log("Checking for news updates");
- $http.get(zm.blogUrl)
- .success(function (data) {
+ $http.get(zm.blogUrl,
+ {transformResponse: function(d,h)
+ {
+ var trunc = "])}while(1);</x>";
+ d = d.substr(trunc.length);
+ return d;
+ }
+ })
+
+ .success(function(datastr)
+ {
+
+ var data = JSON.parse(datastr);
$rootScope.newBlogPost = "";
- if (data.length <= 0) {
+ if (data.payload.posts.length <= 0)
+ {
$rootScope.newBlogPost = "";
return;
}
var lastDate = NVRDataModel.getLatestBlogPostChecked();
//console.log ("************ BLOG LAST DATE " + lastDate);
- if (!lastDate) {
+ if (!lastDate)
+ {
- $rootScope.newBlogPost = "(new post)";
+ $rootScope.newBlogPost = "("+$translate.instant('kNewPost')+")";
+ NVRDataModel.setLatestBlogPostChecked(moment().format("YYYY-MM-DD HH:mm:ss"));
return;
}
var mLastDate = moment(lastDate);
- var mItemDate = moment(data[0].date);
-
- if (mItemDate.diff(mLastDate) > 0) {
- NVRDataModel.debug("New post dated " + data[0].date + " found");
- if (data[0].level == "critical") {
- $rootScope.newBlogPost = "(new post)";
- } else {
- NVRDataModel.debug("Not showing a notification in menu as this is not critical");
- }
- } else {
- NVRDataModel.debug("Latest post dated " + data[0].date + " but you read " + lastDate);
- }
+ var mItemDate = moment(data.payload.posts[0].createdAt);
+ if (mItemDate.diff(mLastDate, 'seconds') > 0)
+ {
+ /*console.log ("DIFF IS "+mItemDate.diff(mLastDate, 'seconds'));
+ console.log ("DIFF mLastDate="+mLastDate);
+ console.log ("DIFF mItemDate="+mItemDate);
+ console.log ("FORMAT DIFF mLastDate="+mLastDate.format("YYYY-MM-DD HH:mm:ss") );
+ console.log ("FORMAT DIFF mItemDate="+mItemDate.format("YYYY-MM-DD HH:mm:ss") );*/
+
+ NVRDataModel.debug("New post dated " + mItemDate.format("YYYY-MM-DD HH:mm:ss") + " found, last date checked was "+mLastDate.format("YYYY-MM-DD HH:mm:ss"));
+
+ $rootScope.newBlogPost = "("+$translate.instant('kNewPost')+")";
+ NVRDataModel.setLatestBlogPostChecked(mItemDate.format("YYYY-MM-DD HH:mm:ss"));
+
+
+
+ }
+ else
+ {
+ NVRDataModel.debug("Latest post dated " + mItemDate.format("YYYY-MM-DD HH:mm:ss") + " but you read " + lastDate);
+ }
});
}
}
- function getLatestUpdateVersion() {
+ function getLatestUpdateVersion()
+ {
return (zmUpdateVersion == "") ? "(unknown)" : zmUpdateVersion;
}
@@ -688,23 +772,22 @@ angular.module('zmApp', [
};
-
})
-
-
//-----------------------------------------------------------------
// This service automatically logs into ZM at periodic intervals
//------------------------------------------------------------------
-.factory('zmAutoLogin', function ($interval, NVRDataModel, $http, zm, $browser, $timeout, $q, $rootScope, $ionicLoading, $ionicPopup, $state, $ionicContentBanner, EventServer, $ionicHistory, $translate) {
+.factory('zmAutoLogin', function($interval, NVRDataModel, $http, zm, $browser, $timeout, $q, $rootScope, $ionicLoading, $ionicPopup, $state, $ionicContentBanner, EventServer, $ionicHistory, $translate)
+{
var zmAutoLoginHandle;
//------------------------------------------------------------------
// doLogin() emits this when there is an auth error in the portal
//------------------------------------------------------------------
- $rootScope.$on("auth-error", function () {
+ $rootScope.$on("auth-error", function()
+ {
NVRDataModel.debug("zmAutoLogin: Inside auth-error emit");
NVRDataModel.displayBanner('error', ['ZoneMinder authentication failed', 'Please check settings']);
@@ -718,9 +801,11 @@ angular.module('zmApp', [
// c) localforage data loaded
//------------------------------------------------------------------
- $rootScope.$on("init-complete", function () {
+ $rootScope.$on("init-complete", function()
+ {
NVRDataModel.log(">>>>>>>>>>>>>>> All init over, going to portal login");
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableAnimate: true
});
$state.go("zm-portal-login");
@@ -731,38 +816,43 @@ angular.module('zmApp', [
// doLogin() emits this when our auth credentials work
//------------------------------------------------------------------
- $rootScope.$on("auth-success", function () {
- var contentBannerInstance = $ionicContentBanner.show({
+ $rootScope.$on("auth-success", function()
+ {
+ var contentBannerInstance = $ionicContentBanner.show(
+ {
text: ['ZoneMinder' + $translate.instant('kAuthSuccess')],
interval: 2000,
type: 'info',
transition: 'vertical'
});
- $timeout(function () {
+ $timeout(function()
+ {
contentBannerInstance();
}, 2000);
NVRDataModel.debug("auth-success emit:Successful");
});
-
- $rootScope.getProfileName = function () {
+ $rootScope.getProfileName = function()
+ {
var ld = NVRDataModel.getLogin();
return (ld.serverName || '(none)');
};
- $rootScope.getLocalTimeZone = function () {
+ $rootScope.getLocalTimeZone = function()
+ {
return moment.tz.guess();
};
-
- $rootScope.getServerTimeZoneNow = function () {
+ $rootScope.getServerTimeZoneNow = function()
+ {
return NVRDataModel.getTimeZoneNow();
};
- $rootScope.isTzSupported = function () {
+ $rootScope.isTzSupported = function()
+ {
return NVRDataModel.isTzSupported();
};
@@ -772,86 +862,103 @@ angular.module('zmApp', [
// which actually means auth failed, but ZM treats it as a success
//------------------------------------------------------------------
- function doLogin(str) {
+ function doLogin(str)
+ {
+
var d = $q.defer();
+
+
NVRDataModel.processFastLogin()
// coming here means login not needed, old login is valid
- .then(function (success) {
+ .then(function(success)
+ {
d.resolve("Login Success due to fast login");
$rootScope.$emit('auth-success', "fast login mode");
return d.promise;
},
// coming here means login is needed
- function (error) {
-
+ function(error)
+ {
var statename = $ionicHistory.currentStateName();
- if (statename == "montage-history") {
+ if (statename == "montage-history")
+ {
NVRDataModel.log("Skipping login process as we are in montage history. Re-logging will mess up the stream");
d.resolve("success");
return d.promise;
}
+ if ($rootScope.isDownloading)
+ {
+ NVRDataModel.log("Skipping login process as we are downloading...");
+ d.resolve("success");
+ return d.promise;
+ }
+
NVRDataModel.debug("Resetting zmCookie...");
$rootScope.zmCookie = '';
// first try to login, if it works, good
// else try to do reachability
proceedWithLogin()
- .then(function (success) {
+ .then(function(success)
+ {
NVRDataModel.debug("Storing login time as " + moment().toString());
localforage.setItem("lastLogin", moment().toString());
d.resolve(success);
return d.promise;
},
- function (error)
+ function(error)
// login to main failed, so try others
{
NVRDataModel.getReachableConfig(true)
- .then(function (data) {
+ .then(function(data)
+ {
proceedWithLogin()
- .then(function (success) {
+ .then(function(success)
+ {
d.resolve(success);
return d.promise;
},
- function (error) {
+ function(error)
+ {
d.reject(error);
return d.promise;
});
},
- function (error) {
+ function(error)
+ {
d.reject(error);
return d.promise;
});
});
-
return d.promise;
-
- function proceedWithLogin() {
+ function proceedWithLogin()
+ {
// recompute rand anyway so even if you don't have auth
// your stream should not get frozen
$rootScope.rand = Math.floor((Math.random() * 100000) + 1);
$rootScope.modalRand = Math.floor((Math.random() * 100000) + 1);
-
-
// console.log ("***** STATENAME IS " + statename);
var d = $q.defer();
var ld = NVRDataModel.getLogin();
NVRDataModel.log("zmAutologin called");
- if (str) {
- $ionicLoading.show({
+ if (str)
+ {
+ $ionicLoading.show(
+ {
template: str,
noBackdrop: true,
duration: zm.httpTimeout
@@ -859,20 +966,23 @@ angular.module('zmApp', [
}
NVRDataModel.isReCaptcha()
- .then(function (result) {
- if (result == true) {
+ .then(function(result)
+ {
+ if (result == true)
+ {
$ionicLoading.hide();
- NVRDataModel.displayBanner('error', ['reCaptcha must be disabled',
- ], "", 8000);
- var alertPopup = $ionicPopup.alert({
+ NVRDataModel.displayBanner('error', ['reCaptcha must be disabled', ], "", 8000);
+ var alertPopup = $ionicPopup.alert(
+ {
title: 'reCaptcha enabled',
- template: $translate.instant('kRecaptcha')
+ template: $translate.instant('kRecaptcha'),
+ okText: $translate.instant('kButtonOk'),
+ cancelText: $translate.instant('kButtonCancel'),
});
-
-
// close it after 5 seconds
- $timeout(function () {
+ $timeout(function()
+ {
alertPopup.close();
}, 5000);
@@ -881,23 +991,22 @@ angular.module('zmApp', [
return (d.promise);
}
-
-
});
-
-
var loginData = NVRDataModel.getLogin();
//NVRDataModel.debug ("*** AUTH LOGIN URL IS " + loginData.url);
- $http({
+ $http(
+ {
method: 'POST',
//withCredentials: true,
url: loginData.url + '/index.php',
- headers: {
+ headers:
+ {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json',
},
- transformRequest: function (obj) {
+ transformRequest: function(obj)
+ {
var str = [];
for (var p in obj)
str.push(encodeURIComponent(p) + "=" +
@@ -906,14 +1015,16 @@ angular.module('zmApp', [
return params;
},
- data: {
+ data:
+ {
username: loginData.username,
password: loginData.password,
action: "login",
view: "console"
}
})
- .success(function (data, status, headers) {
+ .success(function(data, status, headers)
+ {
$ionicLoading.hide();
// Coming here does not mean success
@@ -922,8 +1033,8 @@ angular.module('zmApp', [
// so we will check if the data has
// <title>ZM - Login</title> -- it it does then its the login page
-
- if (data.indexOf(zm.loginScreenString) == -1) {
+ if (data.indexOf(zm.loginScreenString) == -1)
+ {
//eventServer.start();
$rootScope.loggedIntoZm = 1;
@@ -933,7 +1044,8 @@ angular.module('zmApp', [
$rootScope.$emit('auth-success', data);
- } else // this means login error
+ }
+ else // this means login error
{
$rootScope.loggedIntoZm = -1;
//console.log("**** ZM Login FAILED");
@@ -949,7 +1061,8 @@ angular.module('zmApp', [
$rootScope.authSession = "undefined";
var ld = NVRDataModel.getLogin();
NVRDataModel.getAuthKey($rootScope.validMonitorId)
- .then(function (success) {
+ .then(function(success)
+ {
//console.log(success);
$rootScope.authSession = success;
@@ -957,7 +1070,8 @@ angular.module('zmApp', [
$rootScope.authSession);
},
- function (error) {
+ function(error)
+ {
//console.log(error);
NVRDataModel.log("Modal: Error returned Stream authentication construction. Retaining old value of: " + $rootScope.authSession);
@@ -967,7 +1081,8 @@ angular.module('zmApp', [
return (d.promise);
})
- .error(function (error, status) {
+ .error(function(error, status)
+ {
$ionicLoading.hide();
//console.log("**** ZM Login FAILED");
@@ -989,13 +1104,15 @@ angular.module('zmApp', [
}
- function start() {
+ function start()
+ {
var ld = NVRDataModel.getLogin();
// lets keep this timer irrespective of auth or no auth
$rootScope.loggedIntoZm = 0;
$interval.cancel(zmAutoLoginHandle);
//doLogin();
- zmAutoLoginHandle = $interval(function () {
+ zmAutoLoginHandle = $interval(function()
+ {
doLogin("");
}, zm.loginInterval); // Auto login every 5 minutes
@@ -1004,7 +1121,8 @@ angular.module('zmApp', [
}
- function stop() {
+ function stop()
+ {
var ld = NVRDataModel.getLogin();
$interval.cancel(zmAutoLoginHandle);
@@ -1020,16 +1138,12 @@ angular.module('zmApp', [
};
})
-
//====================================================================
// First run in ionic
//====================================================================
-
-.run(function ($ionicPlatform, $ionicPopup, $rootScope, zm, $state, $stateParams, NVRDataModel, $cordovaSplashscreen, $http, $interval, zmAutoLogin, zmCheckUpdates, $fileLogger, $timeout, $ionicHistory, $window, $ionicSideMenuDelegate, EventServer, $ionicContentBanner, $ionicLoading, $ionicNativeTransitions, $translate, $localstorage) {
-
-
-
+.run(function($ionicPlatform, $ionicPopup, $rootScope, zm, $state, $stateParams, NVRDataModel, $cordovaSplashscreen, $http, $interval, zmAutoLogin, zmCheckUpdates, $fileLogger, $timeout, $ionicHistory, $window, $ionicSideMenuDelegate, EventServer, $ionicContentBanner, $ionicLoading, $ionicNativeTransitions, $translate, $localstorage)
+ {
$rootScope.appName = "zmNinja";
$rootScope.zmGlobalCookie = "";
@@ -1056,49 +1170,54 @@ angular.module('zmApp', [
$rootScope.newBlogPost = "";
$rootScope.apiVersion = "";
-
-
// only for android
- $rootScope.exitApp = function () {
+ $rootScope.exitApp = function()
+ {
NVRDataModel.log("user exited app");
ionic.Platform.exitApp();
};
-
// This is a global exception interceptor
- $rootScope.exceptionMessage = function (error) {
+ $rootScope.exceptionMessage = function(error)
+ {
NVRDataModel.debug("**EXCEPTION**" + error.reason + " caused by " + error.cause);
};
-
-
- window.addEventListener('beforeunload', function(ev) {
-
+ window.addEventListener('beforeunload', function(ev)
+ {
+
if ($rootScope.platformOS != 'desktop')
{
- ev.returnValue = "true";
+ ev.returnValue = "true";
return;
}
-
-
-
- localforage.setItem('last-desktop-state', {'name':$ionicHistory.currentView().stateName, 'params' : $ionicHistory.currentView().stateParams}).then(function () {
- return localforage.getItem('last-desktop-state');
- }).then(function (value) {
- ev.returnValue = "true";
- }).catch(function (err) {
- ev.returnValue = "true";
+
+ localforage.setItem('last-desktop-state',
+ {
+ 'name': $ionicHistory.currentView().stateName,
+ 'params': $ionicHistory.currentView().stateParams
+ }).then(function()
+ {
+ return localforage.getItem('last-desktop-state');
+ }).then(function(value)
+ {
+ ev.returnValue = "true";
+ }).catch(function(err)
+ {
+ ev.returnValue = "true";
});
-
+
});
-
+
// register callbacks for online/offline
// lets see if it really works
$rootScope.online = navigator.onLine;
-
- $window.addEventListener("offline", function () {
- $rootScope.$apply(function () {
+
+ $window.addEventListener("offline", function()
+ {
+ $rootScope.$apply(function()
+ {
$rootScope.online = false;
NVRDataModel.log("Your network went offline");
@@ -1107,21 +1226,27 @@ angular.module('zmApp', [
});
}, false);
- $window.addEventListener("online", function () {
- $rootScope.$apply(function () {
+ $window.addEventListener("online", function()
+ {
+ $rootScope.$apply(function()
+ {
$rootScope.online = true;
- $timeout(function () {
+ $timeout(function()
+ {
var networkState = navigator.connection.type;
NVRDataModel.debug("Detected network type as: " + networkState);
var strState = NVRDataModel.getBandwidth();
NVRDataModel.debug("getBandwidth() normalized it as: " + strState);
$rootScope.runMode = strState;
if ((NVRDataModel.getLogin().autoSwitchBandwidth == true) &&
- (NVRDataModel.getLogin().enableLowBandwidth == true)) {
+ (NVRDataModel.getLogin().enableLowBandwidth == true))
+ {
NVRDataModel.debug("Setting app state to: " + strState);
$rootScope.$emit('bandwidth-change', strState);
- } else {
+ }
+ else
+ {
NVRDataModel.debug("Not changing bandwidth state, as auto change is not on");
}
@@ -1133,45 +1258,47 @@ angular.module('zmApp', [
});
}, false);
-
-
// This code takes care of trapping the Android back button
// and takes it to the menu.
//console.log (">>>>>>>>>>>>>>>>>>BACK BUTTON REGISTERED");
- $ionicPlatform.registerBackButtonAction(function (e) {
+ $ionicPlatform.registerBackButtonAction(function(e)
+ {
e.preventDefault();
//console.log ("******** back called with isOpenLeft: " + $ionicSideMenuDelegate.isOpenLeft());
- if (!$ionicSideMenuDelegate.isOpenLeft()) {
+ if (!$ionicSideMenuDelegate.isOpenLeft())
+ {
$ionicSideMenuDelegate.toggleLeft();
//console.log("Status of SIDE MENU IS : " + $ionicSideMenuDelegate.isOpen());
- } else {
+ }
+ else
+ {
navigator.app.exitApp();
}
}, 501);
-
// this works reliably on both Android and iOS. The "onorientation" seems to reverse w/h in Android. Go figure.
// http://stackoverflow.com/questions/1649086/detect-rotation-of-android-phone-in-the-browser-with-javascript
- var checkOrientation = function () {
+ var checkOrientation = function()
+ {
var pixelRatio = window.devicePixelRatio || 1;
$rootScope.devWidth = ((window.innerWidth > 0) ? window.innerWidth : screen.width);
$rootScope.devHeight = ((window.innerHeight > 0) ? window.innerHeight : screen.height);
//console.log("********NEW Computed Dev Width & Height as" + $rootScope.devWidth + "*" + $rootScope.devHeight);
-
};
window.addEventListener("resize", checkOrientation, false);
-
// we come here when a user forcibly cancels portal auth
// useful when you know your auth won't succeed and you need to
// switch to another server
- $rootScope.cancelAuth = function () {
+ $rootScope.cancelAuth = function()
+ {
$ionicLoading.hide();
NVRDataModel.log("User cancelled login");
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableAnimate: true,
disableBack: true
});
@@ -1179,7 +1306,8 @@ angular.module('zmApp', [
window.stop();
//console.log ("inside cancelAuth , calling wizard");
- $state.go("login", {
+ $state.go("login",
+ {
"wizard": false
});
return;
@@ -1189,26 +1317,31 @@ angular.module('zmApp', [
// authorize state transitions
//----------------------------------------------------------------------------
- $rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
+ $rootScope.$on('$stateChangeStart', function(event, toState, toParams)
+ {
var requireLogin = toState.data.requireLogin;
- if (NVRDataModel.isLoggedIn() || toState.data.requireLogin == false) {
+ if (NVRDataModel.isLoggedIn() || toState.data.requireLogin == false)
+ {
//console.log("State transition is authorized");
return;
- } else {
+ }
+ else
+ {
NVRDataModel.log("In Auth State trans: Not logged in, requested to go to " + JSON.stringify(toState));
// event.preventDefault();
//
$state.transitionTo('login');
-
}
- if (requireLogin) {
+ if (requireLogin)
+ {
- $ionicPopup.alert({
+ $ionicPopup.alert(
+ {
title: $translate.instant('kCredentialsTitle'),
template: $translate.instant('kCredentialsBody')
});
@@ -1222,22 +1355,55 @@ angular.module('zmApp', [
});
+ // credit http://stackoverflow.com/a/2091331/1361529
+ function getQueryVariable(query, variable) {
+ var vars = query.split('&');
+ for (var i = 0; i < vars.length; i++) {
+ var pair = vars[i].split('=');
+ if (decodeURIComponent(pair[0]) == variable) {
+ return decodeURIComponent(pair[1]);
+ }
+ }
+ return "";
+ //console.log('Query variable %s not found', variable);
+ }
//---------------------------------------------------------------------
// called when device is ready
//---------------------------------------------------------------------
- function getTextZoomCallback(tz) {
+ function getTextZoomCallback(tz)
+ {
$rootScope.textScaleFactor = parseFloat(tz + "%") / 100.0;
NVRDataModel.debug("text zoom factor is " + $rootScope.textScaleFactor);
}
- $ionicPlatform.ready(function () {
-
+ $ionicPlatform.ready(function()
+ {
+
+ // handles URL launches
+ // if you just launch zmninja:// then it will honor the settings in "tap screen" -> events or montage
+ // if you launch with zmninja://<mid> it will take you to live view for that mid
+ window.handleOpenURL = function(url) {
+ $rootScope.tappedNotification = 1;
+ $rootScope.tappedMid = 0;
+ var c= URI.parse(url);
+ //NVRDataModel.log ("***********launched with "+ JSON.stringify(c));
+ if (c.query)
+ {
+ var qm = getQueryVariable(c.query, "mid");
+ if (qm) $rootScope.tappedMid = parseInt(qm);
+ NVRDataModel.log ("external URL called with MID="+$rootScope.tappedMid);
+ //console.log (">>>>>>>>> EID="+getQueryVariable(c.query, "eid"));
+
+ }
+
+
+
+ };
$rootScope.textScaleFactor = 1.0;
-
$rootScope.db = null;
$rootScope.runMode = NVRDataModel.getBandwidth();
@@ -1253,42 +1419,48 @@ angular.module('zmApp', [
if (window.cordova)
MobileAccessibility.getTextZoom(getTextZoomCallback);
-
- // $rootScope.lastState = "events";
+
+ // $rootScope.lastState = "events";
//$rootScope.lastStateParam = "0";
- localforage.config({
+ localforage.config(
+ {
name: zm.dbName
});
var order = [];
- if ($rootScope.platformOS == 'ios') {
+ if ($rootScope.platformOS == 'ios')
+ {
order = [window.cordovaSQLiteDriver._driver,
- localforage.INDEXEDDB,
- localforage.WEBSQL,
- localforage.LOCALSTORAGE];
- } else
+ localforage.INDEXEDDB,
+ localforage.WEBSQL,
+ localforage.LOCALSTORAGE
+ ];
+ }
+ else
{
// don't do SQL for non IOS - seems to hang?
order = [
- localforage.INDEXEDDB,
- localforage.WEBSQL,
- localforage.LOCALSTORAGE,
- ];
+ localforage.INDEXEDDB,
+ localforage.WEBSQL,
+ localforage.LOCALSTORAGE,
+ ];
}
- localforage.defineDriver(window.cordovaSQLiteDriver).then(function () {
+ localforage.defineDriver(window.cordovaSQLiteDriver).then(function()
+ {
return localforage.setDriver(
// Try setting cordovaSQLiteDriver if available,
order
);
- }).then(function () {
+ }).then(function()
+ {
// this should alert "cordovaSQLiteDriver" when in an emulator or a device
NVRDataModel.log("localforage driver for storage:" + localforage.driver());
@@ -1296,10 +1468,12 @@ angular.module('zmApp', [
var defaultServerName = $localstorage.get("defaultServerName");
localforage.getItem("defaultServerName")
- .then(function (val) {
+ .then(function(val)
+ {
// console.log (">>>> localforage reported defaultServerName as " + val);
// if neither, we are in first use, mates!
- if (!val && !defaultServerName) {
+ if (!val && !defaultServerName)
+ {
continueInitialInit();
/* NVRDataModel.debug ("Neither localstorage or forage - First use, showing warm and fuzzy...");
$ionicHistory.nextViewOptions({
@@ -1307,11 +1481,11 @@ angular.module('zmApp', [
disableBack: true
});
$state.go('first-use');*/
- } else if (!val && defaultServerName) {
+ }
+ else if (!val && defaultServerName)
+ {
NVRDataModel.log(">>>>Importing data from localstorage....");
-
-
var dsn = defaultServerName;
var dl = $localstorage.get('defaultLang') || 'en';
var ifu = ($localstorage.get('isFirstUse') == '0' ? false : true);
@@ -1328,21 +1502,25 @@ angular.module('zmApp', [
NVRDataModel.log("server group list:" + JSON.stringify(sgl));
localforage.setItem('defaultLang', dl)
- .then(function () {
+ .then(function()
+ {
NVRDataModel.log(">>>>migrated defaultLang...");
NVRDataModel.setFirstUse(ifu);
return localforage.setItem('isFirstUse', ifu);
})
- .then(function () {
+ .then(function()
+ {
NVRDataModel.log(">>>>migrated isFirstUse...");
return localforage.setItem('lastUpdateCheck', ifu);
})
- .then(function () {
+ .then(function()
+ {
NVRDataModel.log(">>>>migrated lastUpdateCheck...");
return localforage.setItem('latestBlogPostChecked', lbpc);
})
- .then(function () {
+ .then(function()
+ {
NVRDataModel.log(">>>>migrated latestBlogPostChecked...");
// lets encrypt serverGroupList
NVRDataModel.log("server group list is " + JSON.stringify(sgl));
@@ -1351,36 +1529,38 @@ angular.module('zmApp', [
ct = sgl;
return localforage.setItem('serverGroupList', ct);
})
- .then(function () {
+ .then(function()
+ {
NVRDataModel.log(">>>>migrated serverGroupList...");
return localforage.setItem('defaultServerName', dsn);
})
- .then(function () {
+ .then(function()
+ {
NVRDataModel.log(">>>>migrated defaultServerName...");
NVRDataModel.log(">>>>Migrated all values, continuing...");
//NVRDataModel.migrationComplete();
continueInitialInit();
})
- .catch(function (err) {
+ .catch(function(err)
+ {
NVRDataModel.log("Migration error : " + JSON.stringify(err));
continueInitialInit();
});
- } else {
+ }
+ else
+ {
NVRDataModel.log(">>>>No data to import....");
//NVRDataModel.migrationComplete();
continueInitialInit();
}
-
-
});
});
-
-
- function continueInitialInit() {
+ function continueInitialInit()
+ {
var pixelRatio = window.devicePixelRatio || 1;
$rootScope.devWidth = ((window.innerWidth > 0) ? window.innerWidth : screen.width);
$rootScope.devHeight = ((window.innerHeight > 0) ? window.innerHeight : screen.height);
@@ -1389,11 +1569,12 @@ angular.module('zmApp', [
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
-
- if (window.cordova && window.cordova.plugins.Keyboard) {
+ if (window.cordova && window.cordova.plugins.Keyboard)
+ {
cordova.plugins.Keyboard.disableScroll(true);
}
- if (window.StatusBar) {
+ if (window.StatusBar)
+ {
// org.apache.cordova.statusbar required
NVRDataModel.log("Updating statusbar");
StatusBar.styleDefault();
@@ -1401,24 +1582,28 @@ angular.module('zmApp', [
StatusBar.backgroundColorByHexString("#2980b9");
}
-
- if (window.cordova) {
+ if (window.cordova)
+ {
$cordovaSplashscreen.hide();
NVRDataModel.log("Enabling insecure SSL");
cordova.plugins.certificates.trustUnsecureCerts(true);
- cordova.getAppVersion.getVersionNumber().then(function (version) {
+ cordova.getAppVersion.getVersionNumber().then(function(version)
+ {
appVersion = version;
NVRDataModel.log("App Version: " + appVersion);
NVRDataModel.setAppVersion(appVersion);
});
}
- $fileLogger.checkFile().then(function (resp) {
- if (parseInt(resp.size) > zm.logFileMaxSize) {
+ $fileLogger.checkFile().then(function(resp)
+ {
+ if (parseInt(resp.size) > zm.logFileMaxSize)
+ {
- $fileLogger.deleteLogfile().then(function () {
+ $fileLogger.deleteLogfile().then(function()
+ {
NVRDataModel.log("Deleting old log file as it exceeds " + zm.logFileMaxSize + " bytes");
});
@@ -1428,12 +1613,13 @@ angular.module('zmApp', [
$fileLogger.setStorageFilename(zm.logFile);
$fileLogger.setTimestampFormat('MMM d, y ' + NVRDataModel.getTimeFormat());
-
-
- if (NVRDataModel.getLogin().disableNative) {
+ if (NVRDataModel.getLogin().disableNative)
+ {
NVRDataModel.log("Disabling native transitions...");
$ionicNativeTransitions.enable(false);
- } else {
+ }
+ else
+ {
NVRDataModel.log("Enabling native transitions...");
$ionicNativeTransitions.enable(true);
}
@@ -1442,78 +1628,82 @@ angular.module('zmApp', [
NVRDataModel.log("Retrieving language before init is called...");
localforage.getItem("defaultLang")
- .then(function (val) {
+ .then(function(val)
+ {
var lang = val;
//console.log (">>>>>>>>>>>>>> LANG IS " + val);
-
- if (lang == undefined || lang == null) {
+ if (lang == undefined || lang == null)
+ {
NVRDataModel.log("No language set, switching to en");
lang = "en";
-
- } else {
+ }
+ else
+ {
NVRDataModel.log("Language stored as:" + lang);
}
NVRDataModel.setDefaultLanguage(lang, false)
- .then(function (success) {
+ .then(function(success)
+ {
NVRDataModel.log(">>>>Language to be used:" + $translate.proposedLanguage());
moment.locale($translate.proposedLanguage());
// Remember this is before data Init
// so I need to do a direct forage fetch
localforage.getItem("isFirstUse")
- .then(function (val) {
+ .then(function(val)
+ {
//console.log ("isFirstUse is " + val);
- if (val == null || val == true) {
+ if (val == null || val == true)
+ {
NVRDataModel.log("First time detected");
$state.go("first-use");
return;
- } else {
+ }
+ else
+ {
continueRestOfInit();
}
});
-
});
});
}
+ function continueRestOfInit()
+ {
-
-
-
-
- function continueRestOfInit() {
-
- if ($rootScope.platformOS == 'desktop' )
+ if ($rootScope.platformOS == 'desktop')
{
$rootScope.lastState = "";
$rootScope.lastStateParam = {};
-
+
localforage.getItem('last-desktop-state')
- .then (function (succ) {
- // console.log ("FOUND " + JSON.stringify(succ) + ":"+succ);
- if (succ)
+ .then(function(succ)
{
- $rootScope.lastState = succ.name;
- $rootScope.lastStateParam = succ.params;
-
- }
- loadServices();
- }, function (err) {
- console.log ("ERR " + JSON.stringify(err));
- loadServices();
- });
+ // console.log ("FOUND " + JSON.stringify(succ) + ":"+succ);
+ if (succ)
+ {
+ $rootScope.lastState = succ.name;
+ $rootScope.lastStateParam = succ.params;
+
+ }
+ loadServices();
+ }, function(err)
+ {
+ console.log("ERR " + JSON.stringify(err));
+ loadServices();
+ });
}
else
-
+
{
-
+
loadServices();
}
@@ -1528,76 +1718,79 @@ angular.module('zmApp', [
setupPauseAndResume();
}
-
-
}
-
- function setupPauseAndResume() {
+ function setupPauseAndResume()
+ {
NVRDataModel.log("Setting up pause and resume handler AFTER language is loaded...");
//---------------------------------------------------------------------------
// resume handler
//----------------------------------------------------------------------------
- document.addEventListener("resume", function () {
+ document.addEventListener("resume", function()
+ {
NVRDataModel.log("App is resuming from background");
+ $rootScope.isDownloading = false;
var forceDelay = NVRDataModel.getLogin().resumeDelay;
NVRDataModel.log(">>> Resume delayed for " + forceDelay + " ms, to wait for network stack...");
- $timeout(function () {
+ $timeout(function()
+ {
var ld = NVRDataModel.getLogin();
-
NVRDataModel.setBackground(false);
// don't animate
- $ionicHistory.nextViewOptions({
+ $ionicHistory.nextViewOptions(
+ {
disableAnimate: true,
disableBack: true
});
-
-
-
-
-
- // remember the last state so we can
- // go back there after auth
- if ($ionicHistory.currentView()) {
- $rootScope.lastState = $ionicHistory.currentView().stateName;
- $rootScope.lastStateParam =
- $ionicHistory.currentView().stateParams;
- NVRDataModel.debug("Last State recorded:" +
- JSON.stringify($ionicHistory.currentView()));
-
- if ($rootScope.lastState == "zm-portal-login") {
- NVRDataModel.debug("Last state was portal-login, so forcing montage");
- $rootScope.lastState = "montage";
- }
- NVRDataModel.debug("going to portal login");
- $ionicHistory.nextViewOptions({
- disableAnimate: true
- });
- $state.go("zm-portal-login");
- return;
- } else {
- $rootScope.lastState = "";
- $rootScope.lastStateParam = "";
- NVRDataModel.debug("reset lastState to null");
- $ionicHistory.nextViewOptions({
- disableAnimate: true
- });
- $state.go("zm-portal-login");
- return;
+ // remember the last state so we can
+ // go back there after auth
+ if ($ionicHistory.currentView())
+ {
+ $rootScope.lastState = $ionicHistory.currentView().stateName;
+ $rootScope.lastStateParam =
+ $ionicHistory.currentView().stateParams;
+ NVRDataModel.debug("Last State recorded:" +
+ JSON.stringify($ionicHistory.currentView()));
+
+ if ($rootScope.lastState == "zm-portal-login")
+ {
+ NVRDataModel.debug("Last state was portal-login, so forcing montage");
+ $rootScope.lastState = "montage";
}
-
+
+ NVRDataModel.debug("going to portal login");
+ $ionicHistory.nextViewOptions(
+ {
+ disableAnimate: true
+ });
+ $state.go("zm-portal-login");
+ return;
+ }
+ else
+ {
+ $rootScope.lastState = "";
+ $rootScope.lastStateParam = "";
+ NVRDataModel.debug("reset lastState to null");
+ $ionicHistory.nextViewOptions(
+ {
+ disableAnimate: true
+ });
+ $state.go("zm-portal-login");
+ return;
+ }
+
}, forceDelay);
}, false);
-
//---------------------------------------------------------------------------
// background handler
//----------------------------------------------------------------------------
- document.addEventListener("pause", function () {
+ document.addEventListener("pause", function()
+ {
NVRDataModel.setBackground(true);
NVRDataModel.setJustResumed(true); // used for window stop
@@ -1606,31 +1799,27 @@ angular.module('zmApp', [
$interval.cancel($rootScope.eventQueryInterval);
$interval.cancel($rootScope.intervalHandle);
-
NVRDataModel.log("ROOT APP: Stopping network pull...");
window.stop(); // dont call stopNetwork - we need to stop here
-
var ld = NVRDataModel.getLogin();
- if (ld.exitOnSleep && $rootScope.platformOS == "android") {
+ if (ld.exitOnSleep && $rootScope.platformOS == "android")
+ {
NVRDataModel.log("user exited app");
ionic.Platform.exitApp();
}
-
-
zmAutoLogin.stop();
if ($rootScope.zmPopup)
$rootScope.zmPopup.close();
}, false);
-
-
-
}
+ // URL interceptor
+
}); //platformReady
@@ -1641,7 +1830,8 @@ angular.module('zmApp', [
//------------------------------------------------------------------
// My route map connecting menu options to their respective templates and controllers
-.config(function ($stateProvider, $urlRouterProvider, $httpProvider, $ionicConfigProvider, $provide, $compileProvider, $ionicNativeTransitionsProvider, $logProvider, $translateProvider) {
+.config(function($stateProvider, $urlRouterProvider, $httpProvider, $ionicConfigProvider, $provide, $compileProvider, $ionicNativeTransitionsProvider, $logProvider, $translateProvider)
+{
//$logProvider.debugEnabled(false);
//$compileProvider.debugInfoEnabled(false);
@@ -1649,13 +1839,16 @@ angular.module('zmApp', [
// This is an exception interceptor so it can show up in app logs
// if they occur. I suspect digest and other errors will be useful
// for me to see
+ //$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|cdvphotolibrary):/);
- $provide.decorator("$exceptionHandler", ['$delegate', '$injector', function ($delegate, $injector) {
- return function (exception, cause) {
-
+ $provide.decorator("$exceptionHandler", ['$delegate', '$injector', function($delegate, $injector)
+ {
+ return function(exception, cause)
+ {
var $rootScope = $injector.get("$rootScope");
- $rootScope.exceptionMessage({
+ $rootScope.exceptionMessage(
+ {
reason: exception,
cause: cause
});
@@ -1669,48 +1862,52 @@ angular.module('zmApp', [
//$httpProvider.defaults.withCredentials = true;
$httpProvider.interceptors.push('timeoutHttpIntercept');
$ionicConfigProvider.navBar.alignTitle('center');
+ //$ionicConfigProvider.backButton.text('').icon('ion-chevron-left');
+ //$ionicConfigProvider.backButton.text('').icon('ion-chevron-left').previousTitleText(false);
// use overflow-scroll=false in ion-content
// removing it here doesn't allow you to enable it per view
// so it messes up scrolldelegate zoom and possibly others
//$ionicConfigProvider.scrolling.jsScrolling(false);
$compileProvider.debugInfoEnabled(false);
- $ionicNativeTransitionsProvider.setDefaultOptions({
+ $ionicNativeTransitionsProvider.setDefaultOptions(
+ {
duration: 250,
});
- $translateProvider.useStaticFilesLoader({
+ $translateProvider.useStaticFilesLoader(
+ {
prefix: 'lang/locale-',
suffix: '.json'
});
//$translateProvider.useLocalStorage();
-
- $translateProvider.registerAvailableLanguageKeys(['en', 'de', 'es', 'fr', 'it', 'ru', 'ja', 'ko', 'zh', 'zh_CN', 'zh_TW', 'pt', 'ar', 'hi'], {
+ $translateProvider.registerAvailableLanguageKeys(['en', 'de', 'es', 'fr', 'it', 'ru', 'ja', 'ko', 'pl', 'zh', 'zh_CN', 'zh_TW', 'pt', 'ar', 'hi'],
+ {
'en_*': 'en',
'de_*': 'de',
- 'es_*': 'es',
+ 'es_*': 'es', //already exists
'fr_*': 'fr',
'it_*': 'it',
- 'ru_*': 'ru',
+ 'ru_*': 'ru',
'ja_*': 'ja',
'ko_*': 'ko',
'pt_*': 'pt',
+ 'pl_*': 'pl',
'ar_*': 'ar',
'hi_*': 'hi',
'*': 'en' // must be last
});
-
-
//$translateProvider.determinePreferredLanguage();
//$translateProvider.preferredLanguage("en");
$translateProvider.fallbackLanguage("en");
$translateProvider.useSanitizeValueStrategy('escape');
$stateProvider
- .state('app', {
+ .state('app',
+ {
url: '/',
abstract: true,
templateUrl: 'index.html',
@@ -1719,22 +1916,23 @@ angular.module('zmApp', [
//controller: 'AppCtrl'
})
-
-
- .state('login', {
- data: {
+ .state('login',
+ {
+ data:
+ {
requireLogin: false
},
url: "/login/:wizard",
- cache: false,
+ cache: false,
templateUrl: "templates/login.html",
controller: 'zmApp.LoginCtrl',
})
-
- .state('help', {
- data: {
+ .state('help',
+ {
+ data:
+ {
requireLogin: false
},
url: "/help",
@@ -1744,8 +1942,10 @@ angular.module('zmApp', [
})
- .state('news', {
- data: {
+ .state('news',
+ {
+ data:
+ {
requireLogin: false
},
url: "/news",
@@ -1755,14 +1955,16 @@ angular.module('zmApp', [
})
-
-
- .state('monitors', {
- data: {
+ .state('monitors',
+ {
+ data:
+ {
requireLogin: true
},
- resolve: {
- message: function (NVRDataModel) {
+ resolve:
+ {
+ message: function(NVRDataModel)
+ {
// console.log("Inside app.montage resolve");
return NVRDataModel.getMonitors(0);
}
@@ -1772,15 +1974,18 @@ angular.module('zmApp', [
templateUrl: "templates/monitors.html",
controller: 'zmApp.MonitorCtrl',
-
})
- .state('events', {
- data: {
+ .state('events',
+ {
+ data:
+ {
requireLogin: true
},
- resolve: {
- message: function (NVRDataModel) {
+ resolve:
+ {
+ message: function(NVRDataModel)
+ {
//console.log("Inside app.events resolve");
return NVRDataModel.getMonitors(0);
}
@@ -1790,11 +1995,12 @@ angular.module('zmApp', [
templateUrl: "templates/events.html",
controller: 'zmApp.EventCtrl',
-
})
- .state('lowversion', {
- data: {
+ .state('lowversion',
+ {
+ data:
+ {
requireLogin: false
},
@@ -1803,11 +2009,12 @@ angular.module('zmApp', [
templateUrl: "templates/lowversion.html",
controller: 'zmApp.LowVersionCtrl',
-
})
- .state('importantmessage', {
- data: {
+ .state('importantmessage',
+ {
+ data:
+ {
requireLogin: false
},
@@ -1816,13 +2023,12 @@ angular.module('zmApp', [
templateUrl: "templates/important_message.html",
controller: 'zmApp.ImportantMessageCtrl',
-
})
-
-
- .state('events-graphs', {
- data: {
+ .state('events-graphs',
+ {
+ data:
+ {
requireLogin: true
},
cache: false,
@@ -1832,9 +2038,10 @@ angular.module('zmApp', [
})
-
- .state('events-date-time-filter', {
- data: {
+ .state('events-date-time-filter',
+ {
+ data:
+ {
requireLogin: true
},
cache: false,
@@ -1844,8 +2051,10 @@ angular.module('zmApp', [
})
- .state('state', {
- data: {
+ .state('state',
+ {
+ data:
+ {
requireLogin: true
},
cache: false,
@@ -1855,8 +2064,10 @@ angular.module('zmApp', [
})
- .state('devoptions', {
- data: {
+ .state('devoptions',
+ {
+ data:
+ {
requireLogin: true
},
url: "/devoptions",
@@ -1865,29 +2076,37 @@ angular.module('zmApp', [
controller: 'zmApp.DevOptionsCtrl',
})
- .state('timeline', {
- data: {
+ .state('timeline',
+ {
+ data:
+ {
requireLogin: true
},
- resolve: {
- message: function (NVRDataModel) {
+ resolve:
+ {
+ message: function(NVRDataModel)
+ {
//console.log("Inside app.events resolve");
return NVRDataModel.getMonitors(0);
}
},
url: "/timeline",
- cache:false,
+ cache: false,
templateUrl: "templates/timeline.html",
controller: 'zmApp.TimelineCtrl',
})
- .state('eventserversettings', {
- data: {
+ .state('eventserversettings',
+ {
+ data:
+ {
requireLogin: true
},
- resolve: {
- message: function (NVRDataModel) {
+ resolve:
+ {
+ message: function(NVRDataModel)
+ {
return NVRDataModel.getMonitors(0);
}
},
@@ -1898,8 +2117,10 @@ angular.module('zmApp', [
})
- .state('log', {
- data: {
+ .state('log',
+ {
+ data:
+ {
requireLogin: false
},
url: "/log",
@@ -1909,8 +2130,10 @@ angular.module('zmApp', [
})
- .state('wizard', {
- data: {
+ .state('wizard',
+ {
+ data:
+ {
requireLogin: false
},
url: "/wizard",
@@ -1920,8 +2143,10 @@ angular.module('zmApp', [
})
- .state('zm-portal-login', {
- data: {
+ .state('zm-portal-login',
+ {
+ data:
+ {
requireLogin: false
},
url: "/zm-portal-login",
@@ -1932,8 +2157,10 @@ angular.module('zmApp', [
})
- .state('first-use', {
- data: {
+ .state('first-use',
+ {
+ data:
+ {
requireLogin: false
},
url: "/first-use",
@@ -1943,12 +2170,16 @@ angular.module('zmApp', [
})
- .state('montage-history', {
- data: {
+ .state('montage-history',
+ {
+ data:
+ {
requireLogin: true
},
- resolve: {
- message: function (NVRDataModel) {
+ resolve:
+ {
+ message: function(NVRDataModel)
+ {
//console.log("Inside app.events resolve");
return NVRDataModel.getMonitors(0);
}
@@ -1958,20 +2189,24 @@ angular.module('zmApp', [
url: "/montage-history",
templateUrl: "templates/montage-history.html",
controller: 'zmApp.MontageHistoryCtrl',
- params: {
+ params:
+ {
minimal: false,
isRefresh: false
},
-
})
- .state('montage', {
- data: {
+ .state('montage',
+ {
+ data:
+ {
requireLogin: true
},
- resolve: {
- message: function (NVRDataModel) {
+ resolve:
+ {
+ message: function(NVRDataModel)
+ {
//console.log("Inside app.events resolve");
return NVRDataModel.getMonitors(0);
}
@@ -1981,15 +2216,15 @@ angular.module('zmApp', [
cache: false,
templateUrl: "templates/montage.html",
controller: 'zmApp.MontageCtrl',
- params: {
+ params:
+ {
minimal: false,
isRefresh: false
},
-
});
// We are NOT going to default route. Routing to a view will start on
// a broadcast of "init-complete"
-}); //config \ No newline at end of file
+}); //config
diff --git a/www/js/ionicUtils.js b/www/js/ionicUtils.js
index ae05293b..c593624c 100644
--- a/www/js/ionicUtils.js
+++ b/www/js/ionicUtils.js
@@ -6,22 +6,27 @@
angular.module('ionic.utils', [])
-.factory('$localstorage', ['$window', function($window) {
- return {
+.factory('$localstorage', ['$window', function($window)
+{
+ return {
- init: function() {},
+ init: function() {},
- set: function(key, value) {
- $window.localStorage[key] = value;
- },
- get: function(key, defaultValue) {
- return $window.localStorage[key] || defaultValue;
- },
- setObject: function(key, value) {
- $window.localStorage[key] = JSON.stringify(value);
- },
- getObject: function(key) {
- return JSON.parse($window.localStorage[key] || '{}');
- }
- };
-}]); \ No newline at end of file
+ set: function(key, value)
+ {
+ $window.localStorage[key] = value;
+ },
+ get: function(key, defaultValue)
+ {
+ return $window.localStorage[key] || defaultValue;
+ },
+ setObject: function(key, value)
+ {
+ $window.localStorage[key] = JSON.stringify(value);
+ },
+ getObject: function(key)
+ {
+ return JSON.parse($window.localStorage[key] || '{}');
+ }
+ };
+}]);
diff --git a/www/lang/README.md b/www/lang/README.md
index bf5f23f3..b2737f3c 100644
--- a/www/lang/README.md
+++ b/www/lang/README.md
@@ -1,6 +1,10 @@
+
+If you are familiar with using git, I'd prefer if you follow the Pull Request process [here](https://github.com/pliablepixels/zmNinja/blob/master/CONTRIBUTING.md#steps-for-code-contribution).
+
####Adding a new language
+* Languages translations are available [here](https://github.com/pliablepixels/zmNinja/tree/master/www/lang)
* To contribute a new language, add a new ``locale-xx.json`` (where `xx` is your language code).
-* Ideally, you should also provide a language translation for the zmNinja help file inside ``lang/help``
+* Ideally, you should also provide a language translation for the zmNinja help file inside [lang/help](https://github.com/pliablepixels/zmNinja/tree/master/www/lang/help)
The best way is to simply look at an existing language translation and follow the same model for yours. If any language translation keywords are missed, it will fallback to English.
@@ -12,27 +16,18 @@ The best way is to simply look at an existing language translation and follow th
(replace ``-it`` with the language you are working on)
-Python 2.x:
+
``
./checklang.py -f locale-it.json -b
``
-Python 3.x:
-``
-./checklang.py3 -f locale-it.json -b
-``
This validates your JSON file, makes sure all keys are in sync with -en and if valid, creates pretty-locale-it.json. If you are sure it looks good,
-Python 2.x:
``
./checklang.py -f locale-it.json -b -o
``
-Python 3.x:
-``
-./checklang.py3 -f locale-it.json -b -o
-``
This validates your JSON file,makes sure all keys are in sync with -en and if valid, OVERWRITES your local file with a pretty formatted version, which is what you should PR
diff --git a/www/lang/checklang.py b/www/lang/checklang.py
index cf3291fe..307f51df 100755
--- a/www/lang/checklang.py
+++ b/www/lang/checklang.py
@@ -1,8 +1,9 @@
#!/usr/bin/env python
+from __future__ import absolute_import, division, print_function, unicode_literals
+import sys
import json
import os
import getopt
-import sys
globGood=0
globBad=0
@@ -12,13 +13,14 @@ globFile=""
# prints usage
def usage():
- print (
+ print((
'Usage: '+sys.argv[0]+'\n'
+ ' called without arguments runs a check without any modifications\n'
' -h|--help: this help\n'
' -f|file <fname>: only processes that file\n'
' -b|--beautify: beautifies the json file\n'
' -o|--overwrite: when used with -b/--beautify overwrites the file without adding a pretty- prefix. Use with caution\n'
- )
+ ))
#beautifies a given file
@@ -28,28 +30,31 @@ def beautify(fi,ki):
prefix=''
else:
prefix='pretty-'
- print "Beautifying %s, writing to %s" % (fi,prefix+fi)
+ print("Beautifying %s, writing to %s" % (fi,prefix+fi))
w = len (max(ki, key=len))
pretty=[]
for k in sorted(ki):
line = " \"%s\"%s:\"%s\"" %(k,' '*(w-len(k)+1),ki[k])
pretty.append(line)
- pFh=open (prefix+fi,"w")
- pFh.write("{\n")
- pFh.write(',\n'.join(pretty).encode('utf-8'))
- pFh.write("\n}\n")
+ pFh=open(prefix+fi,"w")
+ pFh.write('{\n')
+ if sys.version_info >=(3, 0):
+ pFh.write(",\n".join(pretty))
+ else:
+ pFh.write(",\n".join(pretty).encode('UTF-8'))
+ pFh.write('\n}\n')
pFh.close()
#Compares keys in language file
-def compare (fname):
+
+def compare(fname):
beaut="no"
global globGood, globBad,globOverwrite, globFile, globBeautify
with open (i) as json_data:
try:
newKeys = json.load(json_data)
- except ValueError as e:
- print 'could not parse %s, skipping!' %fname
- print 'Error was %s' % str(e)
+ except ValueError:
+ print('could not parse %s, skipping!' %fname)
globBad+=1
return
json_data.close()
@@ -58,23 +63,23 @@ def compare (fname):
if len(diffOrig)==0 and len (diffNew)==0:
status = "GOOD"
globGood+=1
- if globBeautify and globFile == fname or globFile == "":
+ if globBeautify and (globFile == fname or globFile == ""):
beaut="YES"
else:
status = "ERROR"
globBad+=1
- print "\n-------Checking:%s:%s, beautify:%s---------" % (fname,status,beaut)
- print "master keys:%d, %s keys:%d" % (len(origKeys), i, len(newKeys))
+ print("\n-------Checking:%s:%s, beautify:%s---------" % (fname,status,beaut))
+ print("master keys:%d, %s keys:%d" % (len(origKeys), i, len(newKeys)))
if beaut=="YES":
beautify(fname,newKeys)
if len(diffOrig) > 0:
- print "Keys not present in :%s" %fname
+ print("Keys not present in :%s" %fname)
for x in diffOrig:
- print '-->',x
+ print("-->",x)
if len(diffNew) > 0:
- print "Extra keys present in :%s" %fname
+ print("Extra keys present in :%s" %fname)
for x in diffNew:
- print '-->',x
+ print("-->",x)
@@ -101,7 +106,7 @@ with open ('locale-en.json') as json_data:
origKeys=json.load(json_data)
json_data.close()
-print "total keys in master language: ", len(origKeys)
+print("total keys in master language: ", len(origKeys))
#iterate through all languages, using -en as the master
for i in os.listdir(os.getcwd()):
@@ -112,11 +117,11 @@ for i in os.listdir(os.getcwd()):
if globFile == "" or globFile == i:
compare(i)
else:
- print "skipping ",i, " as its not ",globFile
+ print("skipping ",i, " as its not ",globFile)
-print "================================================="
-print "Good files:%d, Bad files:%d, Total files:%d" % (globGood, globBad, globGood+globBad)
-print "=================================================\n"
+print("=================================================")
+print("Good files:%d, Bad files:%d, Total files:%d" % (globGood, globBad, globGood+globBad))
+print("=================================================\n")
diff --git a/www/lang/checklang.py3 b/www/lang/checklang.py3
deleted file mode 100755
index 10963da2..00000000
--- a/www/lang/checklang.py3
+++ /dev/null
@@ -1,121 +0,0 @@
-#!/usr/bin/env python
-import json
-import os
-import getopt
-import sys
-
-globGood=0
-globBad=0
-globBeautify=0
-globOverwrite=0
-globFile=""
-
-# prints usage
-def usage():
- print((
- 'Usage: '+sys.argv[0]+'\n'
- ' -h|--help: this help\n'
- ' -f|file <fname>: only processes that file\n'
- ' -b|--beautify: beautifies the json file\n'
- ' -o|--overwrite: when used with -b/--beautify overwrites the file without adding a pretty- prefix. Use with caution\n'
- ))
-
-
-#beautifies a given file
-def beautify(fi,ki):
- global globOverwrite
- if globOverwrite:
- prefix=''
- else:
- prefix='pretty-'
- print("Beautifying %s, writing to %s" % (fi,prefix+fi))
- w = len (max(ki, key=len))
- pretty=[]
- for k in sorted(ki):
- line = " \"%s\"%s:\"%s\"" %(k,' '*(w-len(k)+1),ki[k])
- pretty.append(line)
- pFh=open (prefix+fi,"w")
- pFh.write("{\n")
- pFh.write(',\n'.join(pretty).encode('utf-8'))
- pFh.write("\n}\n")
- pFh.close()
-
-#Compares keys in language file
-def compare (fname):
- beaut="no"
- global globGood, globBad,globOverwrite, globFile, globBeautify
- with open (i) as json_data:
- try:
- newKeys = json.load(json_data)
- except ValueError:
- print('could not parse %s, skipping!' %fname)
- globBad+=1
- return
- json_data.close()
- diffOrig = set(origKeys.keys()) - set(newKeys.keys())
- diffNew = set(newKeys.keys()) - set(origKeys.keys())
- if len(diffOrig)==0 and len (diffNew)==0:
- status = "GOOD"
- globGood+=1
- if globBeautify and globFile == fname or globFile == "":
- beaut="YES"
- else:
- status = "ERROR"
- globBad+=1
- print("\n-------Checking:%s:%s, beautify:%s---------" % (fname,status,beaut))
- print("master keys:%d, %s keys:%d" % (len(origKeys), i, len(newKeys)))
- if beaut=="YES":
- beautify(fname,newKeys)
- if len(diffOrig) > 0:
- print("Keys not present in :%s" %fname)
- for x in diffOrig:
- print("-->",x)
- if len(diffNew) > 0:
- print("Extra keys present in :%s" %fname)
- for x in diffNew:
- print("-->",x)
-
-
-
-#MAIN
-try:
- myopts,args=getopt.getopt(sys.argv[1:],"f:hob",["file=","help","overwrite","beautify"])
-except getopt.GetoptError as err:
- print (err)
- usage()
- sys.exit(2)
-
-for o,a in myopts:
- if o in ("-h","--help"):
- usage()
- sys.exit()
- elif o in ("-b","--beautify"):
- globBeautify=1
- elif o in ("-o","--overwrite"):
- globOverwrite=1
- elif o in ("-f","--file"):
- globFile=a
-
-with open ('locale-en.json') as json_data:
- origKeys=json.load(json_data)
- json_data.close()
-
-print("total keys in master language: ", len(origKeys))
-
-#iterate through all languages, using -en as the master
-for i in os.listdir(os.getcwd()):
- if not i.endswith(".json") or not i.startswith("locale-"):
- #print "skipping ",i," as we will only process locale-*.json"
- continue
- else:
- if globFile == "" or globFile == i:
- compare(i)
- else:
- print("skipping ",i, " as its not ",globFile)
-
-print("=================================================")
-print("Good files:%d, Bad files:%d, Total files:%d" % (globGood, globBad, globGood+globBad))
-print("=================================================\n")
-
-
-
diff --git a/www/lang/help/help-es.html b/www/lang/help/help-es.html
new file mode 100644
index 00000000..4a03ec60
--- /dev/null
+++ b/www/lang/help/help-es.html
@@ -0,0 +1,73 @@
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+<ion-item class="item-text-wrap">
+ <h2><b>¿Hay una sección de preguntas frecuentes oficial?</b> </h2>Así es. Recuerda siempre revisar <a href="#" onclick="window.open('https://github.com/pliablepixels/zmNinja/wiki/FAQ', '_blank', 'location=yes'); return false;">aquí</a> para tener la ayuda más actualizada.
+ </ion-item>
+
+ <ion-item class="item-text-wrap">
+ <h2><b> ¿Qué debo ingresar en los campos de URL del Portal Web de ZM, ruta de cgi-bin y URL API de ZM?</b> </h2>
+ <ul>
+ {{$root.appName}} ahora tiene un ayudante que trata de detectar tus ajustes. Este puede fallar si no tiene una instalación por defecto con rutas de archivo estándar.
+ <li><i class="ion-android-arrow-dropright"></i> URL Portal Web ZM: El URL que usa para acceder a ZM.
+ <p>(ejemplo: http://myserver.ddns.net/zm)</p>
+ </li>
+ <li><i class="ion-android-arrow-dropright"></i> Ruta CGI: El URL utilizado desde el cual la ruta de cgi-bin es accesible.
+ <p>(ejemplo: http://myserver.ddns.net/zm/cgi-bin).</p>
+ </li>
+ <li><i class="ion-android-arrow-dropright"></i> URL de la API: La URL desde la cual la ruta de la API es accesible.
+ <p>(ejemplo: http://myserver.ddns.net/zm/api)</p>
+ </li>
+ </ul>
+ </ion-item>
+
+ <ion-item class="item-text-wrap">
+ <h2><b>¿Desde cuándo ZM tiene una API?</b> </h2> Las últimas versiones de ZoneMinder tienen una API que supuestamente reemplaza el skin XML. Sólo está disponible a partir de la versión 1.28.107 de Zoneminder. Si no tiene la API instalada el cliente no funcionará.
+
+ </ion-item>
+
+ <ion-item class="item-text-wrap">
+ <h2> <b>No tengo habilitada la autenticación pero la app sigue pidiéndome que especifique una autenticación</b></h2> Si no usa autenticación ZM, sólo tiene que ingresar "x" en el nombre y en la contraseña.
+ </ion-item>
+
+
+ <ion-item class="item-text-wrap">
+ <h2> <b>¡Todo funciona! Excepto el streaming en tiempo real </b></h2> Revise si el streaming funciona en la interfaz web. Si no funciona, {{$root.appName}} tampoco funcionará. Solucione esto en ZM antes. Si el streaming funciona en ZM, vaya a los ajustes {{$root.appName}} y arregle la ruta de cgi-bin. La ruta que se llena automáticamente no funcionará. Esta es una pista, ve a zoneminder-options-paths y revise el campo de la ruta a cgi-bin - la ruta de {{$root.appName}} será la "ruta base para el servidor" + la ruta del cgi-bin.
+ </ion-item>
+
+
+ <ion-item class="item-text-wrap">
+ <h2><b> ¡La información no está actualizándose! </b></h2> La mayoría de las pantallas que se muestran en la lista de datos tiene una acción para halar la pantalla. Hale la pantalla para refrescar y sus datos serán recargados.
+ </ion-item>
+
+ <ion-item class="item-text-wrap">
+ <h2><b> ¿Qué significan todos los colores en la vista de Monitor?</b></h2>
+ <i class="icon ion-checkmark-circled" style="color:#03A9F4"></i> Revisando
+ <br/>
+ <i class="icon ion-checkmark-circled" style="color:#4CAF50"></i> Todo bien
+ <br/>
+ <i class="icon ion-close-circled" style="color:#F44336"></i> No corriendo
+ <br/>
+ <i class="icon ion-close-circled" style="color:#FF9800"></i> No corriendo (pendiente)
+ <br/>
+ <i class="icon ion-checkmark-circled" style="color:grey"></i> Deshabilitado
+ <br/>
+ <i class="icon ion-help-circled" style="color:#795548"></i> Desconocido
+ <br/>
+
+ </ion-item>
+
+ <ion-item class="item-text-wrap">
+ <h2><b>¡Quiero contribuir!</b></h2> Genial. ¡<a href="#" onclick="window.open('http://github.com/pliablepixels/zmNinja', '_blank', 'location=yes'); return false;">Aquí está</a> el código fuente!
+ </ion-item>
+
+ <ion-item class="item-text-wrap">
+ <h2><b>Estoy teniendo problemas con esta app</b></h2>Tap en el registro en el menú de opciones. Cliquée el botón de email en la parte superior derecha para enviar los problemas al autor de esta app. Note que cuando el email está listo, zmNinja tratará de quitar la información personal buscando reemplazar la dirección IP del servidor y las contraseñas del mismo. Sin embargo es <b>su responsabilidad</b> asegurarse que los registros no tengan información sensible. Antes de enviar el email, por favor revise los registros embebidos y edítelos de ser necesario.
+ </ion-item>
+
+ <ion-item class="item-text-wrap">
+ <h2><b>Puedo ver la dirección URL de mi servidor y mis contraseñas en el registro. ¿No dijeron que zmNinja las quita?</b></h2>zmNinja trata de quitarlas <b>después</b> de clickar el botón de email. Supongo que también podría quitarlas de los registros guardados, pero entonces sería más difícil para usted darse cuenta si cometió una equivocación al momento de configurar la app.
+ </ion-item>
+
+ <ion-item class="item-text-wrap">
+ <h2><b>¿Cómo puedo contactar al autor?</b></h2> Envíame un <a href="#" onclick="window.open('mailto:pliablepixels@gmail.com', '_blank', 'location=yes'); return false;">email</a>.
+ </ion-item>
+
diff --git a/www/lang/help/help-pl.html b/www/lang/help/help-pl.html
new file mode 100644
index 00000000..cce3a7f8
--- /dev/null
+++ b/www/lang/help/help-pl.html
@@ -0,0 +1,71 @@
+<ion-item class="item-text-wrap">
+ <h2><b>Czy istnieje oficjalne FAQ?</b> </h2>Oczywiście. Zawsze zaglądaj <a href="#" onclick="window.open('https://github.com/pliablepixels/zmNinja/wiki/FAQ', '_blank', 'location=yes'); return false;">tutaj</a> w celu uzyskania najnowszej pomocy.
+ </ion-item>
+
+ <ion-item class="item-text-wrap">
+ <h2><b> Co mam wprowadzić w miejsce Portal ZM URL, Główną ścieżkę do cgi-bin i URL ZM API?</b> </h2>
+ <ul>
+ {{$root.appName}} posiada teraz kreator, który próbuje wykryć Twoje ustawienia. Może nie zadziałać, jeśli posiadasz instalację niestandardową ZM ze zmienionymi ścieżkami.
+ <li><i class="ion-android-arrow-dropright"></i> URL Portalu ZM: URL pod którym jest dostępny ZM
+ <p>(przykład: http://myserver.ddns.net/zm)</p>
+ </li>
+ <li><i class="ion-android-arrow-dropright"></i> Ścieżka CGI: URL pod którym jest dostępne cgi-bin
+ <p>(przykład: http://myserver.ddns.net/zm/cgi-bin).</p>
+ </li>
+ <li><i class="ion-android-arrow-dropright"></i> API URL: URL pod którym jest dostępne API
+ <p>(przykład: http://myserver.ddns.net/zm/api)</p>
+ </li>
+ </ul>
+ </ion-item>
+
+ <ion-item class="item-text-wrap">
+ <h2><b>Od kiedy ZM posiada API?</b> </h2> Ostatnie wersje ZoneMinder'a posiadają API, które powinno zastąpić skórkę XML. Jest osiągalne dopiero od wersji 1.28.107 Zoneminder'a. Jeśli nie masz zainstalowanego API, klient nie zadziała.
+
+ </ion-item>
+
+ <ion-item class="item-text-wrap">
+ <h2> <b>Nie mam ustawionego uwierzytelniania, ale aplikacja ciągle prosi mnie o jego wprowadzenie</b></h2> Jeśli nie używasz uwierzytelniania ZM, wprowadź "x" w oba pola - użytkownik i hasło.
+ </ion-item>
+
+
+ <ion-item class="item-text-wrap">
+ <h2> <b>Wszystko działa, tylko nie podgląd na żywo </b></h2> Sprawdź czy podgląd działa w interfejsie www. Jeśłi nie działa, {{$root.appName}} też nie zadziała. Zacznij od naprawy ZM. Jeśli streaming w ZM działa, przejdź do ustawień {{$root.appName}} i napraw ścieżkę cgi-bin. Jeśli automatycznie wprowadzona ścieżka cgi-bin nie chce działać, spróbuj przejść do zoneminder-options-paths i sprawdź wartości ścieżki cgi-bin - ścieżka {{$root.appName}} będzie "główna ścieżka Twojego serwera" + ścieżka cgi-bin.
+ </ion-item>
+
+
+ <ion-item class="item-text-wrap">
+ <h2><b> Dane się nie odświeżają!</b></h2> Większość ekranów, które pokazują listy danych, używają funkcji pociągnij w dół/naciągnij. Naciągnij by odświeżyć, a Twoje dane zostaną przeładowane.
+ </ion-item>
+
+ <ion-item class="item-text-wrap">
+ <h2><b> Co oznaczają wszystkie te kolory w Monitorze?</b></h2>
+ <i class="icon ion-checkmark-circled" style="color:#03A9F4"></i> Sprawdzanie
+ <br/>
+ <i class="icon ion-checkmark-circled" style="color:#4CAF50"></i> Wszystko dobrze
+ <br/>
+ <i class="icon ion-close-circled" style="color:#F44336"></i> Nie działa
+ <br/>
+ <i class="icon ion-close-circled" style="color:#FF9800"></i> Nie działa (oczekuje)
+ <br/>
+ <i class="icon ion-checkmark-circled" style="color:grey"></i> Wyłączony
+ <br/>
+ <i class="icon ion-help-circled" style="color:#795548"></i> Nieznany
+ <br/>
+
+ </ion-item>
+
+ <ion-item class="item-text-wrap">
+ <h2><b>Chiałbym się jakoś przysłużyć!</b></h2> Świetnie. <a href="#" onclick="window.open('http://github.com/pliablepixels/zmNinja', '_blank', 'location=yes'); return false;"> Zgarnij</a> kod źródłowy!
+ </ion-item>
+
+ <ion-item class="item-text-wrap">
+ <h2><b>Miewam problemy z tą aplikacją</b></h2>Wejdź w opcję logów w menu. Kliknij przycisk mail w górnym prawym by wysłać logi do autora tej aplikacji. Zauważ, że gdy mail zostanie skomponowany, zmNinja stara się usunąć informacje osobiste wyszukując i zmieniając IP serwera i jego hasła. Tak czy inaczej, sprawdzenie czy logi nie zawierają poufnych danych, to <b>Twoja odpowiedzialność</b>. Zanim wyślesz maila sprawdź czy nie zawiera ważnych informacji i ewentualnie je wyedytuj.
+ </ion-item>
+
+ <ion-item class="item-text-wrap">
+ <h2><b>Adres mojego serwera i hasła są w logach. Mówiłeś, że zmNinja je usunie?</b></h2>zmNinja próbuje je usunąć <b>po tym</b> jak klikniesz w przycisk mail. Mógłbym też usunąć je w zachowanych logach, ale wtedy było by Ci ciężej sprawdzić czy nie popełniłeś błędu konfigurując aplikację.
+ </ion-item>
+
+ <ion-item class="item-text-wrap">
+ <h2><b>Jak się skontaktuję z autorem?</b></h2> Wyślij mi <a href="#" onclick="window.open('mailto:pliablepixels@gmail.com', '_blank', 'location=yes'); return false;">mail</a>
+</ion-item>
diff --git a/www/lang/locale-en.json b/www/lang/locale-en.json
index d9e70a12..769631ba 100644
--- a/www/lang/locale-en.json
+++ b/www/lang/locale-en.json
@@ -61,11 +61,13 @@
"kDisableNative" :"Disable Native transitions",
"kDisableNativeSub" :"Enable if the menu is freezing",
"kDisablePush" :"disable APNS/GCM",
+ "kDisableSamsung" :"If you are on a samsung device and are facing input issues, please temporarily disable auto-correction",
"kDiscovering" :"discovering",
"kDiscoveringAPI" :"discovering api",
"kDiscoveringCGI" :"discovering cgi",
"kDiscoveringPortal" :"discovering portal",
"kDone" :"done",
+ "kDownload" :"download",
"kEnable24hr" :"enable 24hr time format",
"kEnableDebug" :"Enable debug logs",
"kEnableLogs" :"Enable logs",
@@ -127,11 +129,14 @@
"kFrom" :"From",
"kFromDate" :"From Date",
"kFromTime" :"From Time",
+ "kGifWarning" :"The GIF animation will only be of alarmed frames and 1fps",
+ "kGifNoCrosswalk" : "Sorry, you need to be on Android 5.0 (Lollipop) or above for this feature to work.",
"kGlobalConfiguration" :"Global Configuration",
"kGraphAlarmed" :"alarmed",
"kGraphAll" :"all",
"kGraphError" :"there was an error rendering the graph. Please see logs",
"kH264VideoSupport" :"H264 Video support",
+ "kHelp" : "Help",
"kHideMonsWithoutEvents" :"Hide monitors without events",
"kHideTip" :"hide tip",
"kHighBWDisplay" :"high bandwidth",
@@ -150,6 +155,7 @@
"kLiveView" :"Live View",
"kLoad" :"load",
"kLoading" :"loading",
+ "kLoadingEvents" : "loading events",
"kLoadingGraph" :"loading graph",
"kLoadingMonitors" :"loading monitors",
"kLocalTimeZone" :"use local timezone",
@@ -197,11 +203,15 @@
"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",
"kNegotiatingStreamAuth" :"negotiating stream authentication",
- "kNews" :"News",
+ "kNews" :"News (latest 10)",
+ "kNewPost" : "new post",
"kNext" :"Next",
"kNextEvent" :"next event",
"kNextMonitor" :"next monitor",
@@ -209,6 +219,8 @@
"kNoMonitors" :"No monitors to display",
"kNoMoreEvents" :"no more events",
"kNormalPlay" :"normal play",
+ "kNote" : "Note",
+ "kNow" :"now",
"kOff" :"off",
"kOn" :"on",
"kOnTapNavigate" :"on tap, navigate to",
@@ -275,6 +287,9 @@
"kSearch" :"search",
"kSearchCancelled" :"search cancelled",
"kSec" :"sec",
+ "kSelect" :"Please Select",
+ "kSelectDelete" :"Selected profile will be deleted",
+ "kSelectSwitch" :"Selected profile will be loaded",
"kSelectFallback" :"Select fallback",
"kSelectLanguage" :"Select Language",
"kSelectRunState" :"Select run state",
@@ -339,6 +354,10 @@
"kVersionIncompatible" :"I am incompatible with your ZoneMinder version",
"kVibrateOnPush" :"Vibrate on push",
"kVideo" :"Video",
+ "kVideoError" : "Video not playable.",
+ "kVideoErrorMobile" : "Video not playable. Try enabling 'force image path for events' in Dev Settings. The format may also be incompatible with a mobile system view",
+ "kVideoLoading" : "Loading Video",
+ "kVideoMp4Warning" : "It is currently not possible to know when video is fully downloaded. Please track file size of download.",
"kWake" :"Wake",
"kWarningLargeTimeline" :"A large value can affect timeline performance. If you find timeline performance slow, try reducing the value to 200 and work your way up from there.",
"kWeek" :"Week",
diff --git a/www/lang/locale-es.json b/www/lang/locale-es.json
new file mode 100644
index 00000000..51759fca
--- /dev/null
+++ b/www/lang/locale-es.json
@@ -0,0 +1,383 @@
+{
+ "k1DaySummary" :"resúmen de 1 día",
+ "k1HourSummary" :"resúmen de 1 hora",
+ "k1MonthSummary" :"resúmen de 1 mes",
+ "k1WeekSummary" :"resúmen de 1 semana",
+ "kAlarmAPIError" :"error - por favor cerciórese que su API soporte esta función",
+ "kAlarmFrameCount" :"Cuenta de Fotogramas en Alarma",
+ "kAlarmMaxFPS" :"Max FPS en Alarma",
+ "kAlarms" :"Alarmas",
+ "kAll" :"Todo",
+ "kAnalyze" :"Analizar",
+ "kApiUrl" :"url de la api de ZM",
+ "kApplyingChanges" :"Aplicando cambios. Por favor espere",
+ "kArrangingImages" :"ordenando imágenes",
+ "kAt" :"at",
+ "kAuthSuccess" :"autenticación exitosa",
+ "kAuthenticating" :"autenticando",
+ "kAutoSwitchBW" :"auto switch ancho de banda",
+ "kAwake1" :"Mantener la pantalla encendida",
+ "kAwake2" :"(mientras se ve el montaje)",
+ "kBannerAPICheckFailed" :"falló el chequeo del API",
+ "kBannerCannotDeleteNeedOne" :"No se puede eliminar, se necesita al menos uno para eliminar",
+ "kBannerPinMismatch" :"Código pin no concuerda",
+ "kBannerPleaseCheck" :"Por favor revise las configuraciones",
+ "kBodyPortalNotConfigured" :"Por favor llene con sus detalles de inicio de sesión así como su url del Portal Web y guarde los cambios antes de intentar de detectar el cgi-path",
+ "kButtonCancel" :"Cancelar",
+ "kButtonClear" :"Limpiar",
+ "kButtonNo" :"No",
+ "kButtonOk" :"OK",
+ "kButtonSave" :"Guardar",
+ "kButtonYes" :"Sí",
+ "kCalcEventSize" :"calculando el tamaño de los eventos",
+ "kCancellingAlarm" :"cancelando alarma",
+ "kChangeSettingsFor" :"Cambiar las configuraciones para",
+ "kChangeState" :"Cambiar Estado",
+ "kCheckCredentials" :"Por favor comprueba tus credenciales",
+ "kChromeMax" :"máximo de 5 monitores - límite de chrome",
+ "kCleaningUp" :"limpiando",
+ "kClear" :"Limpiar",
+ "kCollapse" :"plegar",
+ "kConfiguration" :"Configuración",
+ "kControl" :"control",
+ "kCredentialsBody" :"Por favor introduzca sus credenciales de ZoneMinder",
+ "kCredentialsTitle" :"Credenciales Requeridas",
+ "kCurrentState" :"estado actual",
+ "kCustomRange" :"Rango Personalizado",
+ "kCycleMonitors" :"monitores en ciclo",
+ "kCycleMonitorsInterval" :"Intervalo del ciclo",
+ "kDay" :"Día",
+ "kDecreaseSize" :"disminuir el tamaño",
+ "kDelete" :"Borrar",
+ "kDeleteEventError1" :"no se pudieron borrar los eventos",
+ "kDeleteEventError2" :"por favor revisa los registros",
+ "kDeleteEventSuccess" :"evento borrado",
+ "kDeleteLogsConfirm" :"¿Está seguro que quiere borrar los registros?",
+ "kDeletingEvent" :"borrando evento",
+ "kDevOptions" :"Opc. de Desarrollador",
+ "kDeveloperOptionsFor" :"Opciones de Desarrollador para",
+ "kDisableAlarmMontage" :"Deshabilitar el API de la alarma en montaje",
+ "kDisableAlarmMontageSub" :"puede ser de ayuda si el servidor se sobrecarga",
+ "kDisableNative" :"Deshabilitar transiciones nativas",
+ "kDisableNativeSub" :"Habilitar si el menú se congela",
+ "kDisablePush" :"deshabilitar APNS/GCM",
+ "kDisableSamsung" :"Si estás configurando desde un dispositivo samsung y tienes problemas al ingresar los datos, por favor, deshabilita la autocorrección temporalmente.",
+ "kDiscovering" :"detectando",
+ "kDiscoveringAPI" :"detectando api",
+ "kDiscoveringCGI" :"detectando cgi",
+ "kDiscoveringPortal" :"buscando portal web",
+ "kDone" :"listo",
+ "kDownload" :"descarga",
+ "kEnable24hr" :"Habilitar formato de 24hr",
+ "kEnableDebug" :"Habilitar registro de depuración",
+ "kEnableLogs" :"Habilitar registro",
+ "kEnableNewsUpdates" :"Habilitar actualización de noticias",
+ "kEnterPin" :"Ingrese PIN",
+ "kError" :"Error",
+ "kErrorChangingMonitors" :"Error al cambiar los monitores. Por favor revisar los registros",
+ "kErrorFrameBanner" :"no se pudieron obtener detalles de los cuadros",
+ "kErrorPleaseTryAgain" :"por favor intente de nuevo",
+ "kErrorRetrievingFrames" :"error obteniendo los cuadros",
+ "kErrorRetrievingState" :"error obteniendo el estado",
+ "kErrorSave" :"Error - no se puede guardar",
+ "kEvent" :"evento",
+ "kEventHistFaster" :"más rápido",
+ "kEventHistHrs" :"horas antes",
+ "kEventHistPause" :"pausa",
+ "kEventHistPlay" :"reproducir",
+ "kEventHistShowFrom" :"Mostrar desde",
+ "kEventHistSlower" :"más lento",
+ "kEventMontage" :"Montaje de Evento",
+ "kEventMontageImageScale" :"Escala de imágen en Montaje de Evento",
+ "kEventNavVidFeeds" :"Navegación por eventos no está disponible con señales de vídeo. ZoneMinder todavía no tiene esta característica",
+ "kEventRecording" :"Grabar Evento",
+ "kEventServer" :"Servidor de Eventos",
+ "kEventServerConfig1" :"Por favor cerciórese que sus ajustes de ZM estén configurados y grabados antes que configure el servidor de eventos.",
+ "kEventServerVersionBody1" :"Estás corriendo la versión",
+ "kEventServerVersionBody2" :"Por favor actualice a",
+ "kEventServerVersionTitle" :"Versión de Servidor de Eventos no soportada.",
+ "kEventSingleImageScale" :"Escala de imágen de evento simple",
+ "kEventView" :"Vista de Evento",
+ "kEvents" :"eventos",
+ "kEventsCap" :"Eventos",
+ "kExampleServer" :"ej. Mi Casa",
+ "kExitAppBackground" :"Salir de la app en segundo plano",
+ "kExitEventView" :"salir de la vista de evento",
+ "kExitFullScreen" :"salir de pantalla completa",
+ "kExitLiveView" :"salir de la vista en vivo",
+ "kExpert" :"Experto",
+ "kExploreEnjoy" :"Por favor explore el menú y disfrute",
+ "kFallback" :"Configuración Alterna",
+ "kFallback2Configs" :"Tienes que tener al menos 2 configuraciones distintas creadas para usar configuración alterna.",
+ "kFastForward" :"avance rápido",
+ "kFastRewind" :"retroceso rápido",
+ "kFillScreen" :"pantalla completa",
+ "kFilterByDateTime" :"Filtrar por Hora/Fecha",
+ "kFilterEvents" :"Filtrar Eventos",
+ "kFilterEventsBetween1" :"Estás viendo los eventos entre",
+ "kFilterEventsBetween2" :"¿Quieres eliminar este filtro?",
+ "kFilterOn" :"Filtro Activado",
+ "kFilterSettings" :"Ajustes de Filtro",
+ "kFitScreen" :"ajustar pantalla",
+ "kFootage" :"Metraje",
+ "kForceAlarmConfirm" :"Estás seguro que quieres forzar una alarma para el monitor:",
+ "kForceImagePath" :"Forzar eventos para usar dirección de archivo",
+ "kForcingAlarm" :"forzando alarma",
+ "kFrame" :"cuadro",
+ "kFrameUpdate" :"Actualización de cuadros",
+ "kFrames" :"cuadros",
+ "kFrom" :"Desde",
+ "kFromDate" :"Desde la Fecha",
+ "kFromTime" :"Hasta la Hora",
+ "kGifNoCrosswalk" :"Lo sentimos, tienes que tener instalado Android versión 5.0 (Lollipop) o superior para poder utilizar esta función.",
+ "kGifWarning" :"La animación GIF será solamente de los cuadros donde se ha detectado la alarma y a 1 fps.",
+ "kGlobalConfiguration" :"Configuración Global",
+ "kGraphAlarmed" :"alertado",
+ "kGraphAll" :"todos",
+ "kGraphError" :"Hubo un error renderizando la gráfica. Por favor revise los registros.",
+ "kH264VideoSupport" :"Soporta Vídeo H264",
+ "kHelp" :"Ayuda",
+ "kHideMonsWithoutEvents" :"Ocultar monitores sin eventos.",
+ "kHideTip" :"ocultar sugerencia",
+ "kHighBWDisplay" :"alto ancho de banda",
+ "kId" :"Id",
+ "kImages" :"Imágenes",
+ "kImpMsg1" :"Mensaje Importante",
+ "kImpMsg2" :"Apreciaría si actualizaras ZoneMinder",
+ "kImpMsg3" :"Estás corriendo",
+ "kImpMsg4" :"tiene algunos arreglos importantes que pueden mejorar la API entre otras cosas. Esto es necesario para aprovechar la nueva API para las alarmas y otras características.",
+ "kImpMsg5" :"Versión Reportada",
+ "kImpMsg6" :"Versión Recomendada",
+ "kImpMsg7" :"Ok, lo tengo",
+ "kIncreaseSize" :"aumentar tamaño",
+ "kLanguage" :"Idioma",
+ "kLatestEvents" :"últimos eventos",
+ "kLiveView" :"Vista en Vivo",
+ "kLoad" :"carga",
+ "kLoading" :"cargando",
+ "kLoadingEvents" :"cargando eventos",
+ "kLoadingGraph" :"cargando gráfica",
+ "kLoadingMonitors" :"cargando monitores",
+ "kLocalTimeZone" :"Usar hora local",
+ "kLoginStatusNoCgi" :"Inicio de sesión validado, pero no se pudo validar cgi-path. Si los el stream de vídeo no funciona por favor revise la ruta a cgi-bin o trate de usar la característica de descubrimiento.",
+ "kLoginStatusNoCgiAlt" :"La ruta de cgi-bin que ingresaste puede estar mal. No estoy seguro, pero si no funcionan la vista en vivo, por favor revise la ruta de cgi o trate de usar la característica de descubrimiento.",
+ "kLoginValidAPIFailedTitle" :"Inicio de sesión validado pero falló la API",
+ "kLoginValidatedTitle" :"Inicio de sesión validado",
+ "kLogs" :"Registros",
+ "kLowBWDisplay" :"bajo ancho de banda",
+ "kLowBandwidth" :"modo bajo ancho de banda",
+ "kManageServerGroups" :"Administrar Grupos de Servidores",
+ "kMaxFPS" :"Máx. FPS",
+ "kMaxItemsForTimeline" :"Máx. número de ítems para la Cronología",
+ "kMaxMonitorsMontage" :"Máx. número de monitores en el montaje",
+ "kMenuDevSettings" :"Opciones de Desarrollador",
+ "kMenuEventMontage" :"Montaje de Eventos",
+ "kMenuEvents" :"Eventos",
+ "kMenuExit" :"Salir",
+ "kMenuHelp" :"Ayuda",
+ "kMenuLogs" :"Registros",
+ "kMenuMonitors" :"Monitores",
+ "kMenuMontage" :"Montaje",
+ "kMenuNews" :"Noticias",
+ "kMenuOptions" :"Menú",
+ "kMenuSystemStatus" :"Estado del Sistema",
+ "kMenuTimeline" :"Cronograma",
+ "kMenuTitle" :"Opciones",
+ "kMenuWizard" :"Asistente",
+ "kMenuZMSettings" :"Configuración",
+ "kMinAlarmCount" :"Mínima cuenta de alarma",
+ "kMinVersion" :"Versión Mínima Requerida",
+ "kMinimumIntervals" :"intervalo mínimo",
+ "kMode" :"Modo",
+ "kMonAlarmed" :"alertado",
+ "kMonAlert" :"alerta",
+ "kMonIdle" :"idle",
+ "kMonMocord" :"Mocord",
+ "kMonModect" :"Modect",
+ "kMonMonitor" :"Monitor",
+ "kMonNodect" :"Nodect",
+ "kMonNone" :"Ninguno",
+ "kMonPreAlarm" :"pre-alarm",
+ "kMonRecord" :"Grabación",
+ "kMonitorSingleImageScale" :"Escala de imagen simple en vivo",
+ "kMonitors" :"Monitores",
+ "kMontage" :"Montaje",
+ "kMontageImageScale" :"Escala de montaje de imagen",
+ "kMonth" :"Mes",
+ "kMore" :"más",
+ "kNeedToKnow" :"Necesito saber los detalles de tu usuario y dirección para comenzar",
+ "kNegotiatingStreamAuth" :"negociando el stream de autenticación",
+ "kNewPost" :"nuevo post",
+ "kNews" :"Noticias",
+ "kNext" :"Siguiente",
+ "kNextEvent" :"evento siguiente",
+ "kNextMonitor" :"monitor siguiente",
+ "kNoEvents" :"No hay eventos que mostrar",
+ "kNoMonitors" :"No hay monitores que mostrar",
+ "kNoMoreEvents" :"no hay más eventos",
+ "kNormalPlay" :"reproducción normal",
+ "kNote" :"Nota",
+ "kNow" :"ahora",
+ "kOff" :"apagar",
+ "kOn" :"prender",
+ "kOnTapNavigate" :"al tap, navegar a",
+ "kOneAuth" :"Tiene que habilitar al menos un mecanismo de autenticación.",
+ "kOnlyUseWebSocket" :"sólo usar websockets",
+ "kOperationInProgressBody" :"La operación anterior todavía está en progreso. Por favor espere.",
+ "kOperationInProgressTitle" :"Operación en Progreso",
+ "kPTZ" :"mover/inclinar/acercar",
+ "kPTZNotReady" :"No está listo para PTZ",
+ "kPTZnotConfigured" :"PTZ no está configurado para este monitor.",
+ "kPassword" :"contraseña",
+ "kPathToCgi" :"Ruta a cgi-bin",
+ "kPause" :"pausar",
+ "kPaused" :"pausado",
+ "kPersistHidden" :"Continuar con los monitores ocultos.",
+ "kPinProtect" :"Protegido por PIN",
+ "kPlaceHolderBasicAuthPass" :"contraseña de autenticación básica",
+ "kPlaceHolderBasicAuthUser" :"nombre de usuario de autenticación básica",
+ "kPlaceHolderZMAuthPass" :"contraseña de autenticación zm",
+ "kPlaceHolderZMAuthUser" :"nombre de usuario de autenticación zm",
+ "kPlaybackInterval" :"intervalo de reproducción",
+ "kPleaseCheckCredentials" :"Por favor compruebe sus credenciales",
+ "kPleaseConfirm" :"Por Favor Confirme",
+ "kPleaseSave" :"Por Favor Guarde",
+ "kPleaseTryAgain" :"por favor trate nuevamente",
+ "kPleaseWait" :"por favor espere",
+ "kPortal" :"Portal Web",
+ "kPortalAPIFailed" :"Falló la detección del API",
+ "kPortalCgiBinFailed" :"Falló la detección de cgi-bin",
+ "kPortalDetectionFailed" :"Portal: falló la detección",
+ "kPortalEmpty" :"La url del Portal Web no puede dejarse en blanco",
+ "kPortalInvalidUrl" :"La URL parece inválida (no se detectó un protocolo)",
+ "kPortalLoginUnsuccessful" :"No se pudo iniciar sesión. Por favor revisa tu configuración.",
+ "kPortalNoMonitorFound" :"No configured/enabled monitor found",
+ "kPortalNoProto" :"No protocol specified",
+ "kPortalNotice" :"Si esta pantalla no desaparece después de un rato, es posible que tus APIs no estén configuradas apropiadamente.",
+ "kPortalNoticeSub" :"(Deslize a la derecha para acceder al menú)",
+ "kPortalPleaseSelect" :"Por favor seleccione",
+ "kPortalUrl" :"url portal web ZM",
+ "kPresets" :"preajuste",
+ "kPrev" :"Atrás",
+ "kPrevEvent" :"evento anterior",
+ "kPrevMonitor" :"monitor anterior",
+ "kProfileChangeNotification" :"Has cambiado de {{oldName}} a {{newName}}. Por favor primero guarde este perfil.",
+ "kProtect" :"proteger",
+ "kPullToReload" :"arrastrar para recargar la información",
+ "kReAuthenticating" :"re-autenticando",
+ "kReachability" :"Habilitar Accesibilidad",
+ "kRecaptcha" :"Parece que has activado reCaptcha. Tiene que ser desactivada para que la aplicación funcione.",
+ "kReconfirmPin" :"Confirmar PIN",
+ "kRecordingProgress" :"grabación en progreso",
+ "kRefresh" :"Actualizar",
+ "kRefreshedView" :"actualizar vista",
+ "kReportEvents" :"reportar eventos",
+ "kReportedVersion" :"Reportar Versión",
+ "kReset" :"Resetear",
+ "kResolution" :"Resolución",
+ "kRestart" :"Reiniciar",
+ "kResumeDelay" :"retardo para reanudar",
+ "kSave" :"Guardar",
+ "kSavingSnapshot" :"grabando captura",
+ "kScore" :"puntuación",
+ "kScrub" :"Depurar",
+ "kSearch" :"buscar",
+ "kSearchCancelled" :"búsqueda cancelada",
+ "kSec" :"seg",
+ "kSelectFallback" :"Seleccionar configuración alterna",
+ "kSelectLanguage" :"Seleccionar Lenguaje",
+ "kSelectRunState" :"Seleccionar estado de funcionamiento",
+ "kSendingPTZ" :"Enviando PTZ",
+ "kSensitiveBody" :"Se modificarán los registros cuando se cree la salida final para remover información sensible como urls y contraseñas. Sin embargo eventualmente es su responsabilidad el asegurarse que no haya información sensible en los registros. Por favor asegúrese de haber revisado y editado los registros antes de enviarlos.",
+ "kSensitiveTitle" :"Información Sensible",
+ "kServerAdd" :"Añadir",
+ "kServerEmptyError" :"El servidor debe tener un nombre válido.",
+ "kServerName" :"Nombre del Servidor",
+ "kServerTimeZone" :"servidor TZ",
+ "kSettings" :"Ajustes",
+ "kSettingsSaved" :"Ajustes Guardados",
+ "kShowAlarmedEvents" :"Mostrar eventos de alarmas",
+ "kShowAllEvents" :"Mostrar todos los eventos",
+ "kShowAllFrames" :"todos",
+ "kShowTimeDiffFrames" :" momentos exacto",
+ "kShowTip" :"mostrar sugerencia",
+ "kShowing" :"Mostrando",
+ "kShowingEvent" :"mostrar evento",
+ "kSleep" :"Dormir",
+ "kSoundOnPush" :"Reproducir sonido en evento push",
+ "kSpeed" :"velocidad",
+ "kStart" :"Empezar",
+ "kStateAreYouSure" :"Está seguro que quiere ",
+ "kStateHideControls" :"Ocultar los controles de ZoneMinder",
+ "kStateShowControls" :"Mostrar los controles de ZoneMinder",
+ "kStatus" :"Estatus",
+ "kStop" :"Detener",
+ "kSuccess" :"Hecho",
+ "kSwipeToChangeMon" :"Deslizar para cambiar de monitor",
+ "kSwitchingEvents" :"cambiando eventos",
+ "kSystemStatus" :"Estado del Sistema",
+ "kTapEvents" :"Eventos",
+ "kTapLiveMonitor" :"Monitor en Vivo",
+ "kTapMontage" :"Montaje",
+ "kThanksForUsing" :"Gracias por usar",
+ "kTime" :"tiempo",
+ "kTimeZoneNotSupported" :"tu API no consta con esta función",
+ "kTimeline" :"Cronograma",
+ "kTimelineControlDisplay" :"tap:ver evento, doble-tap:ver gráfica",
+ "kTimelineMessage" :"Doble-tap salir. Tocar/pasar el ratón y arrastrar sobre los items para ver los cuadros.",
+ "kTimelineOnlyDisplaying1" :"mostrar únicamente los últimos {{maxItemsVal}} eventos",
+ "kTitleNotLoggedIn" :"No se ha iniciado sesión",
+ "kTitleNotLoggedInBody" :"No parece que hayas iniciado sesión. Para que funcione la detección, debe ingresar los detalles de inicio de sesión, URL del portal web y entonces Guardar. Después vuelva a esta opción.",
+ "kTitlePortalNotConfigured" :"Portal no configurado",
+ "kTo" :"hasta",
+ "kToDate" :"Hasta la Fecha",
+ "kToTime" :"Hasta el Tiempo",
+ "kToastSearchingPage" :"buscando página ",
+ "kTrying" :"intentando",
+ "kType" :"tipo",
+ "kUnknown" :"(desconocido)",
+ "kUpdateTimeline" :"actualizaciones dinámicas",
+ "kUseEventServer" :"Usar servidor de eventos",
+ "kUseSSL" :"Usar SSL",
+ "kUseVideoControls" :"Por favor use los controles del reproductor de vídeo para los eventos H264. ZoneMinder todavía no soporta los controles zms.",
+ "kUseZmAuth" :"usar autenticación de ZM",
+ "kUserName" :"nombre de usuario",
+ "kValidNameBasicAuth" :"Por favor ingrese un nombre de usuario válido y una contraseña para su autenticación básica",
+ "kValidNameZMAuth" :"Por favor ingrese un nombre de usuario válido y contraseña para su autenticación de ZM",
+ "kVersion" :"Versión",
+ "kVersionIncompatible" :"Soy incompatible con tu versión de ZoneMinder",
+ "kVibrateOnPush" :"Vibrar en notificaciones push",
+ "kVideo" :"Vídeo",
+ "kVideoError" :"El vídeo no se puede reproducir.",
+ "kVideoErrorMobile" :"El vídeo no se puede reproducir. Trata de habilitar 'forzar el directorio para los eventos' en Opc de Desarrollador. El formato también podría ser incompatible con la vista en sistemas móviles",
+ "kVideoLoading" :"Cargando Vídeo",
+ "kVideoMp4Warning" :"Actualmente no es posible saber cuando el vídeo se ha descargado completamente. Por favor fíjese en el tamaño del archivo para tener un estimado.",
+ "kWake" :"Despertar",
+ "kWarningLargeTimeline" :"Un valor alto puede afectar el rendimiento del cronograma. Si el rendimiento del cronograma te parece lento, intenta reducir el valor a 200 y síguelo subiendo a partir de ese punto.",
+ "kWeek" :"Semana",
+ "kWelcomeWizard" :"Bienvenido al asistente de zm",
+ "kWizAuthText1" :"¿Confundido? ZM Auth es el tipo de autenticación utilizado cuando habilitas la opción OPT_USE_AUTH en la consola de ZM dentro de la pantalla de opciones.",
+ "kWizAuthText2" :"Autenticación Básica es cuando configura un nombre de usuario y una contraseña en su servidor web. Si está usando la autenticación LDAP, también consta como autenticación básica.",
+ "kWizBasicAuth" :"Uso autenticación básica",
+ "kWizConfigPain" :"Configurar ZoneMinder puede ser difícil. Veamos si el asistente puede ayudar.",
+ "kWizGotoLogin" :"Ir a la Pantalla de Inicio de Sesión",
+ "kWizNextStep1" :"Una vez se termine la autodetección, podrás",
+ "kWizNextStep2" :"Volver a los pasos anteriores y volver a configurar",
+ "kWizNextStep3" :"Ir a la pantalla de inicio de sesión con las opciones detectadas",
+ "kWizPasswdNote" :"nota: si su contraseña consta de caracteres especiales como #?@ o amperstand, el ayudante podría fallar al detectar el cgi. Es mejor si cambia la contraseña temporalmente por una más simple, corre el ayudante y después lo cambia nuevamente",
+ "kWizPortalAuth" :"Autenticación Portal Web",
+ "kWizPortalText" :"Hablemos sobre como has configurado la autenticación. Si la tienes habilitada, activa la opción.",
+ "kWizPortalTip" :"¿Confundido? Puedes encontrar fácilmente tu dirección URL revisando la dirección que aparece en tu navegador cuando accedes a la Consola ZoneMinder.",
+ "kWizPortalUrl" :"¿Cuál es la dirección url de Zoneminder?",
+ "kWizResults" :"Resultados de la autodetección",
+ "kWizTip" :"Sugerencia",
+ "kWizUseAuth" :"Uso autenticación",
+ "kWizZMAuth" :"Uso autenticación provista por ZM",
+ "kWizard" :"Ayudante",
+ "kWorkingOnGraph" :"trabajar con los datos de la gráfica",
+ "kZMRunning" :"corriendo",
+ "kZMSettingsFor" :"Ajustes de ZoneMinder para",
+ "kZMStopped" :"parado",
+ "kZMUndetermined" :"indeterminado",
+ "kZMUpgradeNeeded" :"Se necesita actualizar ZoneMinder"
+}
diff --git a/www/lang/locale-pl.json b/www/lang/locale-pl.json
new file mode 100644
index 00000000..d913c050
--- /dev/null
+++ b/www/lang/locale-pl.json
@@ -0,0 +1,389 @@
+{
+ "k1DaySummary" :"1 dzień zbiorczo",
+ "k1HourSummary" :"1 godzina zbiorczo",
+ "k1MonthSummary" :"1 miesiąc zbiorczo",
+ "k1WeekSummary" :"1 tydzień zbiorczo",
+ "kAlarmAPIError" :"błąd - upewnij się, że Twoje API obsługuje tą funkcję",
+ "kAlarmFrameCount" :"Ilość Klatek Alarmu",
+ "kAlarmMaxFPS" :"Max FPS Alarmu",
+ "kAlarms" :"Alarmy",
+ "kAll" :"Wszystko",
+ "kAnalyze" :"Analizuj",
+ "kApiUrl" :"Adres url API ZM",
+ "kApplyingChanges" :"Stosuję zmiany. Proszę czekać",
+ "kArrangingImages" :"układam obrazki",
+ "kAt" :"przy",
+ "kAuthSuccess" :"uwierzytelniono",
+ "kAuthenticating" :"uwierzytelniam",
+ "kAutoSwitchBW" :"automatyczna zmiana prędkości",
+ "kAwake1" :"Utrzymuj włączony ekran",
+ "kAwake2" :"(w trakcie oglądania materiału)",
+ "kBannerAPICheckFailed" :"błąd sprawdzania API",
+ "kBannerCannotDeleteNeedOne" :"Nie mogę usunąć, potrzebuję przynajmniej jednego",
+ "kBannerPinMismatch" :"błąd w kodzie PIN",
+ "kBannerPleaseCheck" :"Proszę sprawdzić ustawienia",
+ "kBodyPortalNotConfigured" :"Przed próbą detekcji ścieżki CGI proszę wprowadzić dane logowania oraz adres URL strony, a następnie wcisnąć ZAPISZ.",
+ "kButtonCancel" :"Anuluj",
+ "kButtonClear" :"Wyczyść",
+ "kButtonNo" :"Nie",
+ "kButtonOk" :"OK",
+ "kButtonSave" :"ZAPISZ",
+ "kButtonYes" :"Tak",
+ "kCalcEventSize" :"kalkuluję rozmiar Zdarzenia",
+ "kCancellingAlarm" :"usuwam Alarm",
+ "kChangeSettingsFor" :"Zmień ustawienia dla",
+ "kChangeState" :"Zmień Stan",
+ "kCheckCredentials" :"Proszę sprawdzić dane logowania",
+ "kChromeMax" :"maksimum 5 monitorów - limit chrome",
+ "kCleaningUp" :"czyszczenie",
+ "kClear" :"Wyczyść",
+ "kCollapse" :"zwiń",
+ "kConfiguration" :"Konfiguracja",
+ "kControl" :"kontrola",
+ "kCredentialsBody" :"Wprowadź swoje dane logowania ZoneMinder",
+ "kCredentialsTitle" :"Wymagane dane logowania",
+ "kCurrentState" :"obecny stan",
+ "kCustomRange" :"Zakres niestandardowy",
+ "kCycleMonitors" :"cykl monitorów",
+ "kCycleMonitorsInterval" :"Interwał cyklu monitora",
+ "kDay" :"Dzień",
+ "kDecreaseSize" :"zmniejsz rozmiar",
+ "kDelete" :"Usuń",
+ "kDeleteEventError1" :"Nie można usunąc Zdarzenia",
+ "kDeleteEventError2" :"proszę sprawdzić logi",
+ "kDeleteEventSuccess" :"usunięte Zdarzenia",
+ "kDeleteLogsConfirm" :"Jesteś pewien, że chcesz usunąć logi?",
+ "kDeletingEvent" :"usuwam Zdarzenie",
+ "kDevOptions" :"Ustawienia zaawansowane",
+ "kDeveloperOptionsFor" :"Opcje zaawansowane dla",
+ "kDisableAlarmMontage" :"Wyłącz alarm API w Podglądzie",
+ "kDisableAlarmMontageSub" :"może pomóc jeśli serwer się przeciąży",
+ "kDisableNative" :"Wyłącz natywne przejścia",
+ "kDisableNativeSub" :"Włącz jeśli manu się blokuje",
+ "kDisablePush" :"wyłącz APNS/GCM",
+ "kDisableSamsung" :"Jeśli jesteś na urządzeniu Samsung i masz problem z wypełnianiem pól, czasowo wyłącz autokorektę",
+ "kDiscovering" :"wykrywam",
+ "kDiscoveringAPI" :"wykrywam API",
+ "kDiscoveringCGI" :"wykrywam CGI",
+ "kDiscoveringPortal" :"wykrywam portal",
+ "kDone" :"gotowe",
+ "kDownload" :"pobierz",
+ "kEnable24hr" :"Włącz format czasu 24 godz.",
+ "kEnableDebug" :"Włącz dzienniki debugowania",
+ "kEnableLogs" :"Włącz dziennik",
+ "kEnableNewsUpdates" :"Włącz informację o aktualizacji",
+ "kEnterPin" :"Wprowadź PIN",
+ "kError" :"Błąd",
+ "kErrorChangingMonitors" :"Błąd zmiany monitorów. Proszę sprawdzić logi",
+ "kErrorFrameBanner" :"nie mogłem odczytać właściwości klatki",
+ "kErrorPleaseTryAgain" :"spróbuj jeszcze raz",
+ "kErrorRetrievingFrames" :"błąd odczytu klatki",
+ "kErrorRetrievingState" :"błąd odczytu stanu",
+ "kErrorSave" :"Błąd - nie mogłem zapisać",
+ "kEvent" :"zdarzenie",
+ "kEventHistFaster" :"szybciej",
+ "kEventHistHrs" :"godzin temu",
+ "kEventHistPause" :"pauza",
+ "kEventHistPlay" :"play",
+ "kEventHistShowFrom" :"Pokaż od",
+ "kEventHistSlower" :"wolniej",
+ "kEventMontage" :"Podgląd Zdarzeń",
+ "kEventMontageImageScale" :"Skalowanie obrazu Podglądu Zdarzeń",
+ "kEventNavVidFeeds" :"Nawigacja Zdarzeń nie jest możliwa z materiałem wideo. ZoneMinder jeszcze tego nie wspiera",
+ "kEventRecording" :"Nagrywanie Zdarzenia",
+ "kEventServer" :"Serwer Zdarzeń",
+ "kEventServerConfig1" :"Upewnij się, że ustawienia ZM są skonfigurowane i zapisane zanim zaczniesz konfigurować Serwer Zdarzeń",
+ "kEventServerVersionBody1" :"Używasz wersji",
+ "kEventServerVersionBody2" :"Aktualizuj do",
+ "kEventServerVersionTitle" :"Nie wspierana wersja Serwera Zdarzeń",
+ "kEventSingleImageScale" :"Skalowanie pojedynczego ekranu Zdarzenia",
+ "kEventView" :"Podgląd Zdarzenia",
+ "kEvents" :"zdarzenia",
+ "kEventsCap" :"Zdarzenia",
+ "kExampleServer" :"np. Mój Dom",
+ "kExitAppBackground" :"wyjdź z aplikacji w tle",
+ "kExitEventView" :"opuść podgląd zdarzenia",
+ "kExitFullScreen" :"wyłącz pełny ekran",
+ "kExitLiveView" :"opuść podgląd na żywo",
+ "kExpert" :"Ekspert",
+ "kExploreEnjoy" :"Przeglądaj menu i ciesz się życiem",
+ "kFallback" :"Konfiguracja awaryjna",
+ "kFallback2Configs" :"Potrzebujesz przynajmniej 2 osobnych konfiguracji dla konfiguracji awaryjnej",
+ "kFastForward" :"szybko w przód",
+ "kFastRewind" :"szybko w tył",
+ "kFillScreen" :"wypełnij ekran",
+ "kFilterByDateTime" :"Filtruj po Dacie/Godzinie",
+ "kFilterEvents" :"Filtruj Zdarzenia",
+ "kFilterEventsBetween1" :"Przeglądasz Zdarzenia między",
+ "kFilterEventsBetween2" :"Czy chcesz usunąć ten filtr?",
+ "kFilterOn" :"Filtr włączony",
+ "kFilterSettings" :"Ustawienia filtra",
+ "kFitScreen" :"dopasuj do ekranu",
+ "kFootage" :"Materiał",
+ "kForceAlarmConfirm" :"Czy na pewno chcesz wymusić alarm dla Monitora:",
+ "kForceImagePath" :"Zmuś Zdarzenia do użycia ścieżki pliku",
+ "kForcingAlarm" :"wymuszanie alarmu",
+ "kFrame" :"klatka",
+ "kFrameUpdate" :"Aktualizacja klatki",
+ "kFrames" :"klatki",
+ "kFrom" :"Od",
+ "kFromDate" :"Od Daty",
+ "kFromTime" :"Od Godziny",
+ "kGifWarning" :"Animacja GIF będzie zawierała tylko zaalarmowane klatki i 1fps.",
+ "kGifNoCrosswalk" : "Przepraszam, żeby ta funkcja mogła działać, musisz posiadać co najmniej Android 5, lub wyższy.",
+ "kGlobalConfiguration" :"Konfiguracja Główna",
+ "kGraphAlarmed" :"zaalarmowane",
+ "kGraphAll" :"wszystko",
+ "kGraphError" :"wystąpił błąd w trakcie reenderowania grafiki. Proszę sprawdzić logi",
+ "kH264VideoSupport" :"Wsparcie dla Video H264",
+ "kHelp" : "Pomoc",
+ "kHideMonsWithoutEvents" :"Ukryj Monitory bez Zdarzeń",
+ "kHideTip" :"ukryj podpowiedź",
+ "kHighBWDisplay" :"wysoka przepustowość",
+ "kId" :"Id",
+ "kImages" :"Obrazy",
+ "kImpMsg1" :"Ważna Wiadomość",
+ "kImpMsg2" :"Będę wdzięczny jeśli aktualizujesz ZoneMinder",
+ "kImpMsg3" :"Działasz na",
+ "kImpMsg4" :"posiada ważne poprawki poprawiające pracę API z innymi funkcjami. Jest to niezbędne do działania nowych alarmów API i innych funkcji",
+ "kImpMsg5" :"Zgłoszona Wersja",
+ "kImpMsg6" :"Rekomendowana Wersja",
+ "kImpMsg7" :"Ok, załapałem",
+ "kIncreaseSize" :"zwiększ rozmiar",
+ "kLanguage" :"Język",
+ "kLatestEvents" :"ostatnie zdarzenia",
+ "kLiveView" :"Podgląd na żywo",
+ "kLoad" :"załaduj",
+ "kLoading" :"ładuję",
+ "kLoadingEvents" : "ładuję zdarzenia",
+ "kLoadingGraph" :"ładuję grafikę",
+ "kLoadingMonitors" :"ładuję monitory",
+ "kLocalTimeZone" :"Użyj lokalnej strefy czasowej",
+ "kLoginStatusNoCgi" :"Login potwierdzony, ale nie mogłem potwierdzić ścieżki cgi. Jeśli podgląd na żywo nie działa, sprawdź ścieżkę cgi-bin lub użyj funkcji wykrywania.",
+ "kLoginStatusNoCgiAlt" :"Ścieżka cgi-bin którą wprowadziłeś może być niewłaściwa. Nie jestem pewien, ale jeśli Twój podgląd na żywo nie działa, sprawdź ścieżkę cgi lub spróbuj użyć funkcji wykrywania.",
+ "kLoginValidAPIFailedTitle" :"Login potwierdzony ale API zawiodło",
+ "kLoginValidatedTitle" :"Login Potwierdzony",
+ "kLogs" :"Logi",
+ "kLowBWDisplay" :"niska przepustowość",
+ "kLowBandwidth" :"Tryb niskiej przepustowości",
+ "kManageServerGroups" :"Zarządzaj Serwerem Grup",
+ "kMaxFPS" :"Maksimum FPS",
+ "kMaxItemsForTimeline" :"Maks.szt. na Osi Czasu",
+ "kMaxMonitorsMontage" :"Maksimum monitorów w Podglądzie",
+ "kMenuDevSettings" :"Ustawienia Zaawansowane",
+ "kMenuEventMontage" :"Podgląd Zdarzeń",
+ "kMenuEvents" :"Zdarzenia",
+ "kMenuExit" :"Wyjdź",
+ "kMenuHelp" :"Pomoc",
+ "kMenuLogs" :"Logi",
+ "kMenuMonitors" :"Monitory",
+ "kMenuMontage" :"Podgląd",
+ "kMenuNews" :"Wiadomości",
+ "kMenuOptions" :"Menu",
+ "kMenuSystemStatus" :"Stan Systemu",
+ "kMenuTimeline" :"Oś czasu",
+ "kMenuTitle" :"Opcje",
+ "kMenuWizard" :"Kreator",
+ "kMenuZMSettings" :"Ustawienia",
+ "kMinAlarmCount" :"Minimalna ilość alarmów",
+ "kMinVersion" :"Minimalna Wymagana Wersja",
+ "kMinimumIntervals" :"minimalna przerwa",
+ "kMode" :"Tryb",
+ "kMonAlarmed" :"zaalarmowany",
+ "kMonAlert" :"alert",
+ "kMonIdle" :"bezczynny",
+ "kMonMocord" :"Mocord",
+ "kMonModect" :"Modect",
+ "kMonMonitor" :"Monitor",
+ "kMonNodect" :"Nodect",
+ "kMonNone" :"Żaden",
+ "kMonPreAlarm" :"pre-alarm",
+ "kMonRecord" :"Record",
+ "kMonitorSingleImageScale" :"Skalowanie pojedynczego obrazy Podglądu na żywo",
+ "kMonitors" :"Monitory",
+ "kMontage" :"Podgląd",
+ "kMontageImageScale" :"Skalowanie obrazu Podglądu",
+ "kMontageNoSavedProfiles" :"Nie zapisany profil Montażu",
+ "kMontageSave" :"Zapisz profil Montażu",
+ "kMontageSaveSubtitle" :"aby zapisać obecne ustawienia, proszę wprowadzić nazwę profilu",
+ "kMonth" :"Miesiąc",
+ "kMore" :"więcej",
+ "kNeedToKnow" :"Żeby rozpocząć muszę znać dane ścieżki i login Twojego ZoneMinder'a",
+ "kNegotiatingStreamAuth" :"negocjowanie uwierzytelnienia źródła",
+ "kNews" :"Wiadomości (10 najnowszych)",
+ "kNewPost" : "nowy post",
+ "kNext" :"Następny",
+ "kNextEvent" :"następne zdarzenie",
+ "kNextMonitor" :"następny monitor",
+ "kNoEvents" :"Brak Zdarzeń do wyświetlenia",
+ "kNoMonitors" :"Brak Monitorów do wyświetlenia",
+ "kNoMoreEvents" :"koniec Zdarzeń",
+ "kNormalPlay" :"normalne odtwarzanie",
+ "kNote" : "Notatka",
+ "kNow" :"teraz",
+ "kOff" :"wyłącz",
+ "kOn" :"włącz",
+ "kOnTapNavigate" :"przyciskając przejdź do",
+ "kOneAuth" :"Musisz włączyć co najmniej jeden mechanizm uwierzytelniający",
+ "kOnlyUseWebSocket" :"użyj tylko websockets",
+ "kOperationInProgressBody" :"Poprzednia operacja nadal trwa. Czekaj",
+ "kOperationInProgressTitle" :"Operacja trwa",
+ "kPTZ" :"pan/tilt/zoom",
+ "kPTZNotReady" :"Nie gotowy na PTZ",
+ "kPTZnotConfigured" :"PTZ nie skonfigurowany dla tego Monitora",
+ "kPassword" :"hasło",
+ "kPathToCgi" :"Ścieżka do cgi-bin",
+ "kPause" :"pauza",
+ "kPaused" :"wstrzymane",
+ "kPersistHidden" :"Utrzymuj ukryte Monitory",
+ "kPinProtect" :"Ochrona PIN",
+ "kPlaceHolderBasicAuthPass" :"hasło prostego uwierzytelniania",
+ "kPlaceHolderBasicAuthUser" :"użytkownik zwykłego uwierzytelniania",
+ "kPlaceHolderZMAuthPass" :"hasło uwierzytelniania ZM",
+ "kPlaceHolderZMAuthUser" :"użytkownik uwierzytelniania ZM",
+ "kPlaybackInterval" :"Interwał Odtwarzania",
+ "kPleaseCheckCredentials" :"Sprawdź dane logowania",
+ "kPleaseConfirm" :"Proszę Potwierdzić",
+ "kPleaseSave" :"Proszę Zapisać",
+ "kPleaseTryAgain" :"spróbuj jeszcze raz",
+ "kPleaseWait" :"proszę czekać",
+ "kPortal" :"Portal",
+ "kPortalAPIFailed" :"Błąd wykrywania API",
+ "kPortalCgiBinFailed" :"Błąd wykrywania ścieżki cgi-bin",
+ "kPortalDetectionFailed" :"Portal: błąd wykrywania",
+ "kPortalEmpty" :"Ścieżka Portalu url nie może być pusta",
+ "kPortalInvalidUrl" :"URL wygląda niewłaściwie (nie wykryto protokołu)",
+ "kPortalLoginUnsuccessful" :"Logowanie do Portalu nieudane. Wróć i sprawdź swoje ustawienia",
+ "kPortalNoMonitorFound" :"Nie znaleziono skonfigurowanego/włączonego Monitora",
+ "kPortalNoProto" :"Nie ustalono protokołu",
+ "kPortalNotice" :"Jeśli ten ekran po chwili nie zniknie, to możliwe, że Twoje APIs nie jest skonfigurowane prawidłowo",
+ "kPortalNoticeSub" :"(Przesuń w prawo by wejść do menu)",
+ "kPortalPleaseSelect" :"Proszę wybrać",
+ "kPortalUrl" :"ZM portal url",
+ "kPresets" :"ustawienia",
+ "kPrev" :"Poprz.",
+ "kPrevEvent" :"poprzednie zdarzenie",
+ "kPrevMonitor" :"poprzedni monitor",
+ "kProfileChangeNotification" :"Zmieniłeś z {{oldName}} na {{newName}}. Zapisz wcześniej ten profil.",
+ "kProtect" :"zabezpiecz",
+ "kPullToReload" :"przeciągnij by przeładować dane",
+ "kReAuthenticating" :"ponowne uwierzytelnianie",
+ "kReachability" :"Włącz Osiągalność",
+ "kRecaptcha" :"Wygląda na to, że właczyłeś reCaptcha. Powinna być wyłączona, żeby aplikacja działała.",
+ "kReconfirmPin" :"Potwierdź PIN",
+ "kRecordingProgress" :"trwa nagrywanie",
+ "kRefresh" :"odśwież",
+ "kRefreshedView" :"odśwież podgląd",
+ "kReportEvents" :"raportuj zdarzenie",
+ "kReportedVersion" :"Zgłoszona Wersja",
+ "kReset" :"Reset",
+ "kResolution" :"Rozdzielczość",
+ "kRestart" :"Restart",
+ "kResumeDelay" :"opóźnienie wznowienia",
+ "kSave" :"Zapisz",
+ "kSavingSnapshot" :"zapisuję obraz",
+ "kScore" :"punkty",
+ "kScrub" :"Podgląd",
+ "kSearch" :"szukaj",
+ "kSearchCancelled" :"szukaj anulowane",
+ "kSec" :"sek",
+ "kSelect" :"Proszę wybrać",
+ "kSelectDelete" :"Wybrany profil zostanie usunięty",
+ "kSelectSwitch" :"Wybrany profil zostanie załadowany",
+ "kSelectFallback" :"Wybierz alternatywę",
+ "kSelectLanguage" :"Wybierz język",
+ "kSelectRunState" :"Wybierz działający stan",
+ "kSendingPTZ" :"Wysyła PTZ",
+ "kSensitiveBody" :"tworząc finalny log zmodyfikuje jego zawartość, aby usunąć wrażliwe dane takie jak linki czy hasła. Jednak na Tobie spoczywa odpowiedzialność upewnienia się, że wszystkie dane wrażliwe zostały usunięte. Upewnij się, że przeglądnąłeś i edytowałeś log przed jego wysłaniem",
+ "kSensitiveTitle" :"Dane Wrażliwe",
+ "kServerAdd" :"Dodaj",
+ "kServerEmptyError" :"Nazwa serwera nie może być pusta",
+ "kServerName" :"Nazwa serwera",
+ "kServerTimeZone" :"serwer TZ",
+ "kSettings" :"Ustawienia",
+ "kSettingsSaved" :"Ustawienia Zapisane",
+ "kShowAlarmedEvents" :"Pokaż zaalarmowane Zdarzenia",
+ "kShowAllEvents" :"Pokaż wszystkie Zdarzenia",
+ "kShowAllFrames" :"wszystkie",
+ "kShowTimeDiffFrames" :"unikatowe czasy",
+ "kShowTip" :"pokaż podpowiedź",
+ "kShowing" :"Pokazywanie",
+ "kShowingEvent" :"pokazywanie zdarzenia",
+ "kSleep" :"Uśpij",
+ "kSoundOnPush" :"Dźwięk przycisku",
+ "kSpeed" :"prędkość",
+ "kStart" :"Start",
+ "kStateAreYouSure" :"Jesteś pewien, że chcesz ",
+ "kStateHideControls" :"Ukryj Przyciski ZoneMinder",
+ "kStateShowControls" :"Pokaż Przyciski ZoneMinder",
+ "kStatus" :"Stan",
+ "kStop" :"Stop",
+ "kSuccess" :"Sukces",
+ "kSwipeToChangeMon" :"Przesuń, by zmienić Monitory",
+ "kSwitchingEvents" :"przełączam zdarzenia",
+ "kSystemStatus" :"Stan Systemu",
+ "kTapEvents" :"Zdarzenia",
+ "kTapLiveMonitor" :"Podgląd na żywo",
+ "kTapMontage" :"Podgląd",
+ "kThanksForUsing" :"Dziękuję za używanie",
+ "kTime" :"czas",
+ "kTimeZoneNotSupported" :"twoje API tego nie obsługuje",
+ "kTimeline" :"Oś czasu",
+ "kTimelineControlDisplay" :"stuknięcie:podgl.zdarzenia, podw.stuknięcie:podgl.wykresu klatek",
+ "kTimelineMessage" :"Podwójne stuknięcie - wyjście. Dotknięcie i przesuwanie elementów pozwala przeglądać klatki",
+ "kTimelineOnlyDisplaying1" :"wyświetla tylko {{maxItemsVal}} ostatnich zdarzeń",
+ "kTitleNotLoggedIn" :"Nie zalogowany",
+ "kTitleNotLoggedInBody" :"Nie wygląda jakbyś był zalogowany. Żeby wykrywanie działało, musisz wypełnić dane logowania, portal URL i wcisnąć ZAPISZ. Wtedy możesz tu wrócić.",
+ "kTitlePortalNotConfigured" :"Portal nie skonfigurowany",
+ "kTo" :"do",
+ "kToDate" :"Do Daty",
+ "kToTime" :"Do Czasu",
+ "kToastSearchingPage" :"szuka strony ",
+ "kTrying" :"próbuję",
+ "kType" :"typ",
+ "kUnknown" :"(nieznany)",
+ "kUpdateTimeline" :"aktualizacja dynamiczna",
+ "kUseEventServer" :"Użyj serwera Zdarzeń",
+ "kUseSSL" :"Użyj SSL",
+ "kUseVideoControls" :"Użyj przełączników wideo odtwarzacza dla Zdarzeń H264. ZoneMinder jeszcze nie wspiera przełączników zms",
+ "kUseZmAuth" :"Użyj uwierzytelniania ZM",
+ "kUserName" :"nazwa użytkownika",
+ "kValidNameBasicAuth" :"Wprowadź włąściwego użytkownika i hasło dla prostego uwierzytelniania",
+ "kValidNameZMAuth" :"Wprowadź włąściwego użytkownika i hasło dla uwierzytelniania ZM",
+ "kVersion" :"Wersja",
+ "kVersionIncompatible" :"Jestem niekompatybilny z wersją Twojego ZoneMinder'a",
+ "kVibrateOnPush" :"Wibruj przy dotknięciu",
+ "kVideo" :"Wideo",
+ "kVideoError" : "Wideo nie odtwarzalne.",
+ "kVideoErrorMobile" : "Wideo nie odtwarzalne. Spróbuj włączyć 'wymuś ścieżkę obrazu dla zdarzenia' w Ustawieniach Zaawansowanych. Format może być też niekompatybilny z przeglądarką systemu mobilnego",
+ "kVideoLoading" : "Ładuję Wideo",
+ "kVideoMp4Warning" : "Obecnie nie ma możliwości stwierdzenia, czy wideo zostało w pełni ściągnięte. Sprawdź wielkość pliku do ściągnięcia.",
+ "kWake" :"Wybudź",
+ "kWarningLargeTimeline" :"Wysoka wartość może wpłynąć na prędkość osi czasu. Jeśli oś czasu będzie zbyt wolna, spróbuj zredukować wartość do 200 i powoli zwiększaj do pożądanego efektu.",
+ "kWeek" :"Tydzień",
+ "kWelcomeWizard" :"Witamy w Kreatorze",
+ "kWizAuthText1" :"Skołowany? Uwierzytelnianie ZM, to uwierzytelnianie używane kiedy włączysz OPT_USE_AUTH w oknie opcji konsoli ZM.",
+ "kWizAuthText2" :"Uwierzytelnianie prostego używamy wtedy, kiedy skonfigurujesz użytkownika i hasło w przeglądarce internetowej. Jeśli używasz uwierzytelniania LDAP, wtedy raczej jest to uwierzytelnianie proste.",
+ "kWizBasicAuth" :"Używam uwierzytelniania prostego",
+ "kWizConfigPain" :"Konfiguracja ZoneMinder'a może być bolesnym przeżyciem. Sprawdźmy czy zmWizard pomoże.",
+ "kWizGotoLogin" :"Idź do ekranu logowania",
+ "kWizNextStep1" :"Gdy auto-detekcja zostanie zakończona, można albo",
+ "kWizNextStep2" :"Wróć do poprzednich kroków i popraw",
+ "kWizNextStep3" :"Idź do ekranu logowania z tymi automatycznie wprowadzonymi wartościami",
+ "kWizPasswdNote" :"uwaga: jeśli Twoje hasło ma znaki specjalne, takie jak #?@ czy ampersand, kreator może nie wykryć cgi. Najlepiej tymczasowo zmień hasło na jakieś proste, przejdź kreatora, a następnie przywróć swoje poprzednie hasło",
+ "kWizPortalAuth" :"Uwierzytelnianie portalu",
+ "kWizPortalText" :"Pomówmy o tym jak skonfigurowałeś uwierzytelnianie. Jeśli włączyłeś uwierzytelnianie, przełącz ten przycisk",
+ "kWizPortalTip" :"Skołowany? Możesz łatwo znaleźć adres URL portalu notując adres, który widzisz w swojej przeglądarce gdy używasz Konsoli ZoneMinder.",
+ "kWizPortalUrl" :"Jaki jest adres portalu Zoneminder?",
+ "kWizResults" :"Rezultat autodetekcji",
+ "kWizTip" :"Podpowiedź",
+ "kWizUseAuth" :"Używam uwierzytelniania",
+ "kWizZMAuth" :"Używam uwierzytelniania ZM",
+ "kWizard" :"Kreator",
+ "kWorkingOnGraph" :"Pracuję na danych graficznych",
+ "kZMRunning" :"pracuję",
+ "kZMSettingsFor" :"Ustawienia ZoneMinder dla",
+ "kZMStopped" :"zatrzymane",
+ "kZMUndetermined" :"nieokreślone",
+ "kZMUpgradeNeeded" :"Potrzebna aktualizacja ZoneMinder'a"
+}
diff --git a/www/lang/locale-pt.json b/www/lang/locale-pt.json
index 8dd66c47..89dc853d 100644
--- a/www/lang/locale-pt.json
+++ b/www/lang/locale-pt.json
@@ -15,6 +15,7 @@
"kAt" :"em",
"kAuthSuccess" :"autenticação bem sucedida",
"kAuthenticating" :"autenticando",
+ "kAutoSwitchBW" :"comutação automática da largura de banda",
"kAwake1" :"Manter ecrã ligado",
"kAwake2" :"(quando visualizar imagens)",
"kBannerAPICheckFailed" :"verificação da API falhou",
@@ -55,14 +56,18 @@
"kDeletingEvent" :"eliminado evento",
"kDevOptions" :"Configurações de Desenvolvimento",
"kDeveloperOptionsFor" :"Configurações de desenvolvimento para o",
+ "kDisableAlarmMontage" :"Desativar API de alarme na montagem",
+ "kDisableAlarmMontageSub" :"pode ajudar se o servidor ficar em sobrecarga",
"kDisableNative" :"Desativar transições nativas",
"kDisableNativeSub" :"Ativar se o menu estiver a congelar",
"kDisablePush" :"desativar APNS/GCM",
+ "kDisableSamsung" :"Se você estiver a usar um dispositivo Samsung e estiver enfrentando problemas de entrada, desative temporariamente a correção automática",
"kDiscovering" :"descobrindo",
"kDiscoveringAPI" :"descobrindo api",
"kDiscoveringCGI" :"descobrindo cgi",
"kDiscoveringPortal" :"descobrindo portal",
"kDone" :"feito",
+ "kDownload" :"descarregar",
"kEnable24hr" :"Ativar formato 24hr",
"kEnableDebug" :"Ativar logs de depuração",
"kEnableLogs" :"Ativar logs",
@@ -76,6 +81,12 @@
"kErrorRetrievingState" :"erro a obter estado",
"kErrorSave" :"Erro - não pode gravar",
"kEvent" :"evento",
+ "kEventHistFaster" :"mais rápido",
+ "kEventHistHrs" :"horas atrás",
+ "kEventHistPause" :"pausa",
+ "kEventHistPlay" :"tocar",
+ "kEventHistShowFrom" :"Mostrar de",
+ "kEventHistSlower" :"mais lento",
"kEventMontage" :"Montagem de Eventos",
"kEventMontageImageScale" :"Montagem de Eventos com escalamento de imagem",
"kEventNavVidFeeds" :"Navegação de Eventos não está disponivel com feeds de video. O ZoneMinder ainda não os suporta",
@@ -91,8 +102,9 @@
"kEventsCap" :"Eventos",
"kExampleServer" :"ex. Minha Casa",
"kExitAppBackground" :"terminar app em background",
- "kExitFullScreen" :"sair de tela inteira",
- "kExitLiveView" :"sair de visualização ao vivo",
+ "kExitEventView" :"sair da visualização do evento",
+ "kExitFullScreen" :"sair do ecrã completo",
+ "kExitLiveView" :"sair da visualização ao vivo",
"kExpert" :"Especialista",
"kExploreEnjoy" :"Por favor explore o menu e divirta-se",
"kFallback" :"Recuperar configuração anterior",
@@ -117,13 +129,17 @@
"kFrom" :"De",
"kFromDate" :"Da Data",
"kFromTime" :"Da Hora",
+ "kGifWarning" :"A animação GIF será apenas de quadros de alarme e a 1fps",
+ "kGifNoCrosswalk" :"Desculpe, você precisa estar no Android 5.0 (Lollipop) ou superior para que esse recurso funcione.",
"kGlobalConfiguration" :"Configuração Global",
"kGraphAlarmed" :"alarme",
"kGraphAll" :"todos",
"kGraphError" :"houve um erro de renderização de gráficos. Por favor, consulte os logs",
"kH264VideoSupport" :"Suporte de Video H264",
+ "kHelp" :"Ajuda",
"kHideMonsWithoutEvents" :"Esconder monitores sem eventos",
"kHideTip" :"Esconder dica",
+ "kHighBWDisplay" :"largura de banda alta",
"kId" :"Id",
"kImages" :"Imagens",
"kImpMsg1" :"Mensagem Importante",
@@ -139,14 +155,17 @@
"kLiveView" :"Visualização ao Vivo",
"kLoad" :"carga",
"kLoading" :"carregando",
+ "kLoadingEvents" :"Carregando eventos",
"kLoadingGraph" :"carregando gráfico",
"kLoadingMonitors" :"carregando monitores",
+ "kLocalTimeZone" :"usar timezone local",
"kLoginStatusNoCgi" :"Login validado, mas não pôde validar cgi-path. Se as transmissões ao vivo não funcionarem, por favor verifique o seu caminho cgi-bin ou tente usar o recurso descobrir",
"kLoginStatusNoCgiAlt" :"O caminho cgi-bin que introduziu pode estar errado. Eu não posso ter certeza, mas se as suas transmissões ao vivo não funcionam, por favor reveja o seu caminho cgi ou tente usar o recurso descobrir.",
"kLoginValidAPIFailedTitle" :"Login Validado mas API falhou",
"kLoginValidatedTitle" :"Login Validado",
"kLogs" :"Logs",
- "kLowBandwidth" :"modo baixa largura de banda",
+ "kLowBWDisplay" :"largura de banda baixa",
+ "kLowBandwidth" :"modo largura de banda baixa",
"kManageServerGroups" :"Gerenciar Grupos de Servidores",
"kMaxFPS" :"FPS Max",
"kMaxItemsForTimeline" :"Max items na Timeline",
@@ -183,12 +202,13 @@
"kMonitorSingleImageScale" :"Visualição direta de imagem única à escala",
"kMonitors" :"Monitores",
"kMontage" :"Montagem",
- "kMontageImageScale" :"Montagem com escalamento de imagem",
+ "kMontageImageScale" :"Montagem com escalamento da imagem",
"kMonth" :"Mẽs",
"kMore" :"mais",
"kNeedToKnow" :"Eu preciso saber o seu login no ZoneMinder e os detalhes do caminho para começar",
"kNegotiatingStreamAuth" :"negociar autenticação de stream",
"kNews" :"Novidades",
+ "kNewPost" :"nova postagem",
"kNext" :"Seguinte",
"kNextEvent" :"evento seguinte",
"kNextMonitor" :"monitor seguinte",
@@ -196,6 +216,8 @@
"kNoMonitors" :"Não há monitores para mostrar",
"kNoMoreEvents" :"não há mais eventos",
"kNormalPlay" :"reprodução normal",
+ "kNote" :"Nota",
+ "kNow" :"agora",
"kOff" :"desligado",
"kOn" :"ligado",
"kOnTapNavigate" :"ao tocar, navegue até",
@@ -231,6 +253,8 @@
"kPortalLoginUnsuccessful" :"Login no Portal não foi bem sucedido. Por favor reveja as suas configurações",
"kPortalNoMonitorFound" :"Não foi encontrado nenhum monitor configurado/ativado",
"kPortalNoProto" :"Nenhum protocolo especificado",
+ "kPortalNotice" :"Se este ecrã não desaparecer após algum tempo, é possível que as suas APIs não estejam corretamente configuradas",
+ "kPortalNoticeSub" :"(Deslize para a direita para aceder ao menu)",
"kPortalPleaseSelect" :"Por favor selecione",
"kPortalUrl" :"url do portal ZM",
"kPresets" :"predefinições",
@@ -269,6 +293,7 @@
"kServerAdd" :"Adicionar",
"kServerEmptyError" :"O Nome do Servidor tem de estar preenchido",
"kServerName" :"Nome do Servidor",
+ "kServerTimeZone" :"TZ do servidor",
"kSettings" :"Configurações",
"kSettingsSaved" :"Configurações Guardadas",
"kShowAlarmedEvents" :"Mostrar eventos com alarme",
@@ -296,6 +321,7 @@
"kTapMontage" :"Montagem",
"kThanksForUsing" :"Obrigado por usar o",
"kTime" :"tempo",
+ "kTimeZoneNotSupported" :"A sua API não suporta isto",
"kTimeline" :"Timeline",
"kTimelineControlDisplay" :"clique:ver evento, clique-duplo:ver gráfico de frames",
"kTimelineMessage" :"clique-duplo sai. clique/rato-por-cima e arraste por cima dos items para ver frames",
@@ -310,6 +336,7 @@
"kTrying" :"tentando",
"kType" :"digite",
"kUnknown" :"(desconhecido)",
+ "kUpdateTimeline" :"atualizações dinâmicas",
"kUseEventServer" :"Usar servidor de eventos",
"kUseSSL" :"Usar SSL",
"kUseVideoControls" :"Por favor, use os controles do player de vídeo para eventos H264. ZoneMinder ainda não suporta controles ZMS",
@@ -321,6 +348,10 @@
"kVersionIncompatible" :"Sou incompativel com a sua versão do ZoneMinder",
"kVibrateOnPush" :"Vibrar ao premir",
"kVideo" :"Vídeo",
+ "kVideoError" : "Vídeo não reproduzível.",
+ "kVideoErrorMobile" : "Vídeo não reproduzível. Experimente ativar 'forçar o caminho da imagem para eventos' em Configurações Dev. O formato também pode ser incompatível com o sistema de visualização do equipamento móvel",
+ "kVideoLoading" : "Carregando Vídeo",
+ "kVideoMp4Warning" : "Não é possível de momento saber quando o vídeo está totalmente carregado. Por favor, verifique o tamnho do arquivo de download.",
"kWake" :"Acordar",
"kWarningLargeTimeline" :"Um valor elevado pode afetar o desempenho da timeline. Se você achar o desempenho da timeline lento, tente reduzir o valor para 200 e trabalhe a partir daí.",
"kWeek" :"Semana",
diff --git a/www/lib/videogular-cuepoints/.bower.json b/www/lib/videogular-cuepoints/.bower.json
new file mode 100644
index 00000000..42babc5d
--- /dev/null
+++ b/www/lib/videogular-cuepoints/.bower.json
@@ -0,0 +1,33 @@
+{
+ "name": "videogular-cuepoints",
+ "homepage": "https://github.com/HarryCutts/videogular-cuepoints",
+ "description": "Videogular plugin for displaying marks on a video's scrub bar",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Harry Cutts"
+ }
+ ],
+ "main": [
+ "cuepoints.js",
+ "cuepoints.css"
+ ],
+ "dependencies": {
+ "videogular-controls": "1.x"
+ },
+ "ignore": [
+ "**/.*",
+ "**/*.md"
+ ],
+ "version": "2.1.0",
+ "_release": "2.1.0",
+ "_resolution": {
+ "type": "version",
+ "tag": "v2.1.0",
+ "commit": "79d95d226f06eae08c102f50ff623466e89fa30d"
+ },
+ "_source": "https://github.com/HarryCutts/videogular-cuepoints.git",
+ "_target": "~2.1.0",
+ "_originalSource": "videogular-cuepoints",
+ "_direct": true
+} \ No newline at end of file
diff --git a/www/lib/videogular-cuepoints/LICENSE b/www/lib/videogular-cuepoints/LICENSE
new file mode 100644
index 00000000..98c1d3d2
--- /dev/null
+++ b/www/lib/videogular-cuepoints/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Harry Cutts
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/www/lib/videogular-cuepoints/bower.json b/www/lib/videogular-cuepoints/bower.json
new file mode 100644
index 00000000..b4262999
--- /dev/null
+++ b/www/lib/videogular-cuepoints/bower.json
@@ -0,0 +1,21 @@
+{
+ "name": "videogular-cuepoints",
+ "homepage": "https://github.com/HarryCutts/videogular-cuepoints",
+ "description": "Videogular plugin for displaying marks on a video's scrub bar",
+ "license": "MIT",
+ "authors": [
+ { "name": "Harry Cutts" }
+ ],
+
+ "main": [
+ "cuepoints.js",
+ "cuepoints.css"
+ ],
+ "dependencies": {
+ "videogular-controls": "1.x"
+ },
+ "ignore": [
+ "**/.*",
+ "**/*.md"
+ ]
+}
diff --git a/www/lib/videogular-cuepoints/cuepoints.css b/www/lib/videogular-cuepoints/cuepoints.css
new file mode 100644
index 00000000..24f1cfc7
--- /dev/null
+++ b/www/lib/videogular-cuepoints/cuepoints.css
@@ -0,0 +1,18 @@
+videogular vg-cuepoints {
+ position: relative;
+
+ display: block;
+ width: 100%;
+ height: 0px;
+}
+
+videogular vg-cuepoints vg-cuepoint {
+ position: absolute;
+ top: -16px;
+
+ display: block;
+ width: 2px;
+ height: 30px;
+
+ background-color: red;
+}
diff --git a/www/lib/videogular-cuepoints/cuepoints.js b/www/lib/videogular-cuepoints/cuepoints.js
new file mode 100644
index 00000000..6bfc3c6f
--- /dev/null
+++ b/www/lib/videogular-cuepoints/cuepoints.js
@@ -0,0 +1,52 @@
+(function(){
+'use strict';
+angular.module('uk.ac.soton.ecs.videogular.plugins.cuepoints', [])
+ .directive(
+ 'vgCuepoints',
+ [function() {
+ return {
+ restrict: 'E',
+ require: '^videogular',
+ templateUrl: function(element, attrs) {
+ return attrs.templateUrl || 'videogular-cuepoints/cuepoints.html';
+ },
+ scope: {
+ cuepoints: '=vgCuepointsConfig',
+ theme: '=vgCuepointsTheme',
+ },
+ link: function($scope, elem, attr, API) {
+ // shamelessly stolen from part of videogular's updateTheme function
+ function updateTheme(value) {
+ if (value) {
+ var headElem = angular.element(document).find("head");
+ headElem.append("<link rel='stylesheet' href='" + value + "'>");
+ }
+ }
+
+ var calcLeft = function(cuepoint) {
+ if (API.totalTime === 0) return '-1000';
+
+ var videoLength = API.totalTime / 1000;
+ return (cuepoint.time * 100 / videoLength).toString();
+ };
+
+ $scope.onCuepointClick = function(cuepoint){
+ API.seekTime(cuepoint.time);
+ };
+
+ $scope.cuepointStyle = function(cuepoint) {
+ return {
+ left: calcLeft(cuepoint) + '%'
+ };
+ }
+
+ updateTheme($scope.theme);
+ },
+ };
+ }])
+ .run(['$templateCache', function($templateCache) {
+ $templateCache.put('videogular-cuepoints/cuepoints.html',
+ '<vg-cuepoint ng-repeat="cuepoint in cuepoints.points" ng-click="onCuepointClick(cuepoint)" ng-style="cuepointStyle(cuepoint)"></vg-cuepoint>'
+ );
+ }]);
+})();
diff --git a/www/lib/videogular/videogular.js b/www/lib/videogular/videogular.js
index 1a29b398..105d0463 100644
--- a/www/lib/videogular/videogular.js
+++ b/www/lib/videogular/videogular.js
@@ -960,8 +960,13 @@ angular.module("com.2fdevs.videogular")
return API.playsInline;
},
function (newValue, oldValue) {
- if (newValue) API.mediaElement.attr("webkit-playsinline", "");
- else API.mediaElement.removeAttr("webkit-playsinline");
+ if (newValue) {
+ API.mediaElement.attr("webkit-playsinline", "");
+ API.mediaElement.attr("playsinline", "");
+ } else {
+ API.mediaElement.removeAttr("webkit-playsinline");
+ API.mediaElement.removeAttr("playsinline");
+ }
}
);
diff --git a/www/templates/devoptions.html b/www/templates/devoptions.html
index cf207327..2c373418 100644
--- a/www/templates/devoptions.html
+++ b/www/templates/devoptions.html
@@ -4,7 +4,7 @@
<button data-badge="{{$root.alarmCount}}" class="animated infinite tada button button-icon button-clear ion-ios-bell notification-badge" ng-click="handleAlarms();" ng-if="$root.isAlarm"></button>
</ion-nav-buttons>
<ion-nav-buttons side="right">
- <button class="button button-clear" ng-click="saveDevOptions()">Save</button>
+ <button class="button button-clear" ng-click="saveDevOptions()">{{'kSave' | translate}}</button>
</ion-nav-buttons>
<ion-content scroll-sista delegate-handle="none" overflow-scroll="false" mouse-wheel-scroll>
<div class="list list-inset">
@@ -18,14 +18,15 @@
</label>
</div>
<label>
- <ion-toggle ng-model="loginData.use24hr" ng-checked="{{loginData.use24hr}}" toggle-class="toggle-calm">{{'kEnable24hr' | translate}}</ion-toggle>
+ <ion-toggle ng-model="loginData.use24hr" ng-checked="{{loginData.use24hr}}" toggle-class="toggle-calm"><span class="item-text-wrap">{{'kEnable24hr' | translate}}</span></ion-toggle>
</label>
<label>
- <ion-toggle ng-model="loginData.useLocalTimeZone" ng-checked="{{loginData.useLocalTimeZone}}" ng-disabled="!isTzSupported()" toggle-class="toggle-calm">{{'kLocalTimeZone' | translate}}<span ng-if="!isTzSupported()"><p>{{'kTimeZoneNotSupported' | translate}}</p></span><span ng-if="isTzSupported()"><p>{{'kServerTimeZone' | translate}}:{{getTimeZoneNow()}}</p></span></ion-toggle>
+ <ion-toggle ng-model="loginData.useLocalTimeZone" ng-checked="{{loginData.useLocalTimeZone}}" ng-disabled="!isTzSupported()" toggle-class="toggle-calm"><span class="item-text-wrap">{{'kLocalTimeZone' | translate}}<span ng-if="!isTzSupported()"><p>{{'kTimeZoneNotSupported' | translate}}</p></span><span ng-if="isTzSupported()"><p>{{'kServerTimeZone' | translate}}:{{getTimeZoneNow()}}</p></span></span>
+ </ion-toggle>
</label>
<div ng-if="$root.platformOS=='android'">
<label>
- <ion-toggle ng-model="loginData.exitOnSleep" ng-checked="{{loginData.exitOnSleep}}" toggle-class="toggle-calm">{{'kExitAppBackground' | translate}}</ion-toggle>
+ <ion-toggle ng-model="loginData.exitOnSleep" ng-checked="{{loginData.exitOnSleep}}" toggle-class="toggle-calm"><span class="item-text-wrap">{{'kExitAppBackground' | translate}}</span></ion-toggle>
</label>
</div>
<!--
@@ -41,7 +42,7 @@
toggle-class="toggle-calm">use ZMS for events footage</ion-toggle>
</label>
-->
- <div class="item item-input-inset">
+ <div class="item item-text-wrap item-input-inset">
{{'kMaxMonitorsMontage' | translate }}&nbsp;
<label class="item-input-wrapper">
<input type="tel" placeholder="be careful" ng-model="loginData.maxMontage">
@@ -55,19 +56,19 @@
</label>
</div>
-->
- <div class="item item-input-inset">
+ <div class="item item-text-wrap item-input-inset">
{{'kMontageImageScale' | translate}}(%)&nbsp;
<label class="item-input-wrapper">
<input type="tel" placeholder="max is 70" ng-model="loginData.montageQuality">
</label>
</div>
- <div class="item item-input-inset">
+ <div class="item item-text-wrap item-input-inset">
{{'kEventSingleImageScale' | translate}}(%)&nbsp;
<label class="item-input-wrapper">
<input type="tel" placeholder="" ng-model="loginData.singleImageQuality">
</label>
</div>
- <div class="item item-input-inset">
+ <div class="item item-text-wrap item-input-inset">
{{'kMonitorSingleImageScale' | translate}}(%)&nbsp;
<label class="item-input-wrapper">
<input type="tel" placeholder="" ng-model="loginData.monSingleImageQuality">
@@ -81,13 +82,13 @@
</label>
</div>-->
- <div class="item item-input-inset">
+ <div class="item item-text-wrap item-input-inset">
{{'kEventMontageImageScale'|translate}}(%)&nbsp;
<label class="item-input-wrapper">
<input type="tel" placeholder="" ng-model="loginData.montageHistoryQuality">
</label>
</div>
- <div class="item item-input-inset">
+ <div class="item item-text-wrap item-input-inset">
{{'kMaxItemsForTimeline' | translate}}&nbsp;
<label class="item-input-wrapper">
<input type="tel" placeholder="" ng-model="loginData.graphSize">
@@ -95,7 +96,7 @@
</label>
<br/>
</div>
- <div class="item item-input-inset">
+ <div class="item item-text-wrap item-input-inset">
{{'kMinAlarmCount' | translate}}&nbsp;
<label class="item-input-wrapper">
<input type="tel" placeholder="" ng-model="loginData.minAlarmCount">
@@ -109,27 +110,27 @@
</label>
</div>
<label>
- <ion-toggle ng-model="loginData.disableAlarmCheckMontage" ng-checked="loginData.disableAlarmCheckMontage" toggle-class="toggle-calm">{{'kDisableAlarmMontage' | translate}}
- <p>{{'kDisableAlarmMontageSub' | translate}}</p>
+ <ion-toggle ng-model="loginData.disableAlarmCheckMontage" ng-checked="loginData.disableAlarmCheckMontage" toggle-class="toggle-calm"><span class="item-text-wrap">{{'kDisableAlarmMontage' | translate}}
+ <p>{{'kDisableAlarmMontageSub' | translate}}</p></span>
</ion-toggle>
</label>
<label>
- <ion-toggle ng-model="loginData.enableLogs" ng-checked="{{loginData.enableLogs}}" toggle-class="toggle-calm">{{'kEnableLogs' | translate}}</ion-toggle>
+ <ion-toggle ng-model="loginData.enableLogs" ng-checked="{{loginData.enableLogs}}" toggle-class="toggle-calm"><span class="item-text-wrap">{{'kEnableLogs' | translate}}</span></ion-toggle>
</label>
- <div class="item item-input-inset">
+ <div class="item item-text-wrap item-input-inset">
{{'kCycleMonitorsInterval'|translate}}({{'kSec'|translate}}.)&nbsp;
<label class="item-input-wrapper">
<input type="tel" placeholder="" ng-model="loginData.cycleMonitorsInterval">
</label>
</div>
<label>
- <ion-toggle ng-if="loginData.enableLogs" ng-model="loginData.enableDebug" ng-checked="{{loginData.enableDebug}}" toggle-class="toggle-calm">{{'kEnableDebug' | translate}}</ion-toggle>
+ <ion-toggle ng-if="loginData.enableLogs" ng-model="loginData.enableDebug" ng-checked="{{loginData.enableDebug}}" toggle-class="toggle-calm"><span class="item-text-wrap">{{'kEnableDebug' | translate}}</span></ion-toggle>
</label>
<label>
- <ion-toggle ng-model="loginData.canSwipeMonitors" ng-checked="{{loginData.canSwipeMonitors}}" toggle-class="toggle-calm">{{'kSwipeToChangeMon' | translate}}</ion-toggle>
+ <ion-toggle ng-model="loginData.canSwipeMonitors" ng-checked="{{loginData.canSwipeMonitors}}" toggle-class="toggle-calm"><span class="item-text-wrap">{{'kSwipeToChangeMon' | translate}}</span></ion-toggle>
</label>
<label>
- <ion-toggle ng-model="loginData.forceImageModePath" ng-checked="{{loginData.forceImageModePath}}" toggle-class="toggle-calm"> {{'kForceImagePath' | translate}}</ion-toggle>
+ <ion-toggle ng-model="loginData.forceImageModePath" ng-checked="{{loginData.forceImageModePath}}" toggle-class="toggle-calm"> <span class="item-text-wrap">{{'kForceImagePath' | translate}}</span></ion-toggle>
</label>
<!--
<label>
@@ -137,11 +138,11 @@
</label>
-->
<label>
- <ion-toggle ng-model="loginData.enableBlog" ng-checked="{{loginData.enableBlog}}" toggle-class="toggle-calm"> {{'kEnableNewsUpdates' | translate}}</ion-toggle>
+ <ion-toggle ng-model="loginData.enableBlog" ng-checked="{{loginData.enableBlog}}" toggle-class="toggle-calm"> <span class="item-text-wrap">{{'kEnableNewsUpdates' | translate}}</span></ion-toggle>
</label>
<label ng-if="$root.platformOS != 'desktop'">
- <ion-toggle ng-model="loginData.disableNative" ng-checked="{{loginData.disableNative}}" toggle-class="toggle-calm"> {{'kDisableNative' | translate}}
- <p>{{'kDisableNativeSub' | translate}}</p>
+ <ion-toggle ng-model="loginData.disableNative" ng-checked="{{loginData.disableNative}}" toggle-class="toggle-calm"> <span class="item-text-wrap">{{'kDisableNative' | translate}}
+ <p>{{'kDisableNativeSub' | translate}}</p></span>
</ion-toggle>
</label>
</div>
diff --git a/www/templates/events-graphs.html b/www/templates/events-graphs.html
index 2f7b965d..0e14016f 100644
--- a/www/templates/events-graphs.html
+++ b/www/templates/events-graphs.html
@@ -1,88 +1,60 @@
-
<!----- NOT USED -------->
-
<ion-view view-title="Monitor Event Summary" cache-view="false">
-
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" ng-click="openMenu()"></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>
-
+ <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>
-
<!-- I'm calling the same controller function but with different parameters when
you switch between tabs -->
-
<!-- Oddly, unless I add a "div ng-controller" before each canvas
onClick handler does not work
-->
-
<!-- All Events view -->
- <div id="visualization"></div>
+ <div id="visualization"></div>
<ion-tabs class="tabs-icon-top tabs-stable">
<ion-tab title="All" icon="ion-stats-bars" on-select="generateTCChart(0,'All Events',0)">
<ion-nav-view>
<ion-content class="has-header">
-
<div ng-controller="zmApp.EventsGraphsCtrl">
-
-
-
- <canvas tc-chartjs-bar chart-data="chart.data"
- chart-options="chart.options"
- ng-click="handleChartClick($event)" chart="chartwithbars">
- </canvas>
+ <canvas tc-chartjs-bar chart-data="chart.data" chart-options="chart.options" ng-click="handleChartClick($event)" chart="chartwithbars">
+ </canvas>
</div>
</ion-content>
</ion-nav-view>
</ion-tab>
-
<!-- All Events in the last hour -->
- <ion-tab title="Last Hour" icon="ion-stats-bars"
- on-select="generateTCChart(1,'Events in the last hour',1)">
+ <ion-tab title="Last Hour" icon="ion-stats-bars" on-select="generateTCChart(1,'Events in the last hour',1)">
<ion-nav-view>
<ion-content>
<div ng-controller="zmApp.EventsGraphsCtrl">
- <div style="overflow:scroll;">
- <canvas tc-chartjs-bar chart-data="chart.data"
- chart-options="chart.options"
- ng-click="handleChartClick($event)"
- chart="chartwithbars">
- </canvas>
+ <div style="overflow:scroll;">
+ <canvas tc-chartjs-bar chart-data="chart.data" chart-options="chart.options" ng-click="handleChartClick($event)" chart="chartwithbars">
+ </canvas>
</div>
</div>
</ion-content>
</ion-nav-view>
</ion-tab>
-
<!-- All Events in last 24 hrs-->
- <ion-tab title="Day" icon="ion-stats-bars"
- on-select="generateTCChart(2,'Events in the last day',24)">
+ <ion-tab title="Day" icon="ion-stats-bars" on-select="generateTCChart(2,'Events in the last day',24)">
<ion-nav-view>
<ion-content>
<div ng-controller="zmApp.EventsGraphsCtrl">
- <div style="overflow:scroll;">
- <canvas tc-chartjs-bar chart-data="chart.data"
- chart-options="chart.options" chart="chartwithbars"
- ng-click="handleChartClick($event)">
- </canvas>
- </div>
+ <div style="overflow:scroll;">
+ <canvas tc-chartjs-bar chart-data="chart.data" chart-options="chart.options" chart="chartwithbars" ng-click="handleChartClick($event)">
+ </canvas>
+ </div>
</div>
</ion-content>
</ion-nav-view>
</ion-tab>
-
<!-- All Events in last week -->
- <ion-tab title="Week" icon="ion-stats-bars"
- on-select="generateTCChart(3,'Events in the last week',168)">
+ <ion-tab title="Week" icon="ion-stats-bars" on-select="generateTCChart(3,'Events in the last week',168)">
<ion-nav-view>
<ion-content>
<div ng-controller="zmApp.EventsGraphsCtrl">
<div style="overflow:scroll;">
- <canvas tc-chartjs-bar chart-data="chart.data"
- chart-options="chart.options"
- chart="chartwithbars"
- ng-click="handleChartClick($event)">
+ <canvas tc-chartjs-bar chart-data="chart.data" chart-options="chart.options" chart="chartwithbars" ng-click="handleChartClick($event)">
</canvas>
</div>
</div>
diff --git a/www/templates/events-modal.html b/www/templates/events-modal.html
index 58573026..06ff464e 100644
--- a/www/templates/events-modal.html
+++ b/www/templates/events-modal.html
@@ -2,8 +2,9 @@
<!-- style="width: 90%; height: 90%; top: 5%; left: 5%; right: 5%; bottom: 5%;"-->
<ion-modal-view cache-view="false">
<ion-content style="background-color:#444444" ng-cloak>
- <ion-scroll has-bouncing=false min-zoom=1 zooming="true" direction="xy" style="width: 100%; " delegate-handle="imgscroll" on-swipe-left="onSwipeEvent(nextId,1)" on-swipe-right="onSwipeEvent(prevId,-1)" overflow-scroll="false">
+ <ion-scroll has-bouncing=false min-zoom=1 zooming="true" direction="xy" delegate-handle="imgscroll" on-swipe-left="onSwipeEvent(nextId,1)" on-swipe-right="onSwipeEvent(prevId,-1)" overflow-scroll="false">
<div id="full-screen-event" style="height: 100vh;">
+ <!--<div>-->
<!-- route via ZMS -->
<div ng-if="( (defaultVideo=='') || (loginData.enableh264==false)) && (loginData.useNphZmsForEvents==true)">
<!--<div style="color:white">connkey:{{connKey}}</div>-->
@@ -16,33 +17,34 @@
</div>
<!-- no default video -->
<div ng-if="defaultVideo!==undefined && defaultVideo!='' && loginData.enableh264 == true">
- <!--<div ng-if="videoIsReady" class="videogular-full-container">-->
- <div ng-if="videoIsReady" ng-class="{'object-fit_cover':imageFit==false, 'object-fit_contain':imageFit==true}" >
- <!--<video width="320" height="240" controls>
- <source src="" dynamic-url dynamic-url-src="{{video_url}}">
-
- </video>-->
- <videogular vg-error="onVideoError($event)" vg-can-play = "onCanPlay()" vg-player-ready="onPlayerReady($API)" vg-plays-inline="videoObject.config.playsInline" vg-theme="videoObject.config.theme" vg-complete="playbackFinished()" on-double-tap="closeModal();" vg-autoplay="videoObject.config.autoPlay" vg-responsive="true" >
- <<vg-media vg-src="videoObject.config.sources" vg-native-controls="false">
- </vg-media>-
-
- <vg-controls>
- <vg-play-pause-button></vg-play-pause-button>
- <vg-scrub-bar>
- <vg-scrub-bar-current-time></vg-scrub-bar-current-time>
- </vg-scrub-bar>
- <vg-time-display>{{ timeLeft | date:'mm:ss':'+0000' }}</vg-time-display>
- <vg-fullscreen-button></vg-fullscreen-button>
- <vg-volume>
- <vg-mute-button></vg-mute-button>
- </vg-volume>
- </vg-controls>
- <!--<vg-buffering></vg-buffering>-->
- </videogular>
+ <div ng-if="videoIsReady">
+ <div style="height:{{$root.devHeight}}px; width:{{$root.devWidth}}px;">
+ <videogular vg-error="onVideoError($event)" vg-can-play="onCanPlay()" vg-native-fullscreen="videoObject.config.nativeFullScreen" vg-player-ready="onPlayerReady($API)" vg-plays-inline="videoObject.config.playsInline" vg-theme="videoObject.config.theme" vg-complete="playbackFinished()" on-double-tap="closeModal();" vg-autoplay="videoObject.config.autoPlay" vg-responsive="videoObject.config.responsive" vg-update-time="videoTime(event.Event.StartTime,$currentTime)">
+ <vg-media vg-src="videoObject.config.sources" vg-native-controls="videoObject.config.nativeControls">
+ </vg-media>
+ <vg-controls>
+ <vg-playback-button></vg-playback-button>
+ <vg-play-pause-button></vg-play-pause-button>
+ <vg-time-display>{{ videoTime(event.Event.StartTime, currentTime ); }}</vg-time-display>
+ <vg-scrub-bar>
+ <vg-scrub-bar-current-time></vg-scrub-bar-current-time>
+ <vg-cuepoints vg-cuepoints-config="videoObject.config.cuepoints"
+ vg-cuepoints-theme="videoObject.config.cuepoints.theme.url">
+ </vg-cuepoints>
+ </vg-scrub-bar>
+ <vg-time-display>{{ timeLeft | date:'mm:ss':'+0000' }}</vg-time-display>
+ <vg-fullscreen-button></vg-fullscreen-button>
+ <vg-volume>
+ <vg-mute-button></vg-mute-button>
+ </vg-volume>
+ </vg-controls>
+
+ </videogular>
+ </div>
</div>
- <div id="event_canvas_video">
+ <!--<div id="event_canvas_video">
<canvas id="eventchart" width="100%" height="20px"></canvas>
- </div>
+ </div>-->
</div>
</div>
<!-- 100vh -->
@@ -61,21 +63,22 @@
@{{currentRate}}x {{'kAt' | translate}}:{{currentProgress.progress}}s
</div>
</div>
+
</ion-content>
</ion-modal-view>
<nav mfb-menu position="tr" effect="zoomin" label="{{'kCollapse'|translate}}" active-icon="ion-chevron-up" resting-icon="ion-chevron-down" toggling-method="click">
<a mfb-button icon="ion-arrow-right-c" label="{{'kNextEvent'|translate}}" ng-click="jumpToEvent(nextId,1);"></a>
<a mfb-button icon="ion-arrow-left-c" label="{{'kPrevEvent'|translate}}" ng-click="jumpToEvent(prevId,-1);"></a>
- <a mfb-button icon="ion-arrow-resize" label="{{imageFit?'fill screen':'fit screen'}}" ng-click="scaleImage();"></a>
+ <a mfb-button icon="ion-arrow-resize" label="{{imageFit? ('kFillScreen' | translate):('kFitScreen' | translate)}}" ng-click="scaleImage();"></a>
<a mfb-button icon="ion-close" label="{{'kExitEventView' | translate}}" ng-click="closeModal()"> </a>
</nav>
- <nav ng-if="loginData.useNphZmsForEvents" mfb-menu position="br" effect="zoomin" label="collapse" active-icon="ion-chevron-down" resting-icon="ion-chevron-up" toggling-method="click">
+ <nav ng-if="loginData.useNphZmsForEvents && defaultVideo==''" mfb-menu position="br" effect="zoomin" label="{{'kCollapse' | translate}}" active-icon="ion-chevron-down" resting-icon="ion-chevron-up" toggling-method="click">
<a mfb-button icon="ion-skip-backward" label="{{'kFastRewind'|translate}}" ng-click="adjustSpeed('fr');"></a>
<a mfb-button icon="ion-skip-forward" label="{{'kFastForward'|translate}}" ng-click="adjustSpeed('ff');"></a>
<a mfb-button icon="ion-play" label="{{'kNormalPlay'|translate}}" ng-click="adjustSpeed('np');"></a>
<a mfb-button icon="ion-pause" label="{{'kPause'|translate}}" ng-click="adjustSpeed('p');"> </a>
</nav>
- <div class="events-range-modal-text">{{mName}}&nbsp;<i class="ion-arrow-right-b"></i>&nbsp;{{humanizeTime}} ({{d_eventId}}) </div>
+ <div class="events-range-modal-text">{{mName}}&nbsp;<i class="ion-arrow-right-b"></i>&nbsp;{{videoDynamicTime}} ({{humanizeTime}}) [{{d_eventId}}] </div>
<div id="flyoutmenu" style="position:absolute;bottom:100px;left:10px">
<ul>
<li ng-if="defaultVideo==''">
diff --git a/www/templates/events-modalgraph.html b/www/templates/events-modalgraph.html
index 8b718a5e..25b38543 100644
--- a/www/templates/events-modalgraph.html
+++ b/www/templates/events-modalgraph.html
@@ -1,10 +1,9 @@
<div ng-controller="EventsModalGraphCtrl">
-
-<ion-modal-view cache-view="false" style="width:80%;height:80%; top: 10%; left: 10%; right: 10%; bottom: 10%;" >
+ <ion-modal-view cache-view="false" style="width:80%;height:80%; top: 10%; left: 10%; right: 10%; bottom: 10%;">
<ion-content ng-cloak on-double-tap="closeModal()" delegate-handle="eventgraph-modal-delegate">
- <div data-tap-disabled="true">
- <canvas id="eventchart" width="auto" height="70%"></canvas>
- </div>
+ <div data-tap-disabled="true">
+ <canvas id="eventchart" width="auto" height="70%"></canvas>
+ </div>
</ion-content>
</ion-modal-view>
</div>
diff --git a/www/templates/events-popover.html b/www/templates/events-popover.html
index e48b290e..38dd1d2e 100644
--- a/www/templates/events-popover.html
+++ b/www/templates/events-popover.html
@@ -1,18 +1,15 @@
<ion-popover-view class="fit">
- <ion-content>
- <div class="list" ng-click="popover.hide()">
+ <ion-content>
+ <div class="list" ng-click="popover.hide()">
<a class="item" ng-href="" ng-click="popover.hide();$state.go('events-date-time-filter');">{{'kFilterByDateTime' | translate}}</a>
-
<!-- <a class="item" ng-href="" ng-click=" popover.hide();$state.go('events-graphs');" >
Event Graphs
</a>-->
<a class="item" ng-href="" ng-click="popover.hide();doRefresh();">
{{'kRefresh' | translate}}
</a>
-
<a class="item" ng-href="" ng-click="popover.hide();toggleMinAlarmFrameCount();" ng-if="loginData.enableAlarmCount">{{'kShowAllEvents' | translate}}</a>
- <a class="item" ng-href="" ng-click="popover.hide();toggleMinAlarmFrameCount();" ng-if="!loginData.enableAlarmCount"> {{'kShowAlarmedEvents' | translate}}</a>
- </div>
- </ion-content>
- </ion-popover-view>
-
+ <a class="item" ng-href="" ng-click="popover.hide();toggleMinAlarmFrameCount();" ng-if="!loginData.enableAlarmCount"> {{'kShowAlarmedEvents' | translate}}</a>
+ </div>
+ </ion-content>
+</ion-popover-view>
diff --git a/www/templates/events.html b/www/templates/events.html
index af2a7f89..5d60a46b 100644
--- a/www/templates/events.html
+++ b/www/templates/events.html
@@ -1,82 +1,49 @@
-
<ion-view cache-view="false">
-
<ion-nav-title>{{scrollPosition();}}</ion-nav-title>
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" ng-click="openMenu()">
</button>
-
<button class="button button-icon icon ion-ios-minus-outline" ng-click="eventList.showDelete = !eventList.showDelete;"></button>
-
<button data-badge="{{$root.alarmCount}}" class="animated infinite tada button button-icon button-clear ion-ios-bell notification-badge" ng-click="handleAlarms();" ng-if="$root.isAlarm"></button>
-
</ion-nav-buttons>
-
<ion-nav-buttons side="right">
<a style="" class="button button-icon icon ion-android-more-vertical" ng-click="popover.show($event)" ;>&nbsp;&nbsp;&nbsp;</a>
<a style="" class="button button-icon icon ion-search" ng-href="" ng-click="searchClicked();"> </a>
</ion-nav-buttons>
-
<div ng-if="showSearch">
<ion-header-bar class="bar bar-subheader item-input-inset">
-
<label class="item-input-wrapper">
<i class="icon ion-ios7-search placeholder-icon"></i>
<input type="search" placeholder="{{'kSearch'|translate}}" ng-model="search.text" autocorrect="off" autocomplete="off">
</label>
</ion-header-bar>
</div>
-
-
-
<!-- collection repeat forces js scrolling, thing to remember -->
-
- <ion-content scroll-sista ng-cloak on-tap="tapped();" delegate-handle="mainScroll" mouse-wheel-scroll>
+ <ion-content scroll-sista ng-cloak on-tap="tapped();" delegate-handle="mainScroll" mouse-wheel-scroll>
<!-- needed for header-shrink so first item doesn't go below header-->
- <!-- <div style="height: 64px;"></div>-->
+ <!-- <div style="height: 64px;"></div>-->
<!-- lets make sure the events list is not empty as collection repeat needs height -->
-
<div ng-if="!eventsBeingLoaded">
-
<ion-list show-delete="eventList.showDelete">
- <ion-item collection-repeat="event in events| filter:search.text" item-height="event.Event.height" id="item-{{$index}}" >
-
-
+ <ion-item collection-repeat="event in events| filter:search.text" item-height="event.Event.height" id="item-{{$index}}">
<span style="float:left;margin-top:-18px;background-color:#96281B;color:#fff;font-size:11px;opacity:0.7;border-radius: 0px 0px 5px 5px;">&nbsp;&nbsp;&nbsp;<i class="ion-calendar"></i>&nbsp;&nbsp;{{prettifyTime(event.Event.StartTime)}}&nbsp;{{tzAbbr}}&nbsp;</span>&nbsp;&nbsp;
-
-
<span style="float:left;margin-top:-18px;background-color:#fff;color:#888;font-size:11px;opacity:1;">&nbsp;&nbsp;<i class="ion-arrow-right-b"></i>&nbsp;{{event.Event.humanizeTime}} <span ng-if="!event.Event.EndTime">(<span translate="kRecordingProgress"></span>)</span>
</span>
-
-
-
-
<span style="float:right;margin-top:-18px;background-color:#444444;color:#fff;font-size:11px;opacity:0.7;border-radius: 0px 0px 5px 5px;">&nbsp;&nbsp;&nbsp;<i class="ion-clock"></i>&nbsp;&nbsp;{{prettifyDate(event.Event.StartTime)}}
&nbsp;{{tzAbbr}}&nbsp;</span>
-
-
-
-
-
<div class="row">
<div class="col col-left">
<!-- this ngswitch displays different icons
depending on the cause of the event -->
-
<div ng-switch on="event.Event.Cause">
<div ng-switch-when="Motion">
-
<i class="ion-android-walk" style="float:left; font-size:200%;"></i>
-
- <i ng-class="(event.Event.DefaultVideo !== undefined && event.Event.DefaultVideo!='')? 'ion-ios-videocam':'ion-images'" style="float:left; padding-left:5px; font-size:100%;"></i>
-
+ <i ng-class="(event.Event.DefaultVideo !== undefined && event.Event.DefaultVideo!='')? 'ion-ios-videocam':'ion-images'" style="float:left; padding-left:5px; font-size:100%;"></i>
<br/>
</div>
<div ng-switch-when="Signal">
<i class="ion-wifi" style="float:left; font-size:200%;"></i>
-
<i ng-class="(event.Event.DefaultVideo !== undefined && event.Event.DefaultVideo!='')? 'ion-ios-videocam':'ion-images'" style="float:left; padding-left:5px; font-size:100%;"></i>
-
<br/>
</div>
<div ng-switch-default>
@@ -87,7 +54,6 @@
</div>
<!-- ng switch -->
<!-- {{event.Event.Cause}} -->
-
<br/>
<span style="font-size:80%; color:rgb(110,110,110)">
{{event.Event.Length}}s
@@ -96,17 +62,26 @@
<!-- col col left-->
<div class="col col-80">
<div class="item-text-wrap">
- <i class="ion-ios-videocam-outline"></i>
- <b>{{event.Event.MonitorName}}</b> ({{event.Event.Name}})
- </div>
+
+ <b>{{event.Event.MonitorName}}</b> ({{event.Event.Id}}) &nbsp;
+ <button ng-if="gifshotSupported" class="button button-small button-clear icon gif-icon" ng-click="permissionsDownload(event)">
+ </button>
+
+
+
+ <a ng-if="(event.Event.DefaultVideo!='' && event.Event.DefaultVideo!==undefined) && $root.platformOS=='desktop' " class="button button-clear button-small icon mp4-icon" href="{{event.Event.videoPath}}" download="{{event.Event.Id}}-video.mp4" ng-click="mp4warning()"></a>
+ <button ng-if="event.Event.DefaultVideo!='' && event.Event.DefaultVideo!=undefined && $root.platformOS!='desktop'" class="button button-small button-clear icon mp4-icon" ng-click="downloadFileToDevice(event.Event.videoPath, event.Event.Id)">
+ </button>
+
+
+ </div>
<i class="ion-images"></i> {{event.Event.Frames}} &nbsp;
<i class="ion-ios-bell-outline"></i> {{event.Event.AlarmFrames}} &nbsp;
<i class="ion-arrow-graph-up-right"></i> {{event.Event.TotScore}}
</div>
</div>
<!--row-->
-
<div class="row" style="font-size:80%; color:rgb(110,110,110)">
<div class="item-text-wrap"><i class="ion-calendar"></i>&nbsp; {{prettify(event.Event.StartTime)}}&nbsp;{{tzAbbr}}&nbsp;
<br/>
@@ -114,178 +89,121 @@
<!-- <br/> Default video:{{event.Event.relativePath}}{{event.Event.DefaultVideo}}-->
</div>
</div>
-
<span style="float:right">
- <div ng-if="event.Event.EndTime || 1">
-
- <button class="button button-small button-outline icon icon-left ion-stats-bars"
- ng-click="closeIfOpen(event);analyzeEvent(event)" > <span translate="kAnalyze"></span>
- </button>
-
- <button class="button button-outline button-small icon icon-left ion-ios-eye"
- ng-click="toggleGroupScrub(event,$index,event.Event.Frames)" > <span translate="kScrub"></span>
- </button>
-
- <button ng-if="event.Event.AlarmFrames > 0" class="button button-outline button-small icon icon-left ion-ios-bell"
- ng-click="toggleGroupAlarms(event,$index,event.Event.Frames)" > <span translate="kAlarms"></span>
- </button>
-
+ <div ng-if="event.Event.EndTime || 1">
+ <button class="button button-small button-outline icon icon-left ion-stats-bars"
+ ng-click="closeIfOpen(event);analyzeEvent(event)" >
+ <span translate="kAnalyze"></span>
+ </button>
+ <button class="button button-outline button-small icon icon-left ion-ios-eye" ng-click="toggleGroupScrub(event,$index,event.Event.Frames)"> <span translate="kScrub"></span>
+ </button>
+ <button ng-if="event.Event.AlarmFrames > 0" class="button button-outline button-small icon icon-left ion-ios-bell" ng-click="toggleGroupAlarms(event,$index,event.Event.Frames)"> <span translate="kAlarms"></span>
+ </button>
+ <button class="button button-outline button-small icon icon-left ion-ios-eye" ng-click="closeIfOpen(event);openModal(event)"> <span translate="kFootage"></span>
+ </button>
- <button class="button button-outline button-small icon icon-left ion-ios-eye"
- ng-click="closeIfOpen(event);openModal(event)" > <span translate="kFootage"></span>
- </button>
- </div>
-
-
- </span>
+
- <!-- this is the event scrub/alarm frames area -->
- <div ng-if="isGroupShown(event)">
-
-
-
- <div ng-if="groupType=='alarms'">
- <br/><br/>
-
- <div style="height:190px;">
- <p>
- <!--scroll <i class="icon ion-arrow-left-c"></i>
+ </div>
+ </span>
+ <!-- this is the event scrub/alarm frames area -->
+ <div ng-if="isGroupShown(event)">
+ <div ng-if="groupType=='alarms'">
+ <br/>
+ <br/>
+ <div style="height:190px;">
+ <p>
+ <!--scroll <i class="icon ion-arrow-left-c"></i>
<i class="icon ion-arrow-right-c"></i>-->
-
- <button ng-click="toggleTypeOfAlarms()" class="button button-small button-assertive button-outline">
- <span translate="kType"></span>:{{typeOfFrames}}
- </button>
- </p>
-
-
- <ion-scroll direction="x" overflow-scroll="false">
-
- <span ng-repeat="alarm in alarm_images | selectFrames: typeOfFrames">
+ <button ng-click="toggleTypeOfAlarms()" class="button button-small button-assertive button-outline">
+ <span translate="kType"></span>:{{typeOfFrames}}
+ </button>
+ </p>
+ <ion-scroll direction="x" overflow-scroll="false">
+ <span ng-repeat="alarm in alarm_images | selectFrames: typeOfFrames">
<figure class = "animated slideInLeft" style="display:inline-block">
<!--{{event.Event.baseURL}} p:{{event.Event.imageMode}}-->
- <figcaption class="smallnote"><span translate="kFrame"></span>:{{alarm.frameid}},<span translate="kScore"></span>:{{alarm.score}}, <span translate="kTime"></span>: {{prettifyTimeSec(alarm.time)}}</figcaption>
- <img ng-if="event.Event.imageMode=='path'" image-spinner-src="{{event.Event.baseURL}}/index.php?view=image&path={{event.Event.relativePath}}{{alarm.fname}}&height=380" style="width: auto; height: auto;max-width: 100%;max-height: 170px" on-tap="showImage(event.Event.baseURL,event.Event.relativePath,alarm.fname, alarm.frameid, event.Event.Id, event.Event.imageMode, alarm.id, alarm_images, $index)" />
-
- <img ng-if="event.Event.imageMode=='fid'" image-spinner-src="{{event.Event.baseURL}}/index.php?view=image&fid={{alarm.id}}" style="width: auto; height: auto;max-width: 100%;max-height: 170px" on-tap="showImage(event.Event.baseURL,event.Event.relativePath,alarm.fname, alarm.frameid, event.Event.Id, event.Event.imageMode, alarm.id, alarm_images, $index)" />
-
-
-
-
- </figure>
-
- </span>
-
- </ion-scroll>
- </div>
- </div>
- <div ng-if="groupType=='scrub'">
- <div ng-if="event.Event.DefaultVideo=='' || loginData.enableh264==false">
-
-
- <br/>
- <br/>
- <br/>
- <div style="width:90%">
- <input ng-model="ionRange.index" type="text" id="mySlider1" slider options="slider_options" />
- </div>
-
- <br/>
-
-
-
- <p>{{mycarousel.index+1}}/{{event.Event.Frames}} <span translate="kType"></span>: {{FrameArray[mycarousel.index].Type}}</p>
-
- <div style="height:190px">
-
-
- <ul rn-carousel rn-carousel-buffered rn-carousel-transition="none" rn-swipe-disabled="true" rn-carousel-index="mycarousel.index" rn-carousel-auto-slide="{{event.Event.Length/event.Event.Frames}}" rn-carousel-pause-on-hover rn-platform="{{$root.platformOS}}">
- <li ng-repeat="slide in slides">
-
-
- <img ng-if="event.Event.imageMode=='path'" imageonload="finishedLoadingImage($index)" image-spinner-src="{{event.Event.baseURL}}/index.php?view=image&path={{event.Event.relativePath}}{{slide.img}}&height=380" image-spinner-loader="lines" height="190px" ;/>
-
- <img ng-if="event.Event.imageMode=='fid'" imageonload="finishedLoadingImage($index)" image-spinner-src="{{event.Event.baseURL}}/index.php?view=image&fid={{slide.id}}" image-spinner-loader="lines" height="190px" ;/>
-
-
-
- <br/>
-
-
- </li>
- </ul>
- </div>
- </div>
- <!-- no DefaultVideo -->
-
-
- <div ng-if="event.Event.DefaultVideo!='' && loginData.enableh264 == true">
-
- <br/>
- <br/>
+ <figcaption class="smallnote"><span translate="kFrame"></span>:{{alarm.frameid}},<span translate="kScore"></span>:{{alarm.score}}, <span translate="kTime"></span>: {{prettifyTimeSec(alarm.time)}}</figcaption>
+ <img ng-if="event.Event.imageMode=='path'" image-spinner-src="{{event.Event.baseURL}}/index.php?view=image&path={{event.Event.relativePath}}{{alarm.fname}}&height=380" style="width: auto; height: auto;max-width: 100%;max-height: 170px" on-tap="showImage(event.Event.baseURL,event.Event.relativePath,alarm.fname, alarm.frameid, event.Event.Id, event.Event.imageMode, alarm.id, alarm_images, $index)" />
+ <img ng-if="event.Event.imageMode=='fid'" image-spinner-src="{{event.Event.baseURL}}/index.php?view=image&fid={{alarm.id}}" style="width: auto; height: auto;max-width: 100%;max-height: 170px" on-tap="showImage(event.Event.baseURL,event.Event.relativePath,alarm.fname, alarm.frameid, event.Event.Id, event.Event.imageMode, alarm.id, alarm_images, $index)" />
+ </figure>
+ </span>
+ </ion-scroll>
+ </div>
+ </div>
+ <div ng-if="groupType=='scrub'">
+ <div ng-if="event.Event.DefaultVideo=='' || loginData.enableh264==false">
+ <br/>
+ <br/>
+ <br/>
+ <div style="width:90%">
+ <input ng-model="ionRange.index" type="text" id="mySlider1" slider options="slider_options" />
+ </div>
+ <br/>
+ <p>{{mycarousel.index+1}}/{{event.Event.Frames}} <span translate="kType"></span>: {{FrameArray[mycarousel.index].Type}}</p>
+ <div style="height:190px">
+ <ul rn-carousel rn-carousel-buffered rn-carousel-transition="none" rn-swipe-disabled="true" rn-carousel-index="mycarousel.index" rn-carousel-auto-slide="{{event.Event.Length/event.Event.Frames}}" rn-carousel-pause-on-hover rn-platform="{{$root.platformOS}}">
+ <li ng-repeat="slide in slides">
+ <img ng-if="event.Event.imageMode=='path'" imageonload="finishedLoadingImage($index)" image-spinner-src="{{event.Event.baseURL}}/index.php?view=image&path={{event.Event.relativePath}}{{slide.img}}&height=380" image-spinner-loader="lines" height="190px" ;/>
+ <img ng-if="event.Event.imageMode=='fid'" imageonload="finishedLoadingImage($index)" image-spinner-src="{{event.Event.baseURL}}/index.php?view=image&fid={{slide.id}}" image-spinner-loader="lines" height="190px" ;/>
<br/>
-
-
- <div class="videogular-container">
- <videogular vg-theme="event.Event.video.config.theme"
- vg-plays-inline="'true'" vg-auto-play="'true'" vg-responsive="true">
- <vg-overlay-play></vg-overlay-play>
- <vg-controls>
- <vg-play-pause-button></vg-play-pause-button>
- <vg-scrub-bar>
- <vg-scrub-bar-current-time></vg-scrub-bar-current-time>
- </vg-scrub-bar>
- <vg-time-display>{{ timeLeft | date:'mm:ss':'+0000' }}</vg-time-display>
- <vg-fullscreen-button></vg-fullscreen-button>
- <vg-volume>
- <vg-mute-button></vg-mute-button>
- </vg-volume>
- </vg-controls>
-
- <vg-media vg-src="event.Event.video.config.sources" vg-native-controls="false">
- </vg-media>
-
-
- </videogular>
- </div>
-
- </div>
- <!-- DefaultVideo -->
- </div> <!-- type = scrub -->
+ </li>
+ </ul>
</div>
- <!-- isGroupShown -->
-
- <ion-delete-button class="ion-minus-circled" ng-click="deleteEvent(event.Event.Id, $index)">
- </ion-delete-button>
-
-
-
- </ion-item>
- </ion-list>
+ </div>
+ <!-- no DefaultVideo -->
+ <div ng-if="event.Event.DefaultVideo!='' && loginData.enableh264 == true">
+ <br/>
+ <br/>
+ <br/>
+ <div class="videogular-container">
+ <videogular vg-theme="event.Event.video.config.theme" vg-plays-inline="'true'" vg-auto-play="'true'" vg-responsive="true">
+ <vg-overlay-play></vg-overlay-play>
+ <vg-controls>
+ <vg-play-pause-button></vg-play-pause-button>
+ <vg-scrub-bar>
+ <vg-scrub-bar-current-time></vg-scrub-bar-current-time>
+ </vg-scrub-bar>
+ <vg-time-display>{{ timeLeft | date:'mm:ss':'+0000' }}</vg-time-display>
+ <vg-fullscreen-button></vg-fullscreen-button>
+ <vg-volume>
+ <vg-mute-button></vg-mute-button>
+ </vg-volume>
+ </vg-controls>
+ <vg-media vg-src="event.Event.video.config.sources" vg-native-controls="false">
+ </vg-media>
+ </videogular>
+ </div>
+ </div>
+ <!-- DefaultVideo -->
+ </div>
+ <!-- type = scrub -->
</div>
+ <!-- isGroupShown -->
+ <ion-delete-button class="ion-minus-circled" ng-click="deleteEvent(event.Event.Id, $index)">
+ </ion-delete-button>
+ </ion-item>
+ </ion-list>
+ </div>
+ <!-- !eventsBeingLoaded-->
- <ion-item ng-show="!events.length">
- <span translate="kNoEvents"></span>
- </ion-item>
- <div ng-if="!eventsBeingLoaded">
- <ion-infinite-scroll ng-if="moreDataCanBeLoaded()" icon="ion-loading-c" on-infinite="loadMore()" distance="2%">
- </ion-infinite-scroll>
- </div>
+ <ion-item ng-show="!events.length">
+ <span translate="kNoEvents"></span>
+ </ion-item>
+ <div ng-if="!eventsBeingLoaded">
+ <ion-infinite-scroll ng-if="moreDataCanBeLoaded()" icon="ion-loading-c" on-infinite="loadMore()" distance="2%">
+ </ion-infinite-scroll>
+ </div>
+ <canvas id="canvas" class="hiddengifcanvas"></canvas>
</ion-content>
-
-
-
<div class="events-float-filter" ng-if="isEventFilterOn" on-tap="filterTapped();"><span translate="kFilterOn"></span></div>
-
- <div class="bwmode" ng-if="$root.runMode=='lowbw'">
- <span translate="kLowBWDisplay"></span>
- </div>
-
-
+ <div class="bwmode" ng-if="$root.runMode=='lowbw'">
+ <span translate="kLowBWDisplay"></span>
+ </div>
<ion-pull-up-footer class="zmPullup" on-expand="footerExpand()" on-collapse="footerCollapse()" initial-state="minimized" default-behavior="expand">
-
<ion-pull-up-handle width="100" height="25" toggle="ion-chevron-up ion-chevron-down" style="border-radius: 25px 25px 0 0">
<i class="icon ion-chevron-up"></i>
</ion-pull-up-handle>
@@ -293,74 +211,57 @@
<h1 class="title" ion-pull-up-trigger><span translate="kLatestEvents"></span></h1>
</ion-pull-up-bar>
<ion-pull-up-content scroll="true">
-
<div class="list list-inset">
<div class="item item-divider"><span translate="k1HourSummary"></span></div>
<div ng-repeat="hour in hours|filter:{ monitor: '!'+'(Unknown)'}" id="hour-{{$index}}">
-
<span style="color:black">
<a class="item item-icon-right" href=""
ng-click="showEvents('1', 'hour',hour.mid);">
<b>{{hour.monitor}}</b> {{hour.events}} <span translate="kEvents"></span>
- <i class="icon ion-android-arrow-dropright"></i>
+ <i class="icon ion-android-arrow-dropright"></i>
</a>
</span>
-
</div>
</div>
-
<div class="list list-inset">
<div class="item item-divider"><span translate="k1DaySummary"></span></div>
<div ng-repeat="day in days|filter:{ monitor: '!'+'(Unknown)'}" id="day-{{$index}}">
-
<span style="color:black">
<a class="item item-icon-right" href=""
ng-click="showEvents('1', 'day',day.mid);">
<b>{{day.monitor}}</b> {{day.events}} <span translate="kEvents"></span>
- <i class="icon ion-android-arrow-dropright"></i>
+ <i class="icon ion-android-arrow-dropright"></i>
</a>
</span>
-
</div>
</div>
-
-
<div class="list list-inset">
<div class="item item-divider"><span translate="k1WeekSummary"></span></div>
<div ng-repeat="week in weeks|filter:{ monitor: '!'+'(Unknown)'}" id="week-{{$index}}">
-
<span style="color:black">
<a class="item item-icon-right" href=""
ng-click="showEvents('1', 'week',week.mid);">
<b>{{week.monitor}}</b> {{week.events}} <span translate="kEvents"></span>
- <i class="icon ion-android-arrow-dropright"></i>
+ <i class="icon ion-android-arrow-dropright"></i>
</a>
</span>
-
</div>
</div>
-
<div class="list list-inset">
<div class="item item-divider"><span translate="k1MonthSummary"></span></div>
<div ng-repeat="month in months|filter:{ monitor: '!'+'(Unknown)'}" id="month-{{$index}}">
-
<span style="color:black">
<a class="item item-icon-right" href=""
ng-click="showEvents('1', 'months',month.mid);">
<b>{{month.monitor}}</b> {{month.events}} <span translate="kEvents"></span>
- <i class="icon ion-android-arrow-dropright"></i>
+ <i class="icon ion-android-arrow-dropright"></i>
</a>
</span>
-
</div>
- </div>
+ </div>
<br/>
<br/>
-
-
</ion-pull-up-content>
</ion-pull-up-footer>
-
-
</ion-view>
diff --git a/www/templates/eventserversettings.html b/www/templates/eventserversettings.html
index e6ec4614..393eb61b 100644
--- a/www/templates/eventserversettings.html
+++ b/www/templates/eventserversettings.html
@@ -1,62 +1,41 @@
<ion-view cache-view="false">
-
<ion-nav-title>{{'kEventServer' | translate}}/{{getTitle();}} </ion-nav-title>
-
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" ng-click="openMenu()"></button>
<button data-badge="{{$root.alarmCount}}" class="animated infinite tada button button-icon button-clear ion-ios-bell notification-badge" ng-click="handleAlarms();" ng-if="$root.isAlarm"></button>
</ion-nav-buttons>
-
-
<ion-nav-buttons side="right">
<button class="button button-clear" ng-click="saveItems()">{{'kSave'|translate}}</button>
</ion-nav-buttons>
-
- <ion-content scroll-sista delegate-handle="none" overflow-scroll="false" mouse-wheel-scroll >
-
- <div class="list list-inset">
- {{'kEventServerConfig1' | translate }}
+ <ion-content scroll-sista delegate-handle="none" overflow-scroll="false" mouse-wheel-scroll>
+ <div class="list list-inset">
+ {{'kEventServerConfig1' | translate }}
</div>
<ion-checkbox ng-model="loginData.isUseEventServer" ng-checked="loginData.isUseEventServer">{{'kUseEventServer' | translate}}</ion-checkbox>
-
-
- <ion-item ng-href="" ng-click="selectScreen()">
+ <ion-item ng-href="" ng-click="selectScreen()">
{{'kOnTapNavigate' | translate}}: {{defScreen}}
</ion-item>
-
<label class="item item-input item-floating-label" ng-if="loginData.isUseEventServer">
<span class="input-label">{{'kEventServer' | translate}}</span>
<input autocapitalize="none" autocomplete="off" autocorrect="off" type="text" placeholder="Event notification url" ng-model="loginData.eventServer">
-
</label>
-
- <ion-toggle ng-if="loginData.isUseEventServer" ng-model="loginData.disablePush" ng-checked="loginData.disablePush" toggle-class="toggle-calm" class="item-text-wrap">{{'kOnlyUseWebSocket'| translate }}<br/><p>{{'kDisablePush' | translate }}</p></ion-toggle>
-
-
- <!-- vibrate app setting is not av. on iOS-->
- <ion-toggle ng-if="loginData.isUseEventServer && $root.platformOS !='ios'" ng-model="loginData.vibrateOnPush" ng-checked="loginData.vibrateOnPush" toggle-class="toggle-calm" >{{'kVibrateOnPush'| translate }}</ion-toggle>
-
-
- <ion-toggle ng-if="loginData.isUseEventServer" ng-model="loginData.soundOnPush" ng-checked="loginData.soundOnPush" toggle-class="toggle-calm" >{{'kSoundOnPush'| translate }}</ion-toggle>
-
+ <ion-toggle ng-if="loginData.isUseEventServer" ng-model="loginData.disablePush" ng-checked="loginData.disablePush" toggle-class="toggle-calm" class="item-text-wrap">{{'kOnlyUseWebSocket'| translate }}
+ <br/>
+ <p>{{'kDisablePush' | translate }}</p>
+ </ion-toggle>
+ <!-- vibrate app setting is not av. on iOS-->
+ <ion-toggle ng-if="loginData.isUseEventServer && $root.platformOS !='ios'" ng-model="loginData.vibrateOnPush" ng-checked="loginData.vibrateOnPush" toggle-class="toggle-calm">{{'kVibrateOnPush'| translate }}</ion-toggle>
+ <ion-toggle ng-if="loginData.isUseEventServer" ng-model="loginData.soundOnPush" ng-checked="loginData.soundOnPush" toggle-class="toggle-calm" class="item-text-wrap">{{'kSoundOnPush'| translate }}</ion-toggle>
<!--
<ion-toggle ng-model="loginData.defaultPushSound" toggle-class="toggle-calm" class="item-text-wrap" ng-checked="{{loginData.defaultPushSound}}">use system sound<p>please save and restart app</p></ion-toggle>
-->
<ion-list>
<div ng-repeat="monitor in monitors">
-
- <ion-item class="custom-list"
- ng-click="toggleGroup(monitor)"
- ng-class="{active: isGroupShown(monitor)}">
- <i class="icon" ng-class="isGroupShown(monitor) ? 'ion-minus' : 'ion-plus'"></i>
- &nbsp;
-
- {{monitor.Monitor.Name}}
- </ion-item>
-
- <ion-item class="item-accordion"
- ng-show="isGroupShown(monitor)">
- <span class="item-checkbox">
+ <ion-item class="custom-list" ng-click="toggleGroup(monitor)" ng-class="{active: isGroupShown(monitor)}">
+ <i class="icon" ng-class="isGroupShown(monitor) ? 'ion-minus' : 'ion-plus'"></i> &nbsp; {{monitor.Monitor.Name}}
+ </ion-item>
+ <ion-item class="item-accordion" ng-show="isGroupShown(monitor)">
+ <span class="item-checkbox">
{{'kReportEvents' | translate }}
<label class="checkbox">
@@ -64,30 +43,15 @@
</label>
</span>
</ion-item>
-
- <ion-item class="item-accordion"
- ng-show="isGroupShown(monitor)">
- <div class="item-input-inset" >
- <label class="item-input-wrapper" >
- <input type="tel" placeholder="sec." ng-model="monitor.Monitor.reportingInterval">
- </label>
- &nbsp;{{'kMinimumIntervals' | translate}}
-
-
- </div>
+ <ion-item class="item-accordion" ng-show="isGroupShown(monitor)">
+ <div class="item-input-inset">
+ <label class="item-input-wrapper">
+ <input type="tel" placeholder="sec." ng-model="monitor.Monitor.reportingInterval">
+ </label>
+ &nbsp;{{'kMinimumIntervals' | translate}}
+ </div>
</ion-item>
-
-
</div>
-
</ion-list>
-
-
-
-
-
</ion-content>
</ion-view>
-
-
-
diff --git a/www/templates/first-use.html b/www/templates/first-use.html
index 5599f982..884fb0c9 100644
--- a/www/templates/first-use.html
+++ b/www/templates/first-use.html
@@ -7,8 +7,6 @@
<div id="responsive-image">
<img src="img/authlogo.png">
</div>
-
-
<br/>
<span style="color:white">
<h2 style="color:white" >Hi There!</h2>
@@ -21,15 +19,11 @@
</span>
<br/>
<div id="firstuse">
-
-
- <button class="button icon icon-left ion-wand button-stable animated bounceInUp" ng-click="goToWizard()">
+ <button class="button icon icon-left ion-wand button-stable animated bounceInUp" ng-click="goToWizard()">
{{'kWizard' | translate}}
</button>
-
- <button class="button icon icon-left ion-university button-stable animated bounceInUp" ng-click="goToLogin()">
+ <button class="button icon icon-left ion-university button-stable animated bounceInUp" ng-click="goToLogin()">
{{'kExpert' | translate }}
-
</button>
<br/>
<center>
@@ -37,12 +31,8 @@
{{'kLanguage' | translate}}
</button>
</center>
-
</div>
</center>
-
-
</div>
-
</ion-content>
-</ion-view> \ No newline at end of file
+</ion-view>
diff --git a/www/templates/help.html b/www/templates/help.html
index fffdf2d1..c0f714cf 100644
--- a/www/templates/help.html
+++ b/www/templates/help.html
@@ -1,21 +1,12 @@
-<ion-view cache-view="false" view-title="Help">
-
+<ion-view cache-view="false" view-title="{{'kHelp' | translate}}">
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" ng-click="openMenu()"></button>
-
<button data-badge="{{$root.alarmCount}}" class="animated infinite tada button button-icon button-clear ion-ios-bell notification-badge" ng-click="handleAlarms();" ng-if="$root.isAlarm"></button>
-
</ion-nav-buttons>
-
-
- <ion-content class="padding" scroll-sista delegate-handle="none" overflow-scroll="false" mouse-wheel-scroll>
-
+ <ion-content class="padding" scroll-sista delegate-handle="none" overflow-scroll="false" mouse-wheel-scroll>
<p><small>{{$root.appName}} v{{zmAppVersion}}</small></p>
<div class="list">
-
- <div id="insertHelp"></div>
+ <div id="insertHelp"></div>
</div>
-
</ion-content>
-
-</ion-view> \ No newline at end of file
+</ion-view>
diff --git a/www/templates/important_message.html b/www/templates/important_message.html
index bd3274f5..f0fd245e 100644
--- a/www/templates/important_message.html
+++ b/www/templates/important_message.html
@@ -1,7 +1,6 @@
<ion-view view-title="{{$root.appName}}" hide-nav-bar="false" hide-back-button="false" cache-view="false">
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" ng-click="openMenu()"></button>
-
</ion-nav-buttons>
<ion-content class="pin-background">
<div style="margin-left:20px; margin-right:20px">
@@ -11,8 +10,6 @@
<div id="responsive-image">
<img src="img/authlogo.png">
</div>
-
-
<br/>
<span style="color:white">
<h2 style="color:white" class="animated bounce">{{'kImpMsg1' | translate}}</h2>
@@ -32,10 +29,6 @@
</span>
</center>
<br/>
-
-
-
</div>
-
</ion-content>
-</ion-view> \ No newline at end of file
+</ion-view>
diff --git a/www/templates/log.html b/www/templates/log.html
index e53f00aa..f4e32d66 100644
--- a/www/templates/log.html
+++ b/www/templates/log.html
@@ -1,29 +1,20 @@
<ion-view view-title="{{'kLogs' | translate}}">
-
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" ng-click="openMenu()"></button>
-
<button data-badge="{{$root.alarmCount}}" class="animated infinite tada button button-icon button-clear ion-ios-bell notification-badge" ng-click="handleAlarms();" ng-if="$root.isAlarm"></button>
-
-
-
</ion-nav-buttons>
-
<ion-nav-buttons side="right">
<a class="button button-icon icon ion-trash-a" ng-href="" ng-click="deleteLogs()"></a>
-
<div ng-if="$root.platformOS!='desktop'">
- <a style="" class="button button-icon icon ion-email"
- ng-href="" ng-click="sendEmail(log.logString)" > </a>
+ <a style="" class="button button-icon icon ion-email" ng-href="" ng-click="sendEmail(log.logString)"> </a>
</div>
<div ng-if="$root.platformOS=='desktop'">
- <a style="" class="button button-icon icon ion-android-download"
- ng-href="" ng-click="sendEmail(log.logString)" > </a>
+ <a style="" class="button button-icon icon ion-android-download" ng-href="" ng-click="sendEmail(log.logString)"> </a>
</div>
</ion-nav-buttons>
-
- <ion-content scroll-sista delegate-handle="none" overflow-scroll="false" mouse-wheel-scroll>
- <b>{{$root.appName}} {{'kVersion'|translate}}: {{zmAppVersion}}</b><br/>
+ <ion-content scroll-sista delegate-handle="none" overflow-scroll="false" mouse-wheel-scroll>
+ <b>{{$root.appName}} {{'kVersion'|translate}}: {{zmAppVersion}} ({{$root.platformOS}})</b>
+ <br/>
<!-- don't indent here -- its a pre-->
<pre>{{log.logString}}</pre>
</ion-content>
diff --git a/www/templates/login.html b/www/templates/login.html
index 738c1c46..9372aae0 100644
--- a/www/templates/login.html
+++ b/www/templates/login.html
@@ -1,120 +1,85 @@
<ion-view view-title="{{'kSettings' | translate}}" cache-view="false">
-
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" ng-click="openMenu()"></button>
<button class="button button-icon button-clear ion-arrow-down-b" ng-click="serverActionSheet()"></button>
-
-
-
<button data-badge="{{$root.alarmCount}}" class="animated infinite tada button button-icon button-clear ion-ios-bell notification-badge" ng-click="handleAlarms();" ng-if="$root.isAlarm"></button>
</ion-nav-buttons>
-
<ion-nav-buttons side="right">
- <button class="button button-clear" ng-click="saveItems()">Save</button>
+ <button class="button button-clear" ng-click="saveItems()">{{'kSave' | translate}}</button>
</ion-nav-buttons>
-
- <ion-content scroll-sista delegate-handle="none" overflow-scroll="false" mouse-wheel-scroll >
-
- <div class="item item-input-inset">
+ <ion-content scroll-sista delegate-handle="none" overflow-scroll="false" mouse-wheel-scroll>
+ <div class="item item-text-wrap item-input-inset">
{{'kServerName' | translate }}:&nbsp;
<label class="item-input-wrapper">
<input autocorrect="off" type="text" placeholder="{{'kExampleServer' | translate}}" ng-model="loginData.serverName">
</label>
</div>
-
<label>
- <ion-toggle ng-model="loginData.enableLowBandwidth" toggle-class="toggle-calm">{{'kLowBandwidth'|translate}}
+ <ion-toggle ng-model="loginData.enableLowBandwidth" toggle-class="toggle-calm"><span class="item-text-wrap">{{'kLowBandwidth'|translate}}</span>
</ion-toggle>
</label>
<label ng-if="loginData.enableLowBandwidth">
- <ion-toggle ng-model="loginData.autoSwitchBandwidth" toggle-class="toggle-calm">{{'kAutoSwitchBW'|translate}}
+ <ion-toggle ng-model="loginData.autoSwitchBandwidth" toggle-class="toggle-calm"><span class="item-text-wrap">{{'kAutoSwitchBW'|translate}}</span>
</ion-toggle>
</label>
-
-
<div class="list list-inset">
<span style="color:rgb(100,100,100)">
<i class="ion-android-home" style="font-size:150%"></i>
{{'kZMSettingsFor' | translate}} {{loginData.serverName || ('kUnknown' | translate)}}
</span>
-
- <div class="item">
+ <p ng-if="$root.platformOS=='android'" style="font-size:0.8em; color:gray">{{'kDisableSamsung' | translate}}</p>
+ <div class="item item-text-wrap">
<ion-checkbox ng-model="check.isUseAuth">{{'kUseZmAuth' | translate }}</ion-checkbox>
-
<div ng-if="check.isUseAuth">
-
<label class="item item-input item-floating-label">
<span class="input-label">{{'kUserName'|translate}}</span>
<input autocapitalize="none" autocomplete="off" autocorrect="off" type="text" placeholder="username" ng-model="loginData.username">
</label>
-
<label class="item item-input item-floating-label">
<span class="input-label">{{'kPassword' | translate}}</span>
<input type="password" placeholder="password" ng-model="loginData.password">
</label>
</div>
-
</div>
<label class="item item-input item-floating-label">
<span class="input-label">{{'kPortalUrl' | translate}}</span>
<input autocapitalize="none" autocomplete="off" autocorrect="off" type="text" placeholder="ZM portal url " ng-model="loginData.url" ng-keyup="portalKeypress($event)">
</label>
-
<!--<button class="button button-small button-clear icon-left ion-wand" ng-click="detectCgi()">tap here to discover cgi-bin
</button>-->
-
<label class="item item-input item-floating-label">
-
-
<!--<span style="float:right;margin-top:-7px;background-color:#6d0909;color:#fff;font-size:14px;opacity:0.7;width:90px;border-radius: 0px 0px 5px 5px;" on-tap="detectCgi();">&nbsp;&nbsp;&nbsp;<i class="ion-wand"></i>discover</span>-->
-
-
<span class="input-label">{{'kPathToCgi' | translate}}</span>
-
<input autocapitalize="none" autocomplete="off" autocorrect="off" type="text" placeholder="eg. server.com/zm/cgi-bin" ng-model="loginData.streamingurl">
-
</label>
-
-
-
<label class="item item-input item-floating-label">
<span class="input-label">{{'kApiUrl' | translate}}</span>
<input autocapitalize="none" autocomplete="off" autocorrect="off" type="text" placeholder="ZM api url" ng-model="loginData.apiurl">
</label>
-
<a class="item item-icon-right" href="" ng-click="selectFallback()">
<i class="icon ion-ios-arrow-right">
</i> {{'kFallback' | translate}}
<p>{{loginData.fallbackConfiguration}}</p>
</a>
-
<a class="item item-icon-right" href="" ng-click="eventServerSettings()">
<i class="icon ion-ios-arrow-right">
</i> {{'kEventServer' | translate}}
</a>
-
-
<div ng-if="$root.platformOS != 'desktop'">
<label>
-
- <ion-toggle ng-model="loginData.usePin" ng-change="pinPrompt();" ng-checked="{{loginData.usePin}}" toggle-class="toggle-calm">{{'kPassword' | translate}} {{'kProtect'|translate}}</ion-toggle>
+ <ion-toggle ng-model="loginData.usePin" ng-change="pinPrompt();" ng-checked="{{loginData.usePin}}" toggle-class="toggle-calm"><span class="item-text-wrap">{{'kPassword' | translate}} {{'kProtect'|translate}}</span></ion-toggle>
</label>
</div>
-
<label>
- <ion-toggle ng-model="loginData.useSSL" ng-checked="{{loginData.useSSL}}" toggle-class="toggle-calm">{{'kUseSSL' | translate}}</ion-toggle>
+ <ion-toggle ng-model="loginData.useSSL" ng-checked="{{loginData.useSSL}}" toggle-class="toggle-calm"><span class="item-text-wrap">{{'kUseSSL' | translate}}</span></ion-toggle>
</label>
-
<div ng-if="$root.platformOS != 'desktop'">
<label>
-
- <ion-toggle ng-model="loginData.keepAwake" ng-checked="{{loginData.keepAwake}}" toggle-class="toggle-calm">{{'kAwake1'|translate}}
- <p>{{'kAwake2'| translate}}</p>
+ <ion-toggle ng-model="loginData.keepAwake" ng-checked="{{loginData.keepAwake}}" toggle-class="toggle-calm"><span class="item-text-wrap">{{'kAwake1'|translate}}
+ <p>{{'kAwake2'| translate}}</p></span>
</ion-toggle>
</label>
</div>
</div>
-
-
</ion-content>
-</ion-view> \ No newline at end of file
+</ion-view>
diff --git a/www/templates/lowversion.html b/www/templates/lowversion.html
index d9a50986..d6b9cb7e 100644
--- a/www/templates/lowversion.html
+++ b/www/templates/lowversion.html
@@ -1,5 +1,4 @@
<ion-view view-title="{{$root.appName}}" hide-nav-bar="true" hide-back-button="true" cache-view="false">
-
<ion-content class="pin-background">
<div style="margin-left:20px; margin-right:20px">
<center>
@@ -8,8 +7,6 @@
<div id="responsive-image">
<img src="img/authlogo.png">
</div>
-
-
<br/>
<span style="color:white">
<h2 style="color:white" class="animated bounce">{{'kZMUpgradeNeeded' | translate}}</h2>
@@ -22,10 +19,7 @@
</span>
<br/>
-
-
</center>
</div>
-
</ion-content>
-</ion-view> \ No newline at end of file
+</ion-view>
diff --git a/www/templates/monitors-modal.html b/www/templates/monitors-modal.html
index 1cfbfac9..931d9c33 100644
--- a/www/templates/monitors-modal.html
+++ b/www/templates/monitors-modal.html
@@ -1,77 +1,46 @@
<div ng-controller="MonitorModalCtrl" ng-cloak>
-
- <ion-modal-view cache-view="false" style="background-color:#444444" >
- <ion-content on-double-tap="closeModal();" >
-
- <ion-scroll delegate-handle="imgscroll" has-bouncing=false min-zoom=1 zooming="true" direction="xy" style="width: 100%;" overflow-scroll="false" >
+ <ion-modal-view cache-view="false" style="background-color:#444444">
+ <ion-content on-double-tap="closeModal();">
+ <ion-scroll delegate-handle="imgscroll" has-bouncing=false min-zoom=1 zooming="true" direction="xy" style="width: 100%;" overflow-scroll="false">
<!-- android needs this 100vh - otherwise max- does not work -->
<!-- -->
<div id="monitorimage" style="height: 100vh;" class="main">
-
-
-
<div ng-if="$root.authSession!='undefined'">
-
-
<div ng-if="!animationInProgress && !isBackground() && connKey">
<!--<span style="color:white">{{currentStreamMode}}</span>-->
-
- <img id="singlemonitor" style="width:100vw; height:100vh;" image-spinner-loader="lines" image-spinner-src="{{monitor.Monitor.streamingURL}}/nph-zms?mode={{currentStreamMode}}&monitor={{monitorId}}&scale={{quality}}{{$root.authSession}}&rand={{$root.modalRand}}&connkey={{connKey}}" ng-class="{'object-fit_cover':imageFit==false, 'object-fit_contain':imageFit==true}" on-swipe-left="onSwipe(monitorId,1)" on-swipe-right="onSwipe(monitorId,-1)" on-double-tap="closeModal();" />
-
-
-
-
-
+ <img id="singlemonitor" style="width:100vw; height:100vh;" image-spinner-loader="lines" image-spinner-src="{{monitor.Monitor.streamingURL}}/nph-zms?mode={{currentStreamMode}}&monitor={{monitorId}}&scale={{quality}}{{$root.authSession}}&rand={{$root.modalRand}}&connkey={{connKey}}" ng-class="{'object-fit_cover':imageFit==false, 'object-fit_contain':imageFit==true}" on-swipe-left="onSwipe(monitorId,1)" on-swipe-right="onSwipe(monitorId,-1)" on-double-tap="closeModal();" />
</div>
<div ng-if="animationInProgress || isBackground()">
<img style="width:100vw; height:100vh" ng-src="img/pausevideo.png" class="object-fit_contain" />
</div>
-
</div>
<div ng-if="$root.authSession=='undefined'">
<img id="singlemonitor" ng-src="img/pausevideo.png" style="width:100vw; height:100vh; display:block;" class="object-fit_contain" width="{{((devWidth)/(7-monitorSize[$index]))}}px;" />
</div>
</div>
</ion-scroll>
-
</ion-content>
-
-
<div ng-show="isControllable=='1' && showPTZ">
-
-
-
<div class="ptzcentered">
<circular options="ptzRadialMenuOptions">
</circular>
</div>
-
<div ng-if="presetOn" class="ptzpresetbuttons animated fadeInDown" id="presetlist">
<div ng-repeat="preset in ptzPresets track by $index">
<button class="button {{preset.icon}} button-small {{preset.style}}" style="float:left;margin-right:10px;margin-bottom:10px;" ng-click="controlPTZ(monitorId, preset.cmd);">{{preset.name}}</button>
</div>
</div>
-
-
<div class="ptzcenteredbotbutton">
<div ng-if="canZoom">
<a class="button button-small icon ion-search button-positive" href="" ng-click="controlPTZ(monitorId, zoomInCommand);">+</a>
<a class="button button-small icon ion-search button-positive" href="" ng-click="controlPTZ(monitorId, zoomOutCommand);">-</a>
<a class="button button-small icon ion-search button-positive" href="" ng-click="controlPTZ(monitorId, zoomStopCommand);">x</a>
-
</div>
<br/>
<a class="button button-small icon ion-stop button-assertive" href="" ng-click="controlPTZ(monitorId, ptzStopCommand);"></a>
-
-
<a class="button button-small button-royal" href="" ng-click="togglePresets();">{{controlToggle}}</a>
</div>
-
</div>
-
-
-
-
</ion-modal-view>
<nav mfb-menu position="br" effect="zoomin" label="{{'kCollapse' | translate}}" active-icon="ion-chevron-down" resting-icon="ion-chevron-up" toggling-method="click">
<button mfb-button icon="ion-arrow-resize" label="{{imageFit? ('kFitScreen' | translate):('kFillScreen' | translate)}}" ng-click="scaleImage();">
@@ -80,9 +49,7 @@
</button>
<button mfb-button icon="ion-arrow-expand" label="{{'kControl'| translate}}" ng-click="togglePTZ();">
</button>
-
</nav>
-
<nav mfb-menu position="tr" effect="zoomin" label="{{'kCollapse' | translate}}" active-icon="ion-chevron-up" resting-icon="ion-chevron-down" toggling-method="click">
<button mfb-button icon="ion-android-arrow-back" label="{{'kPrevMonitor' | translate}} " ng-click="onTap(monitorId,-1);">
</button>
@@ -90,58 +57,35 @@
</button>
<button mfb-button icon="ion-close" label="{{'kExitLiveView' | translate}}" ng-click="closeModal();">
</button>
-
</nav>
-
-
-
-
- <div id="flyoutmenu" style="position:absolute;bottom:80px;left:10px">
- <ul>
-
- <li>
- <a href="" ng-click="saveImageToPhoneWithPerms(monitorId)"> <i class="icon ion-ios-camera"></i></a>
- </li>
-
- <li ng-if="$root.platformOS == 'desktop'">
- <a href="" ng-click="zoomImage(1)"><i class="ion-plus-round"></i></a>
- </li>
- <li ng-if="$root.platformOS == 'desktop'">
- <a href="" ng-click="zoomImage(-1)"><i class="ion-minus-round"></i></a>
- </li>
-
- <li>
- <a href="" ng-click="enableAlarm(monitorId,true)"> <i class="icon ion-flash"></i></a>
- </li>
- <li>
- <a href="" ng-click="enableAlarm(monitorId,false)"> <i class="icon ion-flash-off"></i></a>
-
-
- </li>
- <li>
- <a href="" ng-click="toggleCycle()"> <i class="icon ion-android-bicycle"></i>-{{cycleText}}</a>
-
-
- </li>
-
-
- <!--<li>
+ <div id="flyoutmenu" style="position:absolute;bottom:80px;left:10px">
+ <ul>
+ <li>
+ <a href="" ng-click="saveImageToPhoneWithPerms(monitorId)"> <i class="icon ion-ios-camera"></i></a>
+ </li>
+ <li ng-if="$root.platformOS == 'desktop'">
+ <a href="" ng-click="zoomImage(1)"><i class="ion-plus-round"></i></a>
+ </li>
+ <li ng-if="$root.platformOS == 'desktop'">
+ <a href="" ng-click="zoomImage(-1)"><i class="ion-minus-round"></i></a>
+ </li>
+ <li>
+ <a href="" ng-click="enableAlarm(monitorId,true)"> <i class="icon ion-flash"></i></a>
+ </li>
+ <li>
+ <a href="" ng-click="enableAlarm(monitorId,false)"> <i class="icon ion-flash-off"></i></a>
+ </li>
+ <li>
+ <a href="" ng-click="toggleCycle()"> <i class="icon ion-android-bicycle"></i>-{{cycleText}}</a>
+ </li>
+ <!--<li>
<a href="" ng-click="cast(monitorId, monitor)"> <i class="icon ion-android-funnel"></i></a>
</li>-->
-
-
-
-
-
- <li ng-if="$root.isAlarm">
- <a data-badge="{{$root.alarmCount}}" class="notification-badge animated infinite tada" href="" ng-click="handleAlarms()"><i class="ion-ios-bell"></i></a>
- </li>
-
-
- </ul>
-</div>
-
+ <li ng-if="$root.isAlarm">
+ <a data-badge="{{$root.alarmCount}}" class="notification-badge animated infinite tada" href="" ng-click="handleAlarms()"><i class="ion-ios-bell"></i></a>
+ </li>
+ </ul>
+ </div>
<div class="monitor-modal-text">{{monitorName}} &nbsp;<span style="{{stateColor()}}">{{monStatus}}&nbsp;</span></div>
-
</div>
diff --git a/www/templates/monitors.html b/www/templates/monitors.html
index d38e8f65..cd9e8e8e 100644
--- a/www/templates/monitors.html
+++ b/www/templates/monitors.html
@@ -1,82 +1,62 @@
<ion-view view-title="{{'kMonitors' | translate}}" cache-view="false">
-
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" ng-click="openMenu()">
</button>
-
<button data-badge="{{$root.alarmCount}}" class="animated infinite tada button button-icon button-clear ion-ios-bell notification-badge" ng-click="handleAlarms();" ng-if="$root.isAlarm"></button>
-
</ion-nav-buttons>
-
-
- <ion-content scroll-sista delegate-handle="none" overflow-scroll="false" mouse-wheel-scroll >
- <ion-refresher pulling-text="Pull to reload Monitors..." spinner="bubbles" on-refresh="doRefresh()">
+ <ion-content scroll-sista delegate-handle="none" overflow-scroll="false" mouse-wheel-scroll>
+ <ion-refresher pulling-text="Pull to reload Monitors..." spinner="bubbles" on-refresh="doRefresh()">
</ion-refresher>
- <div style="float:right;margin-top:3px;margin-right:8px;">
- <a class="button button-small icon icon-left icon ion-ios-world-outline"
- href="" ng-click="changeConfig('All', '','1','Monitor');">{{'kGlobalConfiguration' | translate}}</a>
- </div><br/>
- <div class="list card" ng-repeat="monitor in monitors">
- <div class="item" ng-style="{'background-color': monitor.Monitor.Enabled=='1'?'white':'white'}">
- <div ng-if="monitor.Monitor.Enabled == '1'">
- <div class='item'>
-
- <span class="item-icon-left">
+ <div style="float:right;margin-top:3px;margin-right:8px;">
+ <a class="button button-small icon icon-left icon ion-ios-world-outline" href="" ng-click="changeConfig('All', '','1','Monitor');">{{'kGlobalConfiguration' | translate}}</a>
+ </div>
+ <br/>
+ <div class="list card" ng-repeat="monitor in monitors">
+ <div class="item" ng-style="{'background-color': monitor.Monitor.Enabled=='1'?'white':'white'}">
+ <div ng-if="monitor.Monitor.Enabled == '1'">
+ <div class='item'>
+ <span class="item-icon-left">
<div class="icon">
- <span class="ion-ios-videocam-outline"></span>
- &nbsp;
-
- <span ng-class="{'ion-eye':monitor.Monitor.listDisplay=='show','ion-eye-disabled':monitor.Monitor.listDisplay!='show'}"> </span>
- </div>
-
- &nbsp;
- <b>{{monitor.Monitor.Name}}</b>
- </span>
-
-
- <span class="item-icon-right">
+ <span class="ion-ios-videocam-outline"></span> &nbsp;
+ <span ng-class="{'ion-eye':monitor.Monitor.listDisplay=='show','ion-eye-disabled':monitor.Monitor.listDisplay!='show'}"> </span>
+ </div>
+ &nbsp;
+ <b>{{monitor.Monitor.Name}}</b>
+ </span>
+ <span class="item-icon-right">
<i class="icon {{monitor.Monitor.char}}" style="color:{{monitor.Monitor.color}};"></i>
</span>
-
- <!-- <i ng-class="{'icon ion-eye':monitor.Monitor.listDisplay=='show','icon ion-eye-disabled':monitor.Monitor.listDisplay!='show'}"> </i>-->
-
-
- </div>
- </div>
-
- <div ng-if="monitor.Monitor.Enabled != '1'">
- <span class='item item-icon-left item-icon-right'>
+ <!-- <i ng-class="{'icon ion-eye':monitor.Monitor.listDisplay=='show','icon ion-eye-disabled':monitor.Monitor.listDisplay!='show'}"> </i>-->
+ </div>
+ </div>
+ <div ng-if="monitor.Monitor.Enabled != '1'">
+ <span class='item item-icon-left item-icon-right'>
<i class="icon ion-ios-videocam-outline"></i>
<b>{{monitor.Monitor.Name}}</b>
<i class="icon {{monitor.Monitor.char}}" style="color:grey;"></i>
</span>
- </div>
-
-
- <p>{{'kMode' | translate}} :{{monitor.Monitor.Function}}<br/>
- {{'kResolution' | translate}}: {{monitor.Monitor.Width}}*{{monitor.Monitor.Height}}<br/>
- {{'kMaxFPS' | translate}}: {{monitor.Monitor.MaxFPS}}
- <br/> {{'kAlarmMaxFPS' | translate}}:{{monitor.Monitor.AlarmMaxFPS}}
- <br/> {{'kAlarmFrameCount' |translate}}: {{monitor.Monitor.AlarmFrameCount}}
- <br/> {{'kStatus' | translate}}: {{monitor.Monitor.isRunningText}}
- <br/> {{'kId' | translate}}: {{monitor.Monitor.Id}}
- <br/> {{'kEventRecording' | translate}}: {{(monitor.Monitor.VideoWriter>0?'kVideo':'kImages') | translate}}
- <br/>
- <br/>
- </p>
-
- <div style="float:right;">
- <a class="button button-small icon icon-left icon ion-gear-a"
- href="" ng-click="changeConfig(monitor.Monitor.Name, monitor.Monitor.Id,monitor.Monitor.Enabled,monitor.Monitor.Function);">{{'kConfiguration' | translate}}</a>
- <a class="button button-small icon icon-left ion-calendar" href="#/events/{{monitor.Monitor.Id}}/false">{{'kEventsCap'|translate}}</a>
- <a class="button button-small icon icon-left ion-ios-eye" ng-click="openModal(monitor.Monitor.Id, monitor.Monitor.Controllable, monitor.Monitor.ControlId, monitor.Monitor.connKey, monitor)">{{'kLiveView' | translate}}</a>
- </div>
- </div>
</div>
-
+ <p>{{'kMode' | translate}} :{{monitor.Monitor.Function}}
+ <br/> {{'kResolution' | translate}}: {{monitor.Monitor.Width}}*{{monitor.Monitor.Height}}
+ <br/> {{'kMaxFPS' | translate}}: {{monitor.Monitor.MaxFPS}}
+ <br/> {{'kAlarmMaxFPS' | translate}}:{{monitor.Monitor.AlarmMaxFPS}}
+ <br/> {{'kAlarmFrameCount' |translate}}: {{monitor.Monitor.AlarmFrameCount}}
+ <br/> {{'kStatus' | translate}}: {{monitor.Monitor.isRunningText}}
+ <br/> {{'kId' | translate}}: {{monitor.Monitor.Id}}
+ <br/> {{'kEventRecording' | translate}}: {{(monitor.Monitor.VideoWriter>0?'kVideo':'kImages') | translate}}
+ <br/>
+ <br/>
+ </p>
+ <div style="float:right;">
+ <a class="button button-small icon icon-left icon ion-gear-a" href="" ng-click="changeConfig(monitor.Monitor.Name, monitor.Monitor.Id,monitor.Monitor.Enabled,monitor.Monitor.Function);">{{'kConfiguration' | translate}}</a>
+ <a class="button button-small icon icon-left ion-calendar" href="#/events/{{monitor.Monitor.Id}}/false">{{'kEventsCap'|translate}}</a>
+ <a class="button button-small icon icon-left ion-ios-eye" ng-click="openModal(monitor.Monitor.Id, monitor.Monitor.Controllable, monitor.Monitor.ControlId, monitor.Monitor.connKey, monitor)">{{'kLiveView' | translate}}</a>
+ </div>
+ </div>
+ </div>
<ion-item ng-show="!monitors.length">
- {{'kNoMonitors' | translate}}
+ {{'kNoMonitors' | translate}}
</ion-item>
</ion-content>
</ion-view>
diff --git a/www/templates/montage-history.html b/www/templates/montage-history.html
index 94659e93..da7b5974 100644
--- a/www/templates/montage-history.html
+++ b/www/templates/montage-history.html
@@ -1,24 +1,23 @@
<ion-view view-title="{{'kEventMontage' | translate}}" cache-view="false" hide-nav-bar="{{minimal}}">
- <ion-nav-buttons side="left">
- <button class="button button-icon button-clear ion-navicon" ng-click="openMenu()"></button>
- <button class="button button-icon button-clear ion-arrow-move" ng-click="dragToggle();">&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>
- <ion-nav-buttons side="right">
- <button class="button button-icon button-clear ion-loop" ng-click="resetSizes();">&nbsp; </button>
- <button class="button button-icon button-clear ion-plus-round" ng-click="sliderChanged(1);">&nbsp; </button>
- <button class="button button-icon button-clear ion-minus-round" ng-click="sliderChanged(-1);">&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>
- <ion-content scroll-sista has-bouncing="false" style="background-color:#444444" delegate-handle="none" overflow-scroll="false" mouse-wheel-scroll>
- <div class="timeline_text"> {{'kFrom' | translate}}:{{prettifyDateTimeFirst(datetimeValueFrom.value)}} ({{humanizeTime(datetimeValueFrom.value)}})
-
- <div ng-if="$root.platformOS != 'ios'">({{'kChromeMax' | translate}})</div>
- </div>
- <div class="grid" id="mygrid">
- <div class="grid-sizer grid-item-10"></div>
- <!-- <span ng-repeat="monitor in MontageMonitors|limitTo: monLimit" ng- -->
- <span ng-repeat="monitor in MontageMonitors | onlyEnabledAndEventHas |limitTo: currentLimit">
+ <ion-nav-buttons side="left">
+ <button class="button button-icon button-clear ion-navicon" ng-click="openMenu()"></button>
+ <button class="button button-icon button-clear ion-arrow-move" ng-click="dragToggle();">&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>
+ <ion-nav-buttons side="right">
+ <button class="button button-icon button-clear ion-loop" ng-click="resetSizes();">&nbsp; </button>
+ <button class="button button-icon button-clear ion-plus-round" ng-click="sliderChanged(1);">&nbsp; </button>
+ <button class="button button-icon button-clear ion-minus-round" ng-click="sliderChanged(-1);">&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>
+ <ion-content scroll-sista has-bouncing="false" style="background-color:#444444" delegate-handle="none" overflow-scroll="false" mouse-wheel-scroll>
+ <div class="timeline_text"> {{'kFrom' | translate}}:{{prettifyDateTimeFirst(datetimeValueFrom.value)}} ({{humanizeTime(datetimeValueFrom.value)}})
+ <div ng-if="$root.platformOS != 'ios'">({{'kChromeMax' | translate}})</div>
+ </div>
+ <div class="grid" id="mygrid">
+ <div class="grid-sizer grid-item-10"></div>
+ <!-- <span ng-repeat="monitor in MontageMonitors|limitTo: monLimit" ng- -->
+ <span ng-repeat="monitor in MontageMonitors | onlyEnabledAndEventHas |limitTo: currentLimit">
<div ng-if="$root.authSession!='undefined'">
<div ng-if = "monitor.Monitor.eventUrl == 'img/noevent.png' ">
<!-- make sure we don't use id here
@@ -53,94 +52,88 @@
<i class="ion-pause"></i>&nbsp;
</span> {{monitor.Monitor.Name}}&nbsp;
-
-
- <div ng-if="sliderVal.showTimeline && $root.runMode!='lowbw'" style="white-space:nowrap;text-overflow:ellipsis;overflow:hidden;font-size:9px" class="header-event-id" id="{{monitor.Monitor.Id}}-timeline">[{{monitor.Monitor.eid}}]
-
- <i class="ion-clock"></i> {{prettifyDateTimeFirst(monitor.Monitor.eventUrlTime)}} ({{humanizeTime(monitor.Monitor.eventUrlTime)}})&nbsp;
-
- </div>
- </figcaption>
- </figure>
- <!-- wait for packery otherwise we get large ranges -->
- <div ng-show="packeryDone">
- <div class="range" style="position:absolute;top:5%;width:95%;z-index:999">
- <input on-release="seek(monitor.Monitor.Id,monitor.Monitor.sliderProgress.progress )" type="range" min="0" max="{{monitor.Monitor.eventDuration}}" ng-model="monitor.Monitor.sliderProgress.progress">
- </div>
- <div id="history_canvas_video">
- <canvas style="padding-left:23px;
+ <div ng-if="sliderVal.showTimeline && $root.runMode!='lowbw'" style="white-space:nowrap;text-overflow:ellipsis;overflow:hidden;font-size:9px" class="header-event-id" id="{{monitor.Monitor.Id}}-timeline">[{{monitor.Monitor.eid}}]
+ <i class="ion-clock"></i> {{prettifyDateTimeFirst(monitor.Monitor.eventUrlTime)}} ({{humanizeTime(monitor.Monitor.eventUrlTime)}})&nbsp;
+ </div>
+ </figcaption>
+ </figure>
+ <!-- wait for packery otherwise we get large ranges -->
+ <div ng-show="packeryDone">
+ <div class="range" style="position:absolute;top:5%;width:95%;z-index:999">
+ <input on-release="seek(monitor.Monitor.Id,monitor.Monitor.sliderProgress.progress )" type="range" min="0" max="{{monitor.Monitor.eventDuration}}" ng-model="monitor.Monitor.sliderProgress.progress">
+ </div>
+ <div id="history_canvas_video">
+ <canvas style="padding-left:23px;
padding-right:23px;z-index:998" id="eventchart-{{monitor.Monitor.Id}}" width="auto" height="20"></canvas>
- </div>
- </div>
- <div ng-if=
-"monitor.Monitor.seek" style="position:absolute;top:0px; left: 0px; width:100%;height:100%; background-color:rgba(0,0,0,0.3); z-index:99999"><div style="position:relative;top:50%;text-align:center;color:white;background-color:rgba(0,0,0,0.5);">{{'kPleaseWait' | translate}}</div></div>
- </div>
- </div>
- </div>
- <!-- valid auth session &!background -->
- <!--<div ng-if="!$root.authSession=='undefined' || isBackground()">
+ </div>
+ </div>
+ <div ng-if="monitor.Monitor.seek" style="position:absolute;top:0px; left: 0px; width:100%;height:100%; background-color:rgba(0,0,0,0.3); z-index:99999">
+ <div style="position:relative;top:50%;text-align:center;color:white;background-color:rgba(0,0,0,0.5);">{{'kPleaseWait' | translate}}</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <!-- valid auth session &!background -->
+ <!--<div ng-if="!$root.authSession=='undefined' || isBackground()">
<img image-spinner-src="img/pausevideo.png" />
</div>-->
- </span>
- </div>
- <ion-item ng-show="!MontageMonitors.length"> {{'kNoMonitors' | translate }} </ion-item>
- </ion-content>
- <div class="bwmode" ng-if="$root.runMode=='lowbw'"> {{ 'kLowBWDisplay' | translate }} </div>
- <div ng-show="minimal">
- <nav mfb-menu position="br" effect="zoomin" label="collapse" active-icon="ion-chevron-down" resting-icon="ion-chevron-up" toggling-method="click">
- <button mfb-button icon="ion-arrow-expand" label="increase size" ng-click="changeSize(-1)"></button>
- <button mfb-button icon="ion-arrow-shrink" label="decrease size" ng-click="changeSize(1)"></button>
- <button mfb-button icon="ion-refresh" label="refresh" ng-click="reloadView();"></button>
- <button mfb-button icon="ion-close" label="exit full screen" ng-click="switchMinimal()"></button>
- </nav>
- <span class="modal-alarm-badge">
+ </span>
+ </div>
+ <ion-item ng-show="!MontageMonitors.length"> {{'kNoMonitors' | translate }} </ion-item>
+ </ion-content>
+ <div class="bwmode" ng-if="$root.runMode=='lowbw'"> {{ 'kLowBWDisplay' | translate }} </div>
+ <div ng-show="minimal">
+ <nav mfb-menu position="br" effect="zoomin" label="collapse" active-icon="ion-chevron-down" resting-icon="ion-chevron-up" toggling-method="click">
+ <button mfb-button icon="ion-arrow-expand" label="increase size" ng-click="changeSize(-1)"></button>
+ <button mfb-button icon="ion-arrow-shrink" label="decrease size" ng-click="changeSize(1)"></button>
+ <button mfb-button icon="ion-refresh" label="refresh" ng-click="reloadView();"></button>
+ <button mfb-button icon="ion-close" label="exit full screen" ng-click="switchMinimal()"></button>
+ </nav>
+ <span class="modal-alarm-badge">
<a data-badge="{{$root.alarmCount}}" class="animated infinite tada button icon ion-ios-bell notification-badge button-assertive"
ng-click="handleAlarmsWhileMinimized();" ng-if="$root.isAlarm"></a>
</span>
- </div>
- <ion-pull-up-footer class="zmPullup" on-expand="footerExpand()" on-minimize="footerCollapse()" on-collapse="footerCollapse()" initial-state="minimized" default-behavior="expand">
- <ion-pull-up-handle width="100" height="25" toggle="ion-chevron-up ion-chevron-down" style="border-radius: 25px 25px 0 0">
- <i class="icon ion-chevron-up"></i>
- </ion-pull-up-handle>
- <ion-pull-up-bar>
- <h1 class="title" ion-pull-up-trigger>{{'kEventMontage' | translate}}</h1>
- </ion-pull-up-bar>
- <ion-pull-up-content scroll="true">
- <div class="list list-inset">
- <div class="item item-divider">{{'kTimeline' | translate}} ({{getLocalTZ()}})</div>
- <div class="item item-input-inset"> {{'kFrom'|translate}}:&nbsp;
-
- <div class="row">
- <div class="col col-50">
- <label class="item-input-wrapper">
- <input ng-change="hrsChanged()" type="tel" placeholder="hours" ng-model="datetimeValueFrom.hrs">
- </label>
- </div>
- <div class="col col-50">
+ </div>
+ <ion-pull-up-footer class="zmPullup" on-expand="footerExpand()" on-minimize="footerCollapse()" on-collapse="footerCollapse()" initial-state="minimized" default-behavior="expand">
+ <ion-pull-up-handle width="100" height="25" toggle="ion-chevron-up ion-chevron-down" style="border-radius: 25px 25px 0 0">
+ <i class="icon ion-chevron-up"></i>
+ </ion-pull-up-handle>
+ <ion-pull-up-bar>
+ <h1 class="title" ion-pull-up-trigger>{{'kEventMontage' | translate}}</h1>
+ </ion-pull-up-bar>
+ <ion-pull-up-content scroll="true">
+ <div class="list list-inset">
+ <div class="item item-divider">{{'kTimeline' | translate}} ({{getLocalTZ()}})</div>
+ <div class="item item-input-inset"> {{'kFrom'|translate}}:&nbsp;
+ <div class="row">
+ <div class="col col-50">
+ <label class="item-input-wrapper">
+ <input ng-change="hrsChanged()" type="tel" placeholder="hours" ng-model="datetimeValueFrom.hrs">
+ </label>
+ </div>
+ <div class="col col-50">
&nbsp;{{'kEventHistHrs' | translate}}
</div>
- </div>
- </div>
- <ion-item>
- <div ion-datetime-picker title="From" am-pm={{!loginData.use24hr}} ng-model="datetimeValueFrom.value" ng-change="dateChanged()">
- <b>{{'kFrom' | translate }}: </b>{{datetimeValueFrom.value | date: timeFormat}}
-
- </div> ({{humanizeTime(datetimeValueFrom.value)}})
-
- </ion-item>
- <div class="row">
- <div class="col col-75">
- <br/>
- <div style="width:90%;color:black;">
- <input ng-model="sliderVal.rate" type="text" id="mySlider6" slider options="slider_modal_options_rate" />
- </div>
- <br/>
- </div>
- <div class="col col-25" style="background-color:#AEA8D3;text-align:center"> {{'kSpeed' | translate }} </div>
- </div>
- <!--<ion-item><div ion-datetime-picker am-pm={{!loginData.use24hr}} ng-model="datetimeValueTo.value"><b>{{'kTo' | translate}}: </b>{{datetimeValueTo.value | date: timeFormat}}
+ </div>
+ </div>
+ <ion-item>
+ <div ion-datetime-picker title="From" am-pm={{!loginData.use24hr}} ng-model="datetimeValueFrom.value" ng-change="dateChanged()">
+ <b>{{'kFrom' | translate }}: </b>{{datetimeValueFrom.value | date: timeFormat}}
+ </div> ({{humanizeTime(datetimeValueFrom.value)}})
+ </ion-item>
+ <div class="row">
+ <div class="col col-75">
+ <br/>
+ <div style="width:90%;color:black;">
+ <input ng-model="sliderVal.rate" type="text" id="mySlider6" slider options="slider_modal_options_rate" />
+ </div>
+ <br/>
+ </div>
+ <div class="col col-25" style="background-color:#AEA8D3;text-align:center"> {{'kSpeed' | translate }} </div>
+ </div>
+ <!--<ion-item><div ion-datetime-picker am-pm={{!loginData.use24hr}} ng-model="datetimeValueTo.value"><b>{{'kTo' | translate}}: </b>{{datetimeValueTo.value | date: timeFormat}}
</div></ion-item>-->
- </div>
- </ion-pull-up-content>
- </ion-pull-up-footer>
- </ion-view> \ No newline at end of file
+ </div>
+ </ion-pull-up-content>
+ </ion-pull-up-footer>
+</ion-view>
diff --git a/www/templates/montage.html b/www/templates/montage.html
index 9d075faf..e8ff5337 100644
--- a/www/templates/montage.html
+++ b/www/templates/montage.html
@@ -1,69 +1,85 @@
<ion-view view-title="{{'kMontage' | translate}}" cache-view="false" hide-nav-bar="{{minimal}}">
-
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" ng-click="openMenu()"></button>
- <button class="button button-icon button-clear ion-eye" ng-click="hideUnhide();">&nbsp;
+ <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="toggleSubMenuFunction();">&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>
-
-
-
<ion-nav-buttons side="right">
<span ng-click="toggleTimeType()" class="icon montage-time">
<i ng-class="(iconTimeNow=='server')?'icon-server':'ion-ios-location'"></i>
{{timeNow}}&nbsp;</span>
-
<button class="button button-icon button-clear ion-arrow-move" ng-click="dragToggle();">&nbsp;
</button>
-
<!--
<button class="button button-icon button-clear ion-monitor" ng-click="cast();">&nbsp;
</button>
-->
-
<button class="button button-icon button-clear ion-loop" ng-click="resetSizes();">&nbsp;
</button>
-
<button class="button button-icon button-clear ion-android-contract" ng-click="switchMinimal()">
</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">
+ <ion-content scroll-sista ng-cloak has-bouncing="false" style="background-color:#444444" delegate-handle="montage-delegate" overflow-scroll="false">
+ <div ng-if="!minimal && toggleSubMenu" >
<!-- 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>
+
+ <li ng-if="isDragabillyOn">
+ <a href="" ng-click="hideMonitor(monitor.Monitor.Id)"> <i class="ion-close-circled"></i></a>
+ </li>
+ <li ng-if="isDragabillyOn">
+ <a href="" ng-click="toggleStamp()"> <i class="ion-pin"></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>
- <br/><br/>
- </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/>
+ </div>
<!-- now lets draw the montage windows -->
<div class="grid" id="mygrid">
<div class="grid-sizer grid-item-10"></div>
-
-
-
- <!-- <span ng-repeat="monitor in MontageMonitors|limitTo: monLimit"
+ <!-- <span ng-repeat="monitor in MontageMonitors|limitTo: monLimit"
ng-if="monitor.Monitor.Function!='None' && monitor.Monitor.Enabled !='0' ">-->
-
- <span ng-repeat="monitor in MontageMonitors | onlyEnabled |limitTo: monLimit" >
+ <span ng-repeat="monitor in MontageMonitors | onlyEnabled |limitTo: monLimit">
@@ -80,12 +96,14 @@
- <img class="{{monitor.Monitor.selectStyle}}" id="img-{{$index}}" image-spinner-src="{{monitor.Monitor.streamingURL}}/nph-zms?mode=single&monitor={{monitor.Monitor.Id}}&scale={{LoginData.montageQuality}}{{$root.authSession}}&rand={{$root.rand}}" ng-click="!isDragabillyOn?openModal(monitor.Monitor.Id, monitor.Monitor.Controllable, monitor.Monitor.ControlId, monitor.Monitor.connKey,monitor):toggleSelectItem($index);" image-spinner-loader="lines" />
+ <img class="{{monitor.Monitor.selectStyle}}" id="img-{{$index}}" image-spinner-src="{{monitor.Monitor.streamingURL}}/nph-zms?mode=single&monitor={{monitor.Monitor.Id}}&scale={{LoginData.montageQuality}}{{$root.authSession}}&rand={{$root.rand}}" ng-click="!isDragabillyOn?openModal(monitor.Monitor.Id, monitor.Monitor.Controllable, monitor.Monitor.ControlId, monitor.Monitor.connKey,monitor):toggleSelectItem(monitor.Monitor.Id);" image-spinner-loader="lines" />
+
+
</div>
<div ng-if = "minimal">
- <img id="img-{{$index}}" image-spinner-src="{{monitor.Monitor.streamingURL}}/zms?mode=single&monitor={{monitor.Monitor.Id}}&scale={{LoginData.montageQuality}}{{$root.authSession}}&rand={{$root.rand}}" ng-click="!isDragabillyOn?openModal(monitor.Monitor.Id, monitor.Monitor.Controllable, monitor.Monitor.ControlId, monitor.Monitor.connKey,monitor):toggleSelectItem($index);" image-spinner-loader="lines" />
+ <img id="img-{{$index}}" image-spinner-src="{{monitor.Monitor.streamingURL}}/zms?mode=single&monitor={{monitor.Monitor.Id}}&scale={{LoginData.montageQuality}}{{$root.authSession}}&rand={{$root.rand}}" ng-click="!isDragabillyOn?openModal(monitor.Monitor.Id, monitor.Monitor.Controllable, monitor.Monitor.ControlId, monitor.Monitor.connKey,monitor):toggleSelectItem(monitor.Monitor.Id);" image-spinner-loader="lines" />
</div>
</div>
@@ -99,9 +117,9 @@
- <figcaption id="slowpulse" ng-class="monitor.Monitor.isAlarmed==true?'alarmed-figcaption animated infinite flash':'normal-figcaption'" >
+ <figcaption id="slowpulse" ng-class="monitor.Monitor.isAlarmed==true?'alarmed-figcaption animated infinite flash':'normal-figcaption'" >&nbsp;
- &nbsp;<i class="ion-ios-videocam"></i>
+ <span ng-if="monitor.Monitor.isStamp && isDragabillyOn"><i class="animated infinite flash ion-pin"></i>&nbsp;</span><i class="ion-ios-videocam"></i>&nbsp;
{{monitor.Monitor.Name}}&nbsp;<i ng-if="$root.runMode!='lowbw'" style="{{monitor.Monitor.alarmState}}" class="ion-record"></i>&nbsp;
</figcaption>
@@ -117,30 +135,18 @@
</figure>
</div>
- </span> <!-- ngrepeat -->
+ </span>
+ <!-- ngrepeat -->
</div>
-
-
-
-
-
-
<ion-item style="background-color:#444444; color:#fff;border:none;" ng-show="!MontageMonitors.length">
{{'kNoMonitors' | translate}}
</ion-item>
-
-
-
</ion-content>
-
<div class="bwmode" ng-if="$root.runMode=='lowbw'">
- {{ 'kLowBWDisplay' | translate }}
- </div>
-
-
+ {{ 'kLowBWDisplay' | translate }}
+ </div>
<div ng-show="minimal">
<nav mfb-menu position="br" effect="zoomin" label="{{'kCollapse' | translate}}" active-icon="ion-chevron-down" resting-icon="ion-chevron-up" toggling-method="click">
-
<button mfb-button icon="ion-arrow-expand" label="{{'kIncreaseSize' | translate}}" ng-click="sliderChanged(1)">
</button>
<button mfb-button icon="ion-arrow-shrink" label="{{'kDecreaseSize' | translate}}" ng-click="sliderChanged(-1)">
@@ -150,14 +156,10 @@
<button mfb-button icon="ion-close" label="{{'kExitFullScreen'| translate}}" ng-click="switchMinimal()">
</button>
</nav>
-
-
<span class="modal-alarm-badge">
<a data-badge="{{$root.alarmCount}}" class="animated infinite tada button icon ion-ios-bell notification-badge button-assertive"
ng-click="handleAlarmsWhileMinimized();" ng-if="$root.isAlarm"></a>
</span>
-
</div>
<br/>
-
-</ion-view> \ No newline at end of file
+</ion-view>
diff --git a/www/templates/news.html b/www/templates/news.html
index f4a87e4f..84e3c2b3 100644
--- a/www/templates/news.html
+++ b/www/templates/news.html
@@ -1,17 +1,13 @@
<ion-view view-title="{{'kNews' | translate}}">
-
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" ng-click="openMenu()"></button>
-
<button data-badge="{{$root.alarmCount}}" class="animated infinite tada button button-icon button-clear ion-ios-bell notification-badge" ng-click="handleAlarms();" ng-if="$root.isAlarm"></button>
-
</ion-nav-buttons>
-
-
- <ion-content scroll-sista delegate-handle="none" overflow-scroll="false" mouse-wheel-scroll >
+ <ion-content scroll-sista delegate-handle="none" overflow-scroll="false" mouse-wheel-scroll>
+
<div class="list">
-
- <span ng-repeat = "post in newsItems">
+
+ <span ng-repeat="post in newsItems">
<a class="item item-text-wrap item-icon-left" ng-click="loadPost(post.url, post.date)" href="">
<i ng-class="isUnread(post.date) ? 'icon ion-email-unread': 'icon ion-ios-email-outline'"></i>
{{post.title}}
@@ -20,14 +16,9 @@
</a>
</span>
-
<div ng-if="!newsItems.length">
<ion-item>{{'kLoading' | translate}}...</ion-item>
</div>
-
-
</div>
-
</ion-content>
-
</ion-view>
diff --git a/www/templates/reorder-modal.html b/www/templates/reorder-modal.html
index f58d567c..397d4be7 100644
--- a/www/templates/reorder-modal.html
+++ b/www/templates/reorder-modal.html
@@ -1,39 +1,20 @@
-
- <ion-modal-view cache-view="false" style="width: 90%; height: 90%; top: 5%; left: 5%; right: 5%; bottom: 5%;" >
-
+<ion-modal-view cache-view="false" style="width: 90%; height: 90%; top: 5%; left: 5%; right: 5%; bottom: 5%;">
<ion-header-bar class="bar-stable">
-
-
- <h1 class="title"></h1>
- <div class="buttons">
-
-
- <button class="button button-icon icon ion-checkmark" ng-click="saveReorder()"></button>
-
- <button class="button button-icon icon ion-close" ng-click="cancelReorder()"></button>
-
- </div>
+ <h1 class="title"></h1>
+ <div class="buttons">
+ <button class="button button-icon icon ion-checkmark" ng-click="saveReorder()"></button>
+ <button class="button button-icon icon ion-close" ng-click="cancelReorder()"></button>
+ </div>
</ion-header-bar>
-
-
- <ion-content >
- <div class="list">
- <span ng-repeat="item in copyMontage">
+ <ion-content>
+ <div class="list">
+ <span ng-repeat="item in copyMontage">
<a class="item item-icon-left" ng-click="toggleHide($index)" href="">
<i ng-class="{'icon ion-eye':item.Monitor.listDisplay=='show','icon ion-eye-disabled':item.Monitor.listDisplay!='show'}"> </i>
{{item.Monitor.Name}}
</a>
</span>
- </div>
-
-
- </ion-content>
-
- </ion-modal-view>
-
-
-
-
-
-
+ </div>
+ </ion-content>
+</ion-modal-view>
diff --git a/www/templates/state.html b/www/templates/state.html
index bf0cda4a..33df994e 100644
--- a/www/templates/state.html
+++ b/www/templates/state.html
@@ -1,12 +1,10 @@
<ion-view view-title="{{'kSystemStatus' | translate}}" cache-view="false">
-
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" ng-click="openMenu()"></button>
<button data-badge="{{$root.alarmCount}}" class="animated infinite tada button button-icon button-clear ion-ios-bell notification-badge" ng-click="handleAlarms();" ng-if="$root.isAlarm"></button>
</ion-nav-buttons>
- <ion-content scroll-sista delegate-handle="none" overflow-scroll="false" mouse-wheel-scroll>
- <ion-refresher pulling-text="{{'kPullToReload' | translate}}..."
- spinner="bubbles" on-refresh="doRefresh()"></ion-refresher>
+ <ion-content scroll-sista delegate-handle="none" overflow-scroll="false" mouse-wheel-scroll>
+ <ion-refresher pulling-text="{{'kPullToReload' | translate}}..." spinner="bubbles" on-refresh="doRefresh()"></ion-refresher>
<ion-list>
<ion-item>
<div class="row">
@@ -28,7 +26,6 @@
<i class="ion-arrow-graph-up-right" style="font-size:150%;"></i>
</div>
<div class="col">
-
ZoneMinder {{'kLoad' | translate}}:
</div>
<div class="col" style="text-align:right;">
@@ -54,20 +51,15 @@
</ion-item>
-->
<ion-item>
- <button class="button button-full {{dangerButtonColor[showDanger?1:0]}}"
- ng-click="showDanger=!showDanger">
+ <button class="button button-full {{dangerButtonColor[showDanger?1:0]}}" ng-click="showDanger=!showDanger">
<i class="ion-alert-circled" "style:font-size:300%;"></i> {{dangerText[showDanger?1:0]}}
</button>
-
- <div ng-show="showDanger" >
+ <div ng-show="showDanger">
<div class="row">
<div class="col text-center">
- <a class="button button-small button-outline button-dark "
- ng-click="selectCustomState();" href="">{{'kChangeState'|translate}}</a>
- <a class="button button-small button-outline button-dark "
- ng-click="controlZM('restart');" href="">{{'kRestart' | translate}}</a>
- <a class="button button-small button-outline button-dark"
- href="" ng-click="controlZM('stop');">{{'kStop' | translate}}</a>
+ <a class="button button-small button-outline button-dark " ng-click="selectCustomState();" href="">{{'kChangeState'|translate}}</a>
+ <a class="button button-small button-outline button-dark " ng-click="controlZM('restart');" href="">{{'kRestart' | translate}}</a>
+ <a class="button button-small button-outline button-dark" href="" ng-click="controlZM('stop');">{{'kStop' | translate}}</a>
<a class="button button-small button-outline button-dark" ng-click="controlZM('start');" href="">{{'kStart' | translate}}</a>
</div>
</div>
diff --git a/www/templates/timeline-modal.html b/www/templates/timeline-modal.html
index b27c749b..afed79f5 100644
--- a/www/templates/timeline-modal.html
+++ b/www/templates/timeline-modal.html
@@ -1,36 +1,26 @@
-
-
- <!-- style="width: 90%; height: 90%; top: 5%; left: 5%; right: 5%; bottom: 5%;"-->
- <ion-modal-view cache-view="false" >
-
- <ion-content ng-cloak on-double-tap="closeModal()" delegate-handle="timeline-modal-delegate">
-<div ng-controller="TimelineModalCtrl" >
+<!-- style="width: 90%; height: 90%; top: 5%; left: 5%; right: 5%; bottom: 5%;"-->
+<ion-modal-view cache-view="false">
+ <ion-content ng-cloak on-double-tap="closeModal()" delegate-handle="timeline-modal-delegate">
+ <div ng-controller="TimelineModalCtrl">
<br/>
<div class="item item-divider">{{mName}}&nbsp;<i class="ion-arrow-right-b"></i>&nbsp;{{'kEvent' | translate}}:{{eid}} ({{humanizeTime}})
- <button class="button icon-left button-small button-positive" style="float:right; opacity:0.7" ng-click="switchType()">
- <i class="ion-shuffle"></i>
+ <button class="button icon-left button-small button-positive" style="float:right; opacity:0.7" ng-click="switchType()">
+ <i class="ion-shuffle"></i>
</button>
-
</div>
-
- <center> <h5>{{'kEvent' | translate}} {{graphType}} {{'kFrames' | translate}} </h5>
+ <center>
+ <h5>{{'kEvent' | translate}} {{graphType}} {{'kFrames' | translate}} </h5>
<p>{{'kTimelineMessage' | translate}}</p>
-
-
- {{errorDetails}}
+ {{errorDetails}}
<!--<p>scroll left/right if needed</p>-->
- </center>
-
- <div data-tap-disabled="true">
- <canvas id="tcchart" width="auto" height="70%"></canvas>
- </div>
-
-
- <ion-spinner icon="spiral" style="position:absolute; top:50%;left:50%" ng-if="!dataReady"></ion-spinner>
-
+ </center>
+ <div data-tap-disabled="true">
+ <canvas id="tcchart" width="auto" height="70%"></canvas>
+ </div>
+ <ion-spinner icon="spiral" style="position:absolute; top:50%;left:50%" ng-if="!dataReady"></ion-spinner>
<div style="height:190px;">
<!-- <ion-scroll direction="x" class="wide-as-needed">-->
- <span ng-repeat="alarm in alarm_images">
+ <span ng-repeat="alarm in alarm_images">
<figure style="display:inline-block">
@@ -44,14 +34,8 @@
</figure>
</span>
-
<!--</ion-scroll>-->
</div>
- </div>
- </ion-content>
-
- </ion-modal-view>
-
-
-
-
+ </div>
+ </ion-content>
+</ion-modal-view>
diff --git a/www/templates/timeline-popover.html b/www/templates/timeline-popover.html
index b5460f57..beeb482d 100644
--- a/www/templates/timeline-popover.html
+++ b/www/templates/timeline-popover.html
@@ -1,26 +1,18 @@
<ion-popover-view class="fit">
- <ion-content>
- <div class="list" ng-click="popover.hide()">
-
+ <ion-content>
+ <div class="list" ng-click="popover.hide()">
<a class="item" ng-href="" ng-click="popover.hide();toggleMinAlarmFrameCount();" ng-if="loginData.enableAlarmCount">{{'kShowAllEvents' | translate}}</a>
- <a class="item" ng-href="" ng-click="popover.hide();toggleMinAlarmFrameCount();" ng-if="!loginData.enableAlarmCount"> {{'kShowAlarmedEvents' | translate}}</a>
-
-
+ <a class="item" ng-href="" ng-click="popover.hide();toggleMinAlarmFrameCount();" ng-if="!loginData.enableAlarmCount"> {{'kShowAlarmedEvents' | translate}}</a>
<a class="item" ng-href="" ng-click="popover.hide();buttonClicked(0);">{{'kMonth' | translate}}</a>
-
- <a class="item" ng-href="" ng-click=" popover.hide();buttonClicked(1);" >
+ <a class="item" ng-href="" ng-click=" popover.hide();buttonClicked(1);">
{{'kWeek' | translate}}
</a>
<a class="item" ng-href="" ng-click="popover.hide();buttonClicked(2);">
{{'kDay' | translate}}
</a>
-
- <a class="item" ng-href="" ng-click="popover.hide();buttonClicked(3);">
+ <a class="item" ng-href="" ng-click="popover.hide();buttonClicked(3);">
{{'kCustomRange' | translate}}
</a>
-
-
- </div>
- </ion-content>
- </ion-popover-view>
-
+ </div>
+ </ion-content>
+</ion-popover-view>
diff --git a/www/templates/timeline.html b/www/templates/timeline.html
index ab9d8030..d2ada69d 100644
--- a/www/templates/timeline.html
+++ b/www/templates/timeline.html
@@ -37,6 +37,9 @@
<button ng-click="toggleFollowTime()" ng-class="follow.time? 'button button-small button-balanced':'button button-small button-assertive'">
{{'kUpdateTimeline' | translate}}:&nbsp;{{follow.time}}
</button>
+ <button ng-click="gotoNow()" class="button button-small button-balanced">
+ {{'kNow' | translate}}
+ </button>
<div ng-if="follow.time">
<p>{{newEvents}}</p>
</div>
diff --git a/www/templates/wizard.html b/www/templates/wizard.html
index 3d20c522..600f4a7f 100644
--- a/www/templates/wizard.html
+++ b/www/templates/wizard.html
@@ -1,115 +1,85 @@
-<ion-view view-title="{{'kWizard' | translate}}" ng-cloak cache-view="false" >
-
+<ion-view view-title="{{'kWizard' | translate}}" ng-cloak cache-view="false">
<ion-nav-buttons side="left">
<button class="button button-icon button-clear ion-navicon" ng-click="openMenu()"></button>
</ion-nav-buttons>
-
-
<ion-content class="padding" overflow-scroll="false">
-
- <wizard on-finish="finishedWizard()">
-
- <!-- portal url -->
- <wz-step wz-title="1" canexit="exitPortal">
-
- <h4><i id="transition-delay" class="animated swing ion-wand" style="font-size:2em"></i> {{'kWelcomeWizard' | translate}}</h4>
- <img src="img/wizard.svg" width="100px" style="float:left"/>
- <p>{{'kWizConfigPain' | translate}}</p>
-
- <h4>{{'kWizPortalUrl' | translate}}</h4>
- <label class="item item-input">
- <input autocorrect="off" autocapitalize="none" autocomplete="off" type="text" placeholder="e.g. http://server/zm" ng-model="wizard.portalurl">
- </label>
-
- <a class="button icon-left ion-information-circled button-clear button-dark" ng-click="toggleTip()">{{wizard.tiptext}}</a>
-
-
- <div class="wizardtip" ng-show="wizard.tipshow">
- <b>{{'kWizTip' | translate}}: </b>{{'kWizPortalTip' | translate}}<br/>
-
- <img src="img/portalurl.png" width="30%">
- </div>
-
- <br/>
- <button class="button button-small icon icon-right ion-chevron-right" wz-next>{{'kNext' | translate}}</button>
-
-
- </wz-step>
-
-
- <!-- auth mode -->
- <wz-step wz-title="2" >
- <h4>{{'kWizPortalAuth' | translate}}</h4>
- <!--<img src="img/wizard.svg" width="100px" style="float:left"/>-->
- <p>{{'kWizPortalText' | translate}}</p>
- <ion-toggle ng-change="toggleAuth()" ng-model="wizard.useauth" ng-checked="wizard.useauth" toggle-class="toggle-calm">{{'kWizUseAuth' | translate}}</ion-toggle>
-
- <ion-toggle ng-show="wizard.useauth" ng-model="wizard.usezmauth" ng-checked="wizard.usezmauth" toggle-class="toggle-calm">{{'kWizZMAuth' | translate}}</ion-toggle>
-
- <div ng-if="wizard.usezmauth">
- <label class="item item-input item-floating-label" >
+ <wizard on-finish="finishedWizard()">
+ <!-- portal url -->
+ <wz-step wz-title="1" canexit="exitPortal">
+ <h4><i id="transition-delay" class="animated swing ion-wand" style="font-size:2em"></i> {{'kWelcomeWizard' | translate}}</h4>
+ <img src="img/wizard.svg" width="100px" style="float:left" />
+ <p>{{'kWizConfigPain' | translate}}</p>
+ <h4>{{'kWizPortalUrl' | translate}}</h4>
+ <label class="item item-input">
+ <input autocorrect="off" autocapitalize="none" autocomplete="off" type="text" placeholder="e.g. http://server/zm" ng-model="wizard.portalurl">
+ </label>
+ <p ng-if="$root.platformOS=='android'" style="font-size:0.8em; color:gray">{{'kDisableSamsung' | translate}}</p>
+ <a class="button icon-left ion-information-circled button-clear button-dark" ng-click="toggleTip()">{{wizard.tiptext}}</a>
+ <div class="wizardtip" ng-show="wizard.tipshow">
+ <b>{{'kWizTip' | translate}}: </b>{{'kWizPortalTip' | translate}}
+ <br/>
+ <img src="img/portalurl.png" width="30%">
+ </div>
+ <br/>
+ <button class="button button-small icon icon-right ion-chevron-right" wz-next>{{'kNext' | translate}}</button>
+ </wz-step>
+ <!-- auth mode -->
+ <wz-step wz-title="2">
+ <h4>{{'kWizPortalAuth' | translate}}</h4>
+ <!--<img src="img/wizard.svg" width="100px" style="float:left"/>-->
+ <p>{{'kWizPortalText' | translate}}</p>
+ <ion-toggle ng-change="toggleAuth()" ng-model="wizard.useauth" ng-checked="wizard.useauth" toggle-class="toggle-calm">{{'kWizUseAuth' | translate}}</ion-toggle>
+ <ion-toggle ng-show="wizard.useauth" ng-model="wizard.usezmauth" ng-checked="wizard.usezmauth" toggle-class="toggle-calm">{{'kWizZMAuth' | translate}}</ion-toggle>
+ <div ng-if="wizard.usezmauth">
+ <label class="item item-input item-floating-label">
+ <span class="input-label">{{'kUserName' | translate}}</span>
+ <input autocorrect="off" autocapitalize="none" autocomplete="off" type="text" ng-model="wizard.zmuser" placeholder="{{'kPlaceHolderZMAuthUser'|translate}}">
+ </label>
+ <label class="item item-text-wrap item-input item-floating-label">
+ <span class="input-label">{{'kPassword' | translate}}</span>
+ <input type="password" ng-model="wizard.zmpassword" placeholder="{{'kPlaceHolderZMAuthPass'|translate}}">
+ <!--<p >{{'kWizPasswdNote' | translate}}</p>-->
+ </label>
+ </div>
+ <ion-toggle ng-show="wizard.useauth" ng-model="wizard.usebasicauth" ng-checked="wizard.usebasicauth" toggle-class="toggle-calm">{{'kWizBasicAuth' | translate}}</ion-toggle>
+ <label class="item item-input item-floating-label" ng-show="wizard.usebasicauth">
<span class="input-label">{{'kUserName' | translate}}</span>
- <input autocorrect="off" autocapitalize="none" autocomplete="off" type="text" ng-model="wizard.zmuser" placeholder="{{'kPlaceHolderZMAuthUser'|translate}}" >
+ <input autocorrect="off" autocapitalize="none" autocomplete="off" type="text" ng-model="wizard.basicuser" placeholder="{{'kPlaceHolderBasicAuthUser'|translate}}">
</label>
- <label class="item item-text-wrap item-input item-floating-label" >
+ <label class="item item-input item-text-wrap item-floating-label" ng-show="wizard.usebasicauth">
<span class="input-label">{{'kPassword' | translate}}</span>
- <input type="password" ng-model="wizard.zmpassword" placeholder="{{'kPlaceHolderZMAuthPass'|translate}}" >
- <!--<p >{{'kWizPasswdNote' | translate}}</p>-->
+ <input type="password" ng-model="wizard.basicpassword" placeholder="{{'kPlaceHolderBasicAuthPass'|translate}}">
+ <p>{{'kWizPasswdNote' | translate}}</p>
</label>
- </div>
-
- <ion-toggle ng-show="wizard.useauth" ng-model="wizard.usebasicauth" ng-checked="wizard.usebasicauth" toggle-class="toggle-calm">{{'kWizBasicAuth' | translate}}</ion-toggle>
- <label class="item item-input item-floating-label" ng-show="wizard.usebasicauth">
- <span class="input-label">{{'kUserName' | translate}}</span>
- <input autocorrect="off" autocapitalize="none" autocomplete="off" type="text" ng-model="wizard.basicuser" placeholder="{{'kPlaceHolderBasicAuthUser'|translate}}" >
- </label>
- <label class="item item-input item-text-wrap item-floating-label" ng-show="wizard.usebasicauth">
- <span class="input-label">{{'kPassword' | translate}}</span>
- <input type="password" ng-model="wizard.basicpassword" placeholder="{{'kPlaceHolderBasicAuthPass'|translate}}" >
- <p>{{'kWizPasswdNote' | translate}}</p>
- </label>
-
-
- <a class="button icon-left ion-information-circled button-clear button-dark" ng-click="toggleTip()">{{wizard.tiptext}}</a>
-
-
- <div class="wizardtip" ng-show="wizard.tipshow">
- <b>{{'kWizTip'| translate}} </b> {{'kWizAuthText1' | translate}}<br/>
- {{'kWizAuthText2' | translate}}
- </div>
-
- <br/>
- <button class="button button-small icon icon-left ion-chevron-left" wz-previous>{{'kPrev' | translate}}</button>
- <button class="button button-small icon icon-right ion-chevron-right" ng-click="exitAuth()">{{'kNext' | translate}}</button>
-
- </wz-step>
-
-
- <wz-step wz-title="3" >
- <br/><br/>
- <h4>{{'kWizResults' | translate}}</h4>
-
- <span ng-if="wizard.portalValidText" style="color:{{wizard.portalColor}};"><i ng-class="wizard.portalColor=='#16a085' ? 'ion-checkmark-circled':'ion-close-circled'"></i>&nbsp;{{wizard.portalValidText}}<br/></span>
-
- <span ng-if="wizard.apiValidText" style="color:{{wizard.apiColor}};"><i ng-class=" wizard.apiColor=='#16a085' ? 'ion-checkmark-circled':'ion-close-circled'"></i>&nbsp;{{wizard.apiValidText}}<br/></span>
-
- <span ng-if="wizard.streamingValidText" style="color:{{wizard.streamingColor}};"><i ng-class="wizard.streamingColor=='#16a085' ? 'ion-checkmark-circled':'ion-close-circled'"></i>&nbsp;{{wizard.streamingValidText}}<br/></span>
-
- <br/>
- <div class="wizardtip">
- {{'kWizNextStep1' | translate}}:
- <ul class="wiz-list">
- <li>{{'kWizNextStep2' | translate}}</li>
- <li>{{'kWizNextStep3' | translate}}</li>
- </ul>
- </div>
- <br/><br/>
- <button class="button button-small icon icon-left ion-chevron-left" wz-previous>{{'kPrev' | translate}}</button>
- <button class="button button-small icon icon-right ion-chevron-right" ng-click="gotoLoginState()">{{'kWizGotoLogin' | translate}}</button>
- </wz-step>
-
-
-</wizard>
+ <a class="button icon-left ion-information-circled button-clear button-dark" ng-click="toggleTip()">{{wizard.tiptext}}</a>
+ <div class="wizardtip" ng-show="wizard.tipshow">
+ <b>{{'kWizTip'| translate}} </b> {{'kWizAuthText1' | translate}}
+ <br/> {{'kWizAuthText2' | translate}}
+ </div>
+ <br/>
+ <button class="button button-small icon icon-left ion-chevron-left" wz-previous>{{'kPrev' | translate}}</button>
+ <button class="button button-small icon icon-right ion-chevron-right" ng-click="exitAuth()">{{'kNext' | translate}}</button>
+ </wz-step>
+ <wz-step wz-title="3">
+ <br/>
+ <br/>
+ <h4>{{'kWizResults' | translate}}</h4>
+ <span ng-if="wizard.portalValidText" style="color:{{wizard.portalColor}};"><i ng-class="wizard.portalColor=='#16a085' ? 'ion-checkmark-circled':'ion-close-circled'"></i>&nbsp;{{wizard.portalValidText}}<br/></span>
+ <span ng-if="wizard.apiValidText" style="color:{{wizard.apiColor}};"><i ng-class=" wizard.apiColor=='#16a085' ? 'ion-checkmark-circled':'ion-close-circled'"></i>&nbsp;{{wizard.apiValidText}}<br/></span>
+ <span ng-if="wizard.streamingValidText" style="color:{{wizard.streamingColor}};"><i ng-class="wizard.streamingColor=='#16a085' ? 'ion-checkmark-circled':'ion-close-circled'"></i>&nbsp;{{wizard.streamingValidText}}<br/></span>
+ <br/>
+ <div class="wizardtip">
+ {{'kWizNextStep1' | translate}}:
+ <ul class="wiz-list">
+ <li>{{'kWizNextStep2' | translate}}</li>
+ <li>{{'kWizNextStep3' | translate}}</li>
+ </ul>
+ </div>
+ <br/>
+ <br/>
+ <button class="button button-small icon icon-left ion-chevron-left" wz-previous>{{'kPrev' | translate}}</button>
+ <button class="button button-small icon icon-right ion-chevron-right" ng-click="gotoLoginState()">{{'kWizGotoLogin' | translate}}</button>
+ </wz-step>
+ </wizard>
</ion-content>
-
-</ion-view> \ No newline at end of file
+</ion-view>
diff --git a/www/templates/zm-portal-login.html b/www/templates/zm-portal-login.html
index 9d2db867..877dbb97 100644
--- a/www/templates/zm-portal-login.html
+++ b/www/templates/zm-portal-login.html
@@ -1,8 +1,5 @@
-
<ion-view view-title="{{$root.appName}}" hide-nav-bar="true" hide-back-button="true" cache-view="false">
-
<ion-content class="pin-background" scroll="false">
-
<div style="margin-left:20px; margin-right:20px">
<center>
<br/>
@@ -10,23 +7,20 @@
<div id="responsive-image">
<img src="img/authlogo.png">
</div>
-
<div ng-if="pinPrompt">
<span style="color:white">{{'kEnterPin' | translate}}</span>
-
<div class="pinCode">
<input id="pin-box" type="number" pattern="[0-9]*" ng-model="pindata.pin" ng-keyup="pinChange()" />
</div>
-
<br/> <span style="color:white">{{pindata.status}}</span>
<br/>
<button class="button button-dark icon ion-unlocked" ng-click="unlock()"> Unlock
</button>
</div>
- <div id = "really-your-fault" class="animated fadeIn" style="color:white">{{'kPortalNotice' | translate}}<br/><br/>{{'kPortalNoticeSub' | translate}}</div>
-
+ <div id="really-your-fault" class="animated fadeIn" style="color:white">{{'kPortalNotice' | translate}}
+ <br/>
+ <br/>{{'kPortalNoticeSub' | translate}}</div>
</center>
</div>
-
</ion-content>
-</ion-view> \ No newline at end of file
+</ion-view>