]> www.vanbest.org Git - motioneye-debian/commitdiff
added ui for adding a device
authorCalin Crisan <ccrisan@gmail.com>
Sat, 5 Oct 2013 13:47:02 +0000 (16:47 +0300)
committerCalin Crisan <ccrisan@gmail.com>
Sat, 5 Oct 2013 13:47:02 +0000 (16:47 +0300)
doc/todo.txt
src/handlers.py
static/css/main.css
static/css/ui.css
static/img/modal-progress.gif [new file with mode: 0644]
static/img/settings.svg
static/js/main.js
static/js/ui.js
templates/main.html

index 0b81b5b22b8dc8daa9dfc15a51453d8dfbfd33ee..3b57167017635d2fe00aa8026ab0657711b3618c 100644 (file)
@@ -19,6 +19,5 @@
 -> click to zoom on cameras
 -> add a previewer for movies
 -> add a previewer for snapshots
--> other todos
--> use svg instead of pngs for icons
--> add a motioneye.svg icon
\ No newline at end of file
+-> add a motioneye.svg icon
+-> other todos
\ No newline at end of file
index 3127648d6d65921a43b1db6ec0137f942956fed3..abc589ee0f042a6d071a29fb8f208342109c9f53 100644 (file)
@@ -200,7 +200,15 @@ class ConfigHandler(BaseHandler):
     def list_devices(self):
         logging.debug('listing devices')
         
-        self.finish_json({'devices': v4l2ctl.list_devices()})
+        configured_devices = {}
+        for camera_id in config.get_camera_ids():
+            data = config.get_camera(camera_id)
+            configured_devices[data['videodevice']] = True
+            
+        devices = [{'device': d[0], 'name': d[1], 'configured': d[0] in configured_devices}
+                for d in v4l2ctl.list_devices()]
+        
+        self.finish_json({'devices': devices})
     
     def add_camera(self):
         logging.debug('adding new camera')
