]> www.vanbest.org Git - motioneye-debian/commitdiff
added camera frame js interaction code
authorCalin Crisan <ccrisan@gmail.com>
Sun, 29 Sep 2013 11:59:58 +0000 (14:59 +0300)
committerCalin Crisan <ccrisan@gmail.com>
Sun, 29 Sep 2013 11:59:58 +0000 (14:59 +0300)
motioneye.py
src/handlers.py
static/css/main.css
static/js/main.js
templates/macros.html [deleted file]
templates/main.html

index bd5e1ed13c765825c8407579019cb4f514b8411c..ad239fbfd4b05b0e31f25620041375074c437d3d 100644 (file)
@@ -45,7 +45,7 @@ if __name__ == '__main__':
     _configure_logging()
     _start_server()
     
-#     import config
+#     import config # TODO remove this
 #     main_config = config.get_main()
 #     config.add_camera('v4l2:///dev/video0')
 #     #data = config.get_camera(1)
index 2453173f34f05762e79e5f75f5ca80ecd74696a7..79768e6f6b907c47beed7cb35e56cf21f2352499 100644 (file)
@@ -115,7 +115,8 @@ class ConfigHandler(BaseHandler):
         cameras = []
         for camera_id in config.get_camera_ids():
             data = config.get_camera(camera_id)
-            data['@id'] = camera_id
+            data = self._camera_dict_to_ui(data)
+            data['id'] = camera_id
             cameras.append(data)
 
         self.finish_json({'cameras': cameras})
index a4994e20cf32fec81e7311c6b599163fdf3452bc..c4d776847326546ee1243208c223f3e4b8e28244 100644 (file)
 }
 
  
