From: Calin Crisan Date: Sun, 29 Sep 2013 11:59:58 +0000 (+0300) Subject: added camera frame js interaction code X-Git-Url: http://www.vanbest.org/gitweb/?a=commitdiff_plain;h=8747377281b4ed2e2b396c8995612b85e83b339f;p=motioneye-debian added camera frame js interaction code --- diff --git a/motioneye.py b/motioneye.py index bd5e1ed..ad239fb 100644 --- a/motioneye.py +++ b/motioneye.py @@ -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) diff --git a/src/handlers.py b/src/handlers.py index 2453173..79768e6 100644 --- a/src/handlers.py +++ b/src/handlers.py @@ -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}) diff --git a/static/css/main.css b/static/css/main.css index a4994e2..c4d7768 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -25,6 +25,14 @@ } + /* 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; -} diff --git a/static/js/main.js b/static/js/main.js index bd3ab37..f7416b3 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -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(''); + videoDeviceSelect.append(''); } 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 = $( + '
' + + '
' + + '' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '' + + '
' + + '
'); + + 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 index 6458ccc..0000000 --- a/templates/macros.html +++ /dev/null @@ -1,14 +0,0 @@ -{% macro video(url) %} -
-
- Video A -
-
-
-
-
-
- -
-
-{% endmacro %} diff --git a/templates/main.html b/templates/main.html index afba521..10c0532 100644 --- a/templates/main.html +++ b/templates/main.html @@ -416,7 +416,6 @@
- {% block page_content %}{% endblock %}