index a95a9ce24e8a6c90117b93d0ad8dcb64649515e7..740e6416f7df7319a621a5681e7b0cb997d3e944 100644 (file)
@@ -42,7 +42,7 @@ html {
 div.page,
 div.header-container {
     position: relative;
-    min-width: 320px;
+    min-width: 360px;
     min-height: 60px;
     width: 100%;
 }
@@ -89,12 +89,28 @@ div.page-container.stretched {
 }
 
 
-    /* icons */
+    /* icons & icon buttons */
 
-img.settings-button {
-    margin: 2px;
-    cursor: pointer;
+div.button.settings-button {
+    margin: 1px;
+    vertical-align: middle;
+    background-image: url(../img/settings.svg);
+    width: 48px;
+    height: 48px;
+}
+
+div.button.rem-camera-button {
+    display: none;
+    margin: 1px;
     vertical-align: middle;
+    background-image: url(../img/settings.svg);
+    width: 48px;
+    height: 48px;
+    background-position: -48px 0px;
+}
+
+div.settings-top-bar.open div.button.rem-camera-button {
+    display: inline-block;
 }
 
 div.logo {
@@ -135,7 +151,7 @@ div.settings {
 
 div.settings.open {
     width: 40%;
-    min-width: 320px;
+    min-width: 360px;
 }
 
 div.settings-container {
@@ -158,7 +174,7 @@ div.settings-top-bar {
 
 div.settings-top-bar.open {
     background-color: #414141;
-    min-width: 320px;
+    min-width: 360px;
 }
 
 div.settings-section-title {
@@ -204,9 +220,9 @@ select.video-device {
     display: none;
     padding: 4px 1.5em 4px 4px;
     vertical-align: middle;
-    font-size: 20px;
+    font-size: 1.1em;
     width: auto;
-    max-width: 40%;
+    max-width: 35%;
 }
 
 div.apply-button {
@@ -271,6 +287,15 @@ input[type=text].working-schedule.number {
 }
 
 
+    /* dialogs */
+
+table.add-camera-dialog select,
+table.add-camera-dialog input[type=text],
+table.add-camera-dialog input[type=password] {
+    width: 10em;
+}
+
+
     /* camera frames */
 
 div.camera-list {
@@ -318,10 +343,6 @@ div.camera-button {
     transition: all 0.1s linear;
 }
 
-div.camera-button:ACTIVE {
-    opacity: 0.6;
-}
-
 div.camera-button.close {
     background-position: 0px 0px;
 }
index eba130f575375f53def8a0d03d53fa970e83b1c9..ecf038546487670c5148615c674b596294de838c 100644 (file)
@@ -24,11 +24,11 @@ div.button {
     -webkit-user-select: none;
     -moz-user-select: none;
     user-select: none;
-    cursor: pointer;    
+    cursor: pointer;
+    display: inline-block;    
 }
 
 div.button.dialog {
-    display: inline-block;
     background-color: #414141;
     min-width: 60px;
     height: 1.2em;
@@ -41,27 +41,21 @@ div.button.dialog {
     transition: all 0.1s linear;
 }
 
-div.button.dialog:FOCUS,
-div.button.dialog:HOVER {
-    border: 1px solid #3498db;
-    background-color: #515151;
-}
-
-div.button.dialog:ACTIVE {
-    background-color: #414141;
-}
-
 div.button.dialog.default {
     background-color: #317CAD;
 }
 
-div.button.dialog.default:FOCUS,
-div.button.dialog.default:HOVER {
-    background-color: #3498db;
+div.button.mouse-effect {
+    opacity: 0.9;
+    transition: opacity 0.1s linear;
 }
 
-div.button.dialog.default:ACTIVE {
-    background-color: #317CAD;
+div.button.mouse-effect:HOVER {
+    opacity: 1;
+}
+
+div.button.mouse-effect:ACTIVE {
+    opacity: 0.6;
 }
 
 
@@ -312,16 +306,12 @@ div.modal-close-button {
     height: 16px;
     background-image: url(../img/top-bar-buttons.svg);
     opacity: 1;
-    transition: all 0.1s linear;
     cursor: pointer;
 }
 
-div.modal-close-button:ACTIVE {
-    opacity: 0.6;
-}
-
 table.modal-buttons-container {
     width: 100%;
+    margin: auto;
     text-align: center;
     -webkit-user-select: none;
     -moz-user-select: none;
@@ -336,6 +326,27 @@ table.modal-buttons-container div.button.dialog {
     display: block;
 }
 
+div.modal-progress {
+    border-radius: 10px;
+    background-image: url(../img/modal-progress.gif);
+    width: 64px;
+    height: 64px;
+}
+
+td.dialog-item-label {
+    text-align: right;
+    padding-right: 5px;
+}
+
+td.dialog-item-value {
+    text-align: left;
+    padding-left: 5px;
+}
+
+span.dialog-item-label {
+    font-size: 0.9em;    
+}
+
 
     /* misc */
 
diff --git a/static/img/modal-progress.gif b/static/img/modal-progress.gif
new file mode 100644 (file)
index 0000000..df1657c
Binary files /dev/null and b/static/img/modal-progress.gif differ
index 683f53824f202b846031da32641590185dd251c6..c11c28ea011551185afd42016ccce1a02fc52cfb 100644 (file)
@@ -9,7 +9,7 @@
    xmlns="http://www.w3.org/2000/svg"
    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="48"
+   width="96"
    height="48"
    id="svg2"
    version="1.1"
@@ -27,9 +27,9 @@
      borderopacity="1.0"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="7.59375"
-     inkscape:cx="72.187114"
-     inkscape:cy="23.704825"
+     inkscape:zoom="17.085938"
+     inkscape:cx="93.239037"
+     inkscape:cy="25.695745"
      inkscape:document-units="px"
      inkscape:current-layer="layer1"
      showgrid="false"
@@ -50,7 +50,9 @@
      inkscape:snap-object-midpoints="true"
      inkscape:snap-center="true"
      inkscape:snap-page="true"
-     inkscape:snap-intersection-paths="true" />
+     inkscape:snap-intersection-paths="true"
+     showguides="true"
+     inkscape:guide-bbox="true" />
   <metadata
      id="metadata7">
     <rdf:RDF>
      id="layer1"
      transform="translate(0,-1004.3622)">
     <path
-       style="fill:#3498db;fill-opacity:1;stroke:none;opacity:1"
-       d="M 23 30 C 20.954307 30 19.211772 31.240478 18.4375 33 L 5 33 C 4.446 33 4 33.446 4 34 L 4 36 C 4 36.554 4.446 37 5 37 L 18.4375 37 C 19.211772 38.759522 20.954307 40 23 40 C 25.045693 40 26.788228 38.759522 27.5625 37 L 43 37 C 43.554 37 44 36.554 44 36 L 44 34 C 44 33.446 43.554 33 43 33 L 27.5625 33 C 26.788228 31.240478 25.045693 30 23 30 z M 23 33 C 24.10457 33 25 33.89543 25 35 C 25 36.10457 24.10457 37 23 37 C 21.89543 37 21 36.10457 21 35 C 21 33.89543 21.89543 33 23 33 z "
+       style="fill:#3498db;fill-opacity:1;stroke:none"
+       d="m 23,30 c -2.045693,0 -3.788228,1.240478 -4.5625,3 L 5,33 c -0.554,0 -1,0.446 -1,1 l 0,2 c 0,0.554 0.446,1 1,1 l 13.4375,0 c 0.774272,1.759522 2.516807,3 4.5625,3 2.045693,0 3.788228,-1.240478 4.5625,-3 L 43,37 c 0.554,0 1,-0.446 1,-1 l 0,-2 c 0,-0.554 -0.446,-1 -1,-1 L 27.5625,33 C 26.788228,31.240478 25.045693,30 23,30 z m 0,3 c 1.10457,0 2,0.89543 2,2 0,1.10457 -0.89543,2 -2,2 -1.10457,0 -2,-0.89543 -2,-2 0,-1.10457 0.89543,-2 2,-2 z"
        transform="translate(0,1004.3622)"
-       id="rect3755" />
+       id="rect3755"
+       inkscape:connector-curvature="0" />
     <path
-       style="fill:#3498db;fill-opacity:1;stroke:none;opacity:1"
-       d="M 36 20 C 33.954307 20 32.211772 21.240478 31.4375 23 L 5 23 C 4.446 23 4 23.446 4 24 L 4 26 C 4 26.554 4.446 27 5 27 L 31.4375 27 C 32.211772 28.759522 33.954307 30 36 30 C 38.045693 30 39.788228 28.759522 40.5625 27 L 43 27 C 43.554 27 44 26.554 44 26 L 44 24 C 44 23.446 43.554 23 43 23 L 40.5625 23 C 39.788228 21.240478 38.045693 20 36 20 z M 36 23 C 37.10457 23 38 23.89543 38 25 C 38 26.10457 37.10457 27 36 27 C 34.89543 27 34 26.10457 34 25 C 34 23.89543 34.89543 23 36 23 z "
+       style="fill:#3498db;fill-opacity:1;stroke:none"
+       d="m 36,20 c -2.045693,0 -3.788228,1.240478 -4.5625,3 L 5,23 c -0.554,0 -1,0.446 -1,1 l 0,2 c 0,0.554 0.446,1 1,1 l 26.4375,0 c 0.774272,1.759522 2.516807,3 4.5625,3 2.045693,0 3.788228,-1.240478 4.5625,-3 L 43,27 c 0.554,0 1,-0.446 1,-1 l 0,-2 c 0,-0.554 -0.446,-1 -1,-1 l -2.4375,0 C 39.788228,21.240478 38.045693,20 36,20 z m 0,3 c 1.10457,0 2,0.89543 2,2 0,1.10457 -0.89543,2 -2,2 -1.10457,0 -2,-0.89543 -2,-2 0,-1.10457 0.89543,-2 2,-2 z"
        transform="translate(0,1004.3622)"
-       id="rect3757" />
+       id="rect3757"
+       inkscape:connector-curvature="0" />
     <path
-       style="fill:#3498db;fill-opacity:1;stroke:none;opacity:1"
-       d="M 15 10 C 12.954308 10 11.211772 11.240478 10.4375 13 L 5 13 C 4.446 13 4 13.446 4 14 L 4 16 C 4 16.554 4.446 17 5 17 L 10.4375 17 C 11.211772 18.759522 12.954308 20 15 20 C 17.045692 20 18.788228 18.759522 19.5625 17 L 43 17 C 43.554 17 44 16.554 44 16 L 44 14 C 44 13.446 43.554 13 43 13 L 19.5625 13 C 18.788228 11.240478 17.045692 10 15 10 z M 15 13 C 16.10457 13 17 13.89543 17 15 C 17 16.10457 16.10457 17 15 17 C 13.89543 17 13 16.10457 13 15 C 13 13.89543 13.89543 13 15 13 z "
+       style="fill:#3498db;fill-opacity:1;stroke:none"
+       d="m 15,10 c -2.045692,0 -3.788228,1.240478 -4.5625,3 L 5,13 c -0.554,0 -1,0.446 -1,1 l 0,2 c 0,0.554 0.446,1 1,1 l 5.4375,0 c 0.774272,1.759522 2.516808,3 4.5625,3 2.045692,0 3.788228,-1.240478 4.5625,-3 L 43,17 c 0.554,0 1,-0.446 1,-1 l 0,-2 c 0,-0.554 -0.446,-1 -1,-1 L 19.5625,13 C 18.788228,11.240478 17.045692,10 15,10 z m 0,3 c 1.10457,0 2,0.89543 2,2 0,1.10457 -0.89543,2 -2,2 -1.10457,0 -2,-0.89543 -2,-2 0,-1.10457 0.89543,-2 2,-2 z"
        transform="translate(0,1004.3622)"
-       id="rect3759" />
+       id="rect3759"
+       inkscape:connector-curvature="0" />
+    <path
+       sodipodi:type="arc"
+       style="fill:none;stroke:#3498db;stroke-width:2.6408689;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="path3764"
+       sodipodi:cx="67.901237"
+       sodipodi:cy="6.4814816"
+       sodipodi:rx="20.098766"
+       sodipodi:ry="19.518518"
+       d="m 88.000004,6.4814816 a 20.098766,19.518518 0 1 1 -40.197533,0 20.098766,19.518518 0 1 1 40.197533,0 z"
+       transform="matrix(0.74631447,0,0,0.76850096,21.324322,1023.3813)" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path3766"
+       d="m 82,1030.3622 c 0.554,0 1,-0.446 1,-1 l 0,-2 c 0,-0.554 -0.446,-1 -1,-1 -13.004,0 -8.7461,0 -20,0 -0.554,0 -1,0.446 -1,1 l 0,2 c 0,0.554 0.446,1 1,1 12.4816,0 8.4097,0 20,0 z"
+       style="fill:#3498db;fill-opacity:1;stroke:none"
+       sodipodi:nodetypes="cssccsscc" />
   </g>
 </svg>
index 7874cd7e90c46ae0692d264560d72de26b3bba10..0f39a6161a39172fd0441ebba2d95ec1a02f80db 100644 (file)
@@ -208,7 +208,21 @@ function initUI() {
     $('#workingScheduleSwitch').change(updateConfigUi);
     
     /* fetch & push handlers */
-    $('#videoDeviceSelect').change(fetchCurrentCameraConfig);
+    $('#videoDeviceSelect').change(function () {
+        if ($('#videoDeviceSelect').val() === 'add') {
+            runAddCameraDialog();
+            if ($('#videoDeviceSelect').find('option').length > 1) {
+                $('#videoDeviceSelect')[0].selectedIndex = 0;
+            }
+            else {
+                $('#videoDeviceSelect')[0].selectedIndex = -1;
+            }
+            $('#videoDeviceSelect').change();
+        }
+        else {
+            fetchCurrentCameraConfig();
+        }
+    });
     $('input.general').change(pushMainConfig);
     $('input.device, select.device, ' +
       'input.storage, select.storage, ' +
@@ -368,7 +382,7 @@ function updateConfigUi() {
     });
     
     /* re-validate all the input validators */
-    $('div.settings').find('input.text-validator, input.number-validator, input.time-validator').each(function () {
+    $('div.settings').find('input.validator').each(function () {
         this.validate();
     });
     
@@ -687,6 +701,12 @@ function doApply() {
         return;
     }
     
+    if (!configUiValid()) {
+        runAlertDialog('Make sure all the configuration options are valid!');
+        
+        return;
+    }
+    
     showProgress();
     
     for (var i = 0; i < configs.length; i++) {
@@ -725,11 +745,15 @@ function fetchCurrentConfig() {
                 var camera = cameras[i];
                 videoDeviceSelect.append('<option value="' + camera['id'] + '">' + camera['name'] + '</option>');
             }
+            videoDeviceSelect.append('<option value="add">add camera...</option>');
             
-            if (cameras.length) {
+            if (cameras.length > 1) {
                 videoDeviceSelect[0].selectedIndex = 0;
                 fetchCurrentCameraConfig();
             }
+            else {
+                videoDeviceSelect[0].selectedIndex = -1;
+            }
             
             recreateCameraFrames(cameras);
         });
@@ -785,6 +809,95 @@ function pushPreview() {
 }
 
 
+    /* dialogs */
+
+function runAlertDialog(message) {
+    runModalDialog({title: message, buttons: 'ok'});
+}
+
+function runAddCameraDialog(devices) {
+    var content = 
+            $('<table class="add-camera-dialog">' +
+                '<tr>' +
+                    '<td class="dialog-item-label"><span class="dialog-item-label">Device</span></td>' +
+                    '<td class="dialog-item-value"><select class="styled" id="deviceSelect"></select></td>' +
+                    '<td><span class="help-mark" title="the device you wish to add to motionEye">?</span></td>' +
+                '</tr>' +
+                '<tr class="remote">' +
+                    '<td class="dialog-item-label"><span class="dialog-item-label">Host</span></td>' +
+                    '<td class="dialog-item-value"><input type="text" class="styled" id="hostEntry" placeholder="e.g. 192.168.1.2"></td>' +
+                    '<td><span class="help-mark" title="the remote motionEye host (e.g. 192.168.1.2)">?</span></td>' +
+                '</tr>' +
+                '<tr class="remote">' +
+                    '<td class="dialog-item-label"><span class="dialog-item-label">Port</span></td>' +
+                    '<td class="dialog-item-value"><input type="text" class="styled" id="portEntry" placeholder="e.g. 80"></td>' +
+                    '<td><span class="help-mark" title="the remote motionEye port (e.g. 80)">?</span></td>' +
+                '</tr>' +
+                '<tr class="remote">' +
+                    '<td class="dialog-item-label"><span class="dialog-item-label">Username</span></td>' +
+                    '<td class="dialog-item-value"><input type="text" class="styled" id="usernameEntry" placeholder="username..."></td>' +
+                    '<td><span class="help-mark" title="the remote administrator\'s username">?</span></td>' +
+                '</tr>' +
+                '<tr class="remote">' +
+                    '<td class="dialog-item-label"><span class="dialog-item-label">Password</span></td>' +
+                    '<td class="dialog-item-value"><input type="password" class="styled" id="passwordEntry" placeholder="password..."></td>' +
+                    '<td><span class="help-mark" title="the remote administrator\'s password">?</span></td>' +
+                '</tr>' +
+            '</table>');
+    
+    /* collect ui widgets */
+    var deviceSelect = content.find('#deviceSelect');
+    var hostEntry = content.find('#hostEntry');
+    var portEntry = content.find('#portEntry');
+    var usernameEntry = content.find('#usernameEntry');
+    var passwordEntry = content.find('#passwordEntry');
+    
+    /* make validators */
+    makeTextValidator(hostEntry, true);
+    makeNumberValidator(portEntry, 1, 65535, false, false, true);
+    makeTextValidator(usernameEntry, true);
+    
+    /* ui interaction */
+    content.find('tr.remote').css('display', 'none');
+    var updateUi = function () {
+        if (deviceSelect.val() === 'remote') {
+            content.find('tr.remote').css('display', 'table-row');
+        }
+        else {
+            content.find('tr.remote').css('display', 'none');
+        }
+        
+        updateModalDialogPosition();
+    };
+    
+    deviceSelect.change(updateUi).change();
+    
+    showModalDialog('<div class="modal-progress"></div>');
+
+    /* fetch the available devices */
+    ajax('GET', '/config/list_devices/', null, function (data) {
+        /* add available devices */
+        data.devices.forEach(function (device) {
+            if (!device.configured) {
+                deviceSelect.append('<option value="' + device.device + '">' + device.name + '</option>');
+            }
+        });
+        
+        deviceSelect.append('<option value="remote">Remote device...</option>');
+        
+        runModalDialog({
+            title: 'Add Camera...',
+            closeButton: true,
+            buttons: 'okcancel',
+            content: content,
+            onOk: function () {
+                
+            }
+        });
+    });
+}
+
+
     /* camera frames */
 
 function addCameraFrameUi(cameraId, cameraName, framerate) {
@@ -793,8 +906,8 @@ function addCameraFrameUi(cameraId, cameraName, framerate) {
                 '<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 class="button camera-button mouse-effect configure" title="configure"></div>' +
+                        '<div class="button camera-button mouse-effect close" title="close"></div>' +
                     '</div>' +
                 '</div>' +
                 '<div class="camera-container">' +
@@ -969,7 +1082,7 @@ function refreshCameraFrames() {
 
 $(document).ready(function () {
     /* open/close settings */
-    $('img.settings-button').click(function () {
+    $('div.settings-button').click(function () {
         if (isSettingsOpen()) {
              closeSettings();
         }
index 0b2eaaba02e46825b1140f44ff0c14d626967097..2b28bf65a87e12298a452c747a9a7786be6fa40c 100644 (file)
@@ -266,6 +266,7 @@ function makeTextValidator($input, required) {
     $input.blur(validate);
     $input.change(validate).change();
     
+    $input.addClass('validator');
     $input.addClass('text-validator');
     $input[0].validate = validate;
 }
@@ -357,6 +358,7 @@ function makeNumberValidator($input, minVal, maxVal, floating, sign, required) {
     $input.blur(validate);
     $input.change(validate).change();
     
+    $input.addClass('validator');
     $input.addClass('number-validator');
     $input[0].validate = validate;
 }
@@ -395,10 +397,53 @@ function makeTimeValidator($input) {
         timeFormat: 'H:i',
     });
     
+    $input.addClass('validator');
     $input.addClass('time-validator');
     $input[0].validate = validate;
 }
 
+function makeRegexValidator($input, regex, required) {
+    if (required == null) {
+        required = true;
+    }
+    
+    function isValid(strVal) {
+        if (!$input.parents('tr:eq(0)').is(':visible')) {
+            return true; /* an invisible element is considered always valid */
+        }
+
+        if (strVal.length === 0 && !required) {
+            return true;
+        }
+        
+        return strVal.match(new RegExp(regex)) != null;
+    }
+    
+    var msg = 'enter a valid value';
+    
+    function validate() {
+        var strVal = $input.val();
+        if (isValid(strVal)) {
+            $input.attr('title', '');
+            $input.removeClass('error');
+            $input[0].invalid = false;
+        }
+        else {
+            $input.attr('title', msg);
+            $input.addClass('error');
+            $input[0].invalid = true;
+        }
+    }
+    
+    $input.keyup(validate);
+    $input.blur(validate);
+    $input.change(validate).change();
+    
+    $input.addClass('validator');
+    $input.addClass('regex-validator');
+    $input[0].validate = validate;
+}
+
 
     /* modal dialog */
 
@@ -406,6 +451,21 @@ function showModalDialog(content, onClose) {
     var glass = $('div.modal-glass');
     var container = $('div.modal-container');
     
+    if (container.is(':visible')) {
+        /* the modal dialog is already visible,
+         * we just replace the content */
+        
+        if (container[0]._onClose) {
+            container[0]._onClose();
+        }
+        
+        container[0]._onClose = onClose; /* remember the onClose handler */
+        container.html(content);
+        updateModalDialogPosition();
+        
+        return;
+    }
+    
     glass.css('display', 'block');
     glass.animate({'opacity': '0.7'}, 200);
     
@@ -445,7 +505,7 @@ function updateModalDialogPosition() {
     var windowWidth = $(window).width();
     var windowHeight = $(window).height();
     var modalWidth = container.width();
-    var modalHeight = container.width();
+    var modalHeight = container.height();
     
     container.css('left', (windowWidth - modalWidth) / 2);
     container.css('top', (windowHeight - modalHeight) / 2);
@@ -462,7 +522,7 @@ function makeModalDialogButtons(buttonsInfo) {
     var tr = buttonsContainer.find('tr');
     
     buttonsInfo.forEach(function (info) {
-        var buttonDiv = $('<div class="button dialog"></div>');
+        var buttonDiv = $('<div class="button dialog mouse-effect"></div>');
         
         buttonDiv.click(hideModalDialog); /* every button closes the dialog */
         buttonDiv.attr('tabIndex', '0'); /* make button focusable */
@@ -481,6 +541,9 @@ function makeModalDialogButtons(buttonsInfo) {
         tr.append(td);
     });
     
+    /* limit the size of the buttons container */
+    buttonsContainer.css('max-width', (buttonsInfo.length * 10) + 'em');
+    
     return buttonsContainer;
 }
 
@@ -498,7 +561,7 @@ function makeModalDialogTitleBar(options) {
     titleBar.append(titleSpan);
     
     if (options.closeButton) {
-        var closeButton = $('<div class="modal-close-button" title="close"></div>');
+        var closeButton = $('<div class="button modal-close-button mouse-effect" title="close"></div>');
         closeButton.click(hideModalDialog);
         titleBar.append(closeButton);
     }
@@ -511,7 +574,7 @@ function runModalDialog(options) {
      * * title: String
      * * closeButton: Boolean
      * * content: any
-     * * buttons: 'yesno'|'okcancel'|Array
+     * * buttons: 'ok'|'yesno'|'okcancel'|Array
      * * onYes: Function
      * * onNo: Function
      * * onOk: Function
@@ -532,7 +595,9 @@ function runModalDialog(options) {
     
     /* add supplied content */
     if (options.content) {
-        content.append(options.content);
+        var contentWrapper = $('<div style="padding: 10px;"></div>');
+        contentWrapper.append(options.content);
+        content.append(contentWrapper);
     }
     
     /* add buttons */
@@ -542,12 +607,24 @@ function runModalDialog(options) {
             {caption: 'Yes', isDefault: true, click: options.onYes}
         ];
     }
+    if (options.buttons === 'yesnocancel') {
+        options.buttons = [
+            {caption: 'Cancel', click: options.onCancel},
+            {caption: 'No', click: options.onNo},
+            {caption: 'Yes', isDefault: true, click: options.onYes}
+        ];
+    }
     else if (options.buttons === 'okcancel') {
         options.buttons = [
             {caption: 'Cancel', click: options.onCancel},
             {caption: 'OK', isDefault: true, click: options.onOk}
         ];
     }
+    else if (options.buttons === 'ok') {
+        options.buttons = [
+            {caption: 'OK', isDefault: true, click: options.onOk}
+        ];
+    }
     
     if (options.buttons) {
         buttonsDiv = makeModalDialogButtons(options.buttons);
@@ -560,10 +637,15 @@ function runModalDialog(options) {
         });
     }
     
+    /* add some margins */
     if ((buttonsDiv || options.content) && titleBar) {
         titleBar.css('margin-bottom', '5px');
     }
     
+    if (buttonsDiv && options.content) {
+        buttonsDiv.css('margin-top', '5px');
+    }
+    
     var handleKeyUp = function (e) {
         switch (e.which) {
             case 13:
index 810cf68f97fcb7f1e7b484da4eab484e6b2f3f7c..01d24f9f08e056890b462a63b57902e6f508cc38 100644 (file)
@@ -16,8 +16,9 @@
     <div class="header">
         <div class="header-container">
             <div class="settings-top-bar">
-                <img class="settings-button" src="{{STATIC_URL}}img/settings.svg" title="settings">
+                <div class="button settings-button mouse-effect" title="settings"></div>
                 <select class="video-device styled" id="videoDeviceSelect"></select>
+                <div class="button rem-camera-button mouse-effect" id="remCameraButton" title="remove camera"></div>
                 <div class="button apply-button" id="applyButton">Apply</div>
             </div>
             <div class="logo">