+    /* links */
+    
+a {
+    color: inherit;
+    text-decoration: inherit;
+}
+
+
     /* layout */
 
 html {
@@ -64,7 +72,7 @@ div.footer {
     bottom: 0px;
     width: 100%;
     height: 20px;
-    font-size: 0.5em;
+    font-size: 0.7em;
     color: #aaa;
     text-align: center;
 }
@@ -79,6 +87,36 @@ div.page-container.stretched {
 }
 
 
+    /* icons */
+
+img.settings-button {
+    margin: 2px;
+    cursor: pointer;
+    vertical-align: middle;
+}
+
+div.logo {
+    float: right;
+    display: inline-block;
+    white-space: nowrap;
+    opacity: 0.86;
+}
+
+span.logo {
+    vertical-align: middle;
+    font-size: 27px;
+    font-weight: bold;
+    position: relative;
+    top: 3px;
+}
+
+img.logo {
+    width: 48px;
+    height: 48px;
+    vertical-align: middle;
+}
+
+
     /* settings */
 
 div.settings {
@@ -185,6 +223,7 @@ div.apply-button {
     background-color: #FF6F00;
     border-radius: 3px;
     transition: all 0.1s linear;
+    transition: opacity 0s;
 }
 
 div.apply-button:HOVER {
@@ -227,11 +266,11 @@ input[type=text].working-schedule.number {
 
     /* camera frames */
 
-div.video-list {
+div.camera-list {
     text-align: center;
 }
 
-div.video-frame {
+div.camera-frame {
     width: 32%;
     text-align: left;
     background-color: #313131;
@@ -239,28 +278,30 @@ div.video-frame {
     padding: 0px 5px 5px 5px;
     border-radius: 3px;
     transition: all 0.2s;
-    margin-top: 3px;
+    transition: opacity 0s;
+    margin: 2px;
+    opacity: 0;
 }
 
-div.video-frame:HOVER {
+div.camera-frame:HOVER {
     background-color: #414141;
 }
 
-div.video-top-bar {
+div.camera-top-bar {
     padding: 3px 0px;
     font-size: 17px;
     height: 17px;
 }
 
-span.video-name {
+span.camera-name {
     float: left;
 }
 
-div.video-buttons {
+div.camera-buttons {
     float: right;
 }
 
-div.video-button {
+div.camera-button {
     display: inline-block;
     width: 16px;
     height: 16px;
@@ -271,27 +312,27 @@ div.video-button {
     transition: all 0.1s linear;
 }
 
-div.video-button:HOVER {
+div.camera-button:HOVER {
     opacity: 1;
 }
 
-div.video-button:ACTIVE {
+div.camera-button:ACTIVE {
     opacity: 0.5;
 }
 
-div.video-button.close {
+div.camera-button.close {
     background-position: 0px 0px;
 }
 
-div.video-button.configure {
+div.camera-button.configure {
     background-position: -16px 0px;
 }
 
-div.video-container {
+div.camera-container {
     padding: 0px;
 }
 
-img.video {
+img.camera {
     width: 100%;
     display: block;
 }
@@ -343,41 +384,3 @@ img.video {
         width: 98%;
     }
 }
-
-
-    /* links */
-    
-a {
-    color: inherit;
-    text-decoration: inherit;
-}
-
-
-    /* icons */
-
-img.settings-button {
-    margin: 2px;
-    cursor: pointer;
-    vertical-align: middle;
-}
-
-div.logo {
-    float: right;
-    display: inline-block;
-    white-space: nowrap;
-    opacity: 0.86;
-}
-
-span.logo {
-    vertical-align: middle;
-    font-size: 27px;
-    font-weight: bold;
-    position: relative;
-    top: 3px;
-}
-
-img.logo {
-    width: 48px;
-    height: 48px;
-    vertical-align: middle;
-}
index bd3ab378da3c3ac87d9a5ab9bbf65346fc3dc6f7..f7416b3122b0025c7208f0f63f642ee63c66df05 100644 (file)
@@ -65,6 +65,53 @@ Object.keys = Object.keys || (function () {
     };
 })();
 
+Array.prototype.indexOf = Array.prototype.indexOf || function (obj) {
+    for (var i = 0; i < this.length; i++) {
+        if (this[i] === obj) {
+            return i;
+        }
+    }
+    
+    return -1;
+};
+
+Array.prototype.forEach = Array.prototype.forEach || function (callback, thisArg) {
+    for (var i = 0; i < this.length; i++) {
+        callback.call(thisArg, this[i], i, this);
+    }
+};
+
+Array.prototype.unique = function (callback, thisArg) {
+    var uniqueElements = [];
+    this.forEach(function (element) {
+        if (uniqueElements.indexOf(element, Utils.equals) === -1) {
+            uniqueElements.push(element);
+        }
+    });
+    
+    return uniqueElements;
+};
+
+Array.prototype.filter = function (func, thisArg) {
+    var filtered = [];
+    for (var i = 0; i < this.length; i++) {
+        if (func.call(thisArg, this[i], i, this)) {
+            filtered.push(this[i]);
+        }
+    }
+    
+    return filtered;
+};
+
+Array.prototype.map = function (func, thisArg) {
+    var mapped = [];
+    for (var i = 0; i < this.length; i++) {
+        mapped.push(func.call(thisArg, this[i], i, this));
+    }
+    
+    return mapped;
+};
+
 
     /* UI */
 
@@ -161,7 +208,7 @@ function initUI() {
     $('#workingScheduleSwitch').change(updateConfigUi);
     
     /* fetch & push handlers */
-    $('#videoDeviceSelect').change(fetchCameraConfig);
+    $('#videoDeviceSelect').change(fetchCurrentCameraConfig);
     $('input.general').change(pushMainConfig);
     $('input.device, select.device, ' +
       'input.storage, select.storage, ' +
@@ -183,6 +230,31 @@ function initUI() {
     });
 }
 
+
+    /* settings */
+
+function openSettings(cameraId) {
+    if (cameraId != null) {
+        $('#videoDeviceSelect').val(cameraId).change();
+    }
+    
+    $('div.settings').addClass('open');
+    $('div.page-container').addClass('stretched');
+    $('div.settings-top-bar').addClass('open');
+    
+    updateConfigUi();
+}
+
+function closeSettings() {
+    $('div.settings').removeClass('open');
+    $('div.page-container').removeClass('stretched');
+    $('div.settings-top-bar').removeClass('open');
+}
+
+function isSettingsOpen() {
+    return $('div.settings').hasClass('open');   
+}
+
 function updateConfigUi() {
     var objs = $('tr.settings-item, div.advanced-setting, table.advanced-setting, div.settings-section-title, table.settings');
     
@@ -543,12 +615,21 @@ function hideApply() {
 
     var applyButton = $('#applyButton');
     
-    applyButton.animate({'opacity': '0'}, 200, function () {
+    applyButton.animate({'opacity': '0'}, 100, function () {
         applyButton.removeClass('progress');
         applyButton.css('display', 'none');
     });
 }
 
+function endProgress() {
+    if (Object.keys(pushConfigs).length === 0) {
+        hideApply();
+    }
+    else {
+        showApply();
+    }
+}
+
 function isProgress() {
     var applyButton = $('#applyButton');
     
@@ -567,12 +648,8 @@ function doApply() {
     
     function testReady() {
         if (finishedCount >= configs.length) {
-            if (Object.keys(pushConfigs).length === 0) {
-                hideApply();
-            }
-            else {
-                showApply();
-            }
+            endProgress();
+            recreateCameraFrames();
         }
     }
     
@@ -604,6 +681,9 @@ function doApply() {
     pushConfigs = {};
 }
 
+
+    /* fetch & push */
+
 function fetchCurrentConfig() {
     /* fetch the main configuration */
     ajax('GET', '/config/main/get/', null, function (data) {
@@ -617,17 +697,19 @@ function fetchCurrentConfig() {
         videoDeviceSelect.html('');
         for (i = 0; i < cameras.length; i++) {
             var camera = cameras[i];
-            videoDeviceSelect.append('<option value="' + camera['@id'] + '">' + camera['@name'] + '</option>');
+            videoDeviceSelect.append('<option value="' + camera['id'] + '">' + camera['name'] + '</option>');
         }
         
         if (cameras.length) {
             videoDeviceSelect[0].selectedIndex = 0;
-            fetchCameraConfig();
+            fetchCurrentCameraConfig();
         }
+        
+        recreateCameraFrames(cameras);
     });
 }
 
-function fetchCameraConfig() {
+function fetchCurrentCameraConfig() {
     var cameraId = $('#videoDeviceSelect').val();
     if (cameraId != null) {
         ajax('GET', '/config/' + cameraId + '/get/', null, function (data) {
@@ -658,24 +740,147 @@ function pushCameraConfig() {
     }
 }
 
+
+    /* camera frames */
+
+function addCameraFrameUi(cameraId, cameraName) {
+    var cameraFrameDiv = $(
+            '<div class="camera-frame">' +
+                '<div class="camera-top-bar">' +
+                    '<span class="camera-name"></span>' +
+                    '<div class="camera-buttons">' +
+                        '<div class="camera-button configure" title="configure"></div>' +
+                        '<div class="camera-button close" title="close"></div>' +
+                    '</div>' +
+                '</div>' +
+                '<div class="camera-container">' +
+                    '<img class="camera">' +
+                '</div>' +
+            '</div>');
+    
+    var nameSpan = cameraFrameDiv.find('span.camera-name');
+    var configureButton = cameraFrameDiv.find('div.camera-button.configure');
+    var closeButton = cameraFrameDiv.find('div.camera-button.close');
+    var cameraImg = cameraFrameDiv.find('img.camera');
+    
+    cameraFrameDiv.attr('id', 'camera' + cameraId);
+    nameSpan.html(cameraName);
+    
+    /* insert the new camera frame at the right position,
+     * with respect to the camera id */
+    var pageContainer = $('div.page-container');
+    var cameraFrames = pageContainer.find('div.camera-frame');
+    var cameraIds = cameraFrames.map(function () {return parseInt(this.id.substring(6));});
+    cameraIds.sort();
+    
+    var index = 0; /* find the first position that is greater than the current camera id */
+    while (index < cameraIds.length && cameraIds[index] < cameraId) {
+        index++;
+    }
+    
+    if (index < cameraIds.length) {
+        var beforeCameraFrame = pageContainer.find('div.camera-frame#camera' + cameraIds[index]);
+        cameraFrameDiv.insertAfter(beforeCameraFrame);
+    }
+    else  {
+        pageContainer.append(cameraFrameDiv);
+    }
+
+    /* fade in */
+    cameraFrameDiv.animate({'opacity': 1}, 100);
+    
+    /* add the button handlers */
+    configureButton.click(function () {
+        doConfigureCamera(cameraId);
+    });
+
+    closeButton.click(function () {
+        doCloseCamera(cameraId);
+    });
+    
+    /* add content to the frame */
+    cameraImg.attr('src', staticUrl + 'img/video1.jpg');
+}
+
+function remCameraFrameUi(cameraId) {
+    var pageContainer = $('div.page-container');
+    var cameraFrameDiv = pageContainer.find('div.camera-frame#camera' + cameraId);
+    cameraFrameDiv.animate({'opacity': 0}, 100, function () {
+        cameraFrameDiv.remove();
+    });
+}
+
+function recreateCameraFrames(cameras) {
+    var pageContainer = $('div.page-container');
+    
+    function updateCameras(cameras) {
+        cameras = cameras.filter(function (camera) {return camera.enabled;});
+        var i, camera, cameraId;
+        
+        /* remove no longer existing camera frames */
+        var addedCameraFrames = pageContainer.find('div.camera-frame');
+        for (i = 0; i < addedCameraFrames.length; i++) {
+            cameraId = parseInt(addedCameraFrames[i].id.substring(6));
+            if (cameras.filter(function (camera) {return camera.id === cameraId;}).length === 0) {
+                remCameraFrameUi(cameraId);
+            }
+        }
+        
+        /* add new camera frames */
+        for (i = 0; i < cameras.length; i++) {
+            camera = cameras[i];
+            if (pageContainer.find('div.camera-frame#camera' + camera.id).length === 0) {
+                addCameraFrameUi(camera.id);
+            }
+        }
+    }
+    
+    if (cameras != null) {
+        updateCameras(cameras);
+    }
+    else {
+        ajax('GET', '/config/list/', null, function (data) {
+            updateCameras(data.cameras);
+        });
+    }
+}
+
+function doConfigureCamera(cameraId) {
+    openSettings(cameraId);
+}
+
+function doCloseCamera(cameraId) {
+    remCameraFrameUi(cameraId);
+    showProgress();
+    ajax('GET', '/config/' + cameraId + '/get/', null, function (data) {
+        data['enabled'] = false;
+        ajax('POST', '/config/' + cameraId + '/set/', data, function () {
+            endProgress();
+            
+            /* if the current camera in the settings panel is the closed camera,
+             * we refresh its settings and update the UI */
+            if (isSettingsOpen() && ($('#videoDeviceSelect').val() === '' + cameraId)) {
+                fetchCurrentCameraConfig();
+            }
+        });
+    });
+}
+
+
+    /* startup function */
+
 $(document).ready(function () {
     /* open/close settings */
     $('img.settings-button').click(function () {
-        if ($('div.settings').hasClass('open')) {
-            $('div.settings').removeClass('open');
-            $('div.page-container').removeClass('stretched');
-            $('div.settings-top-bar').removeClass('open');
+        if (isSettingsOpen()) {
+             closeSettings();
         }
         else {
-            $('div.settings').addClass('open');
-            $('div.page-container').addClass('stretched');
-            $('div.settings-top-bar').addClass('open');
-
-            updateConfigUi();
+            openSettings();
         }
     });
     
-    /* prevent scroll events on settings div from propagating */
+    /* prevent scroll events on settings div from propagating TODO this does not work */
     $('div.settings').mousewheel(function (e, d) {
         var t = $(this);
         if (d > 0 && t.scrollTop() === 0) {
diff --git a/templates/macros.html b/templates/macros.html
deleted file mode 100644 (file)
index 6458ccc..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-{% macro video(url) %}
-<div class="video-frame">
-    <div class="video-top-bar">
-        <span class="video-name">Video A</span>
-        <div class="video-buttons">
-            <div class="video-button configure" title="configure">
-            </div><div class="video-button close" title="close"></div>
-        </div>
-    </div>
-    <div class="video-container">
-        <img class="video" src="{{url}}">
-    </div>
-</div>
-{% endmacro %}
index afba521b79af811743392ac9427b16dc8ef4d1bc..10c0532086e4990b638a55ef6661c0cfd959fe06 100644 (file)
             </div>
         </div>
         <div class="page-container">
-            {% block page_content %}{% endblock %}
         </div>
         <div class="footer">
             copyright &copy; Calin Crisan 2013