]> www.vanbest.org Git - motioneye-debian/commitdiff
got rid of base-site
authorCalin Crisan <ccrisan@gmail.com>
Sun, 29 Sep 2013 09:56:03 +0000 (12:56 +0300)
committerCalin Crisan <ccrisan@gmail.com>
Sun, 29 Sep 2013 09:56:03 +0000 (12:56 +0300)
motioneye.py
src/config.py
src/motionctl.py
static/css/base-site.css [deleted file]
static/css/main.css
static/js/base-site.js [deleted file]
static/js/main.js [new file with mode: 0644]
templates/base-site.html [deleted file]
templates/main.html

index f62efd40dcec5ee3ddcd31939c31dad0e90d6604..bd5e1ed13c765825c8407579019cb4f514b8411c 100644 (file)
@@ -41,9 +41,9 @@ def _start_server():
 
 
 if __name__ == '__main__':
-    _configure_signals()
-    _configure_logging()
-    _start_server()
+    _configure_signals()
+    _configure_logging()
+    _start_server()
     
 #     import config
 #     main_config = config.get_main()
@@ -53,6 +53,5 @@ if __name__ == '__main__':
 #     #config.set_camera(1, data)
 #     config.rem_camera(1)
     
-    import motionctl
-    
-    motionctl.start()
+#     import motionctl
+#     motionctl.start()
index 86273d0fc916137e3377fa8aa2c5acec3743c954..a87b4c4dbbacfedc9b39227323256ea92fcfc536 100644 (file)
@@ -477,7 +477,7 @@ def _set_default_motion_camera(data):
     data.setdefault('@network_share_name', '')
     data.setdefault('@network_username', '')
     data.setdefault('@network_password', '')
-    data.setdefault('target_dir', '.')
+    data.setdefault('target_dir', settings.RUN_PATH)
     
     data.setdefault('webcam_localhost', False)
     data.setdefault('webcam_port', 8080)
index 507f584980b2fa2f710b0ad9388d80a42524a3dd..6c7cce1a95371584cdd306c12c9e2b3c81d3107b 100644 (file)
@@ -35,7 +35,7 @@ def start():
     log_file = open(motion_log_path, 'w')
     
     process = subprocess.Popen(args, stdout=log_file, stderr=log_file, close_fds=True,
-            cwd=settings.RUN_PATH)
+            cwd=settings.CONF_PATH)
     
     # wait 2 seconds to see that the process has successfully started
     for i in xrange(20):  # @UnusedVariable
diff --git a/static/css/base-site.css b/static/css/base-site.css
deleted file mode 100644 (file)
index c104b06..0000000
+++ /dev/null
@@ -1,295 +0,0 @@
-
-
-    /* fonts */
-
-@font-face {
-    font-family: 'Maven Pro';
-    src: url('../fnt/mavenpro-regular-webfont.eot');
-    src: url('../fnt/mavenpro-regular-webfont.eot?#iefix') format('embedded-opentype'),
-         url('../fnt/mavenpro-regular-webfont.woff') format('woff'),
-         url('../fnt/mavenpro-regular-webfont.ttf') format('truetype'),
-         url('../fnt/mavenpro-regular-webfont.svg#maven_proregular') format('svg');
-    font-weight: normal;
-    font-style: normal;
-}
-@font-face {
-    font-family: 'Maven Pro';
-    src: url('../fnt/mavenpro-bold-webfont.eot');
-    src: url('../fnt/mavenpro-bold-webfont.eot?#iefix') format('embedded-opentype'),
-         url('../fnt/mavenpro-bold-webfont.woff') format('woff'),
-         url('../fnt/mavenpro-bold-webfont.ttf') format('truetype'),
-         url('../fnt/mavenpro-bold-webfont.svg#maven_probold') format('svg');
-    font-weight: bold;
-    font-style: normal;
-}
-
-    /* layout */
-
-html {
-    font-family: 'Maven Pro';
-}
-
-div.page,
-div.header-container {
-    position: relative;
-    min-width: 320px;
-    width: 100%;
-}
-
-div.page {
-    margin-top: 50px;
-    padding-bottom: 20px;
-    font-size: 1em;
-    transition: all 0.5s linear;
-}
-
-div.header {
-    background-color: rgba(64, 64, 64, 0.5);
-    top: 0px;
-    width: 100%;
-    height: 50px;
-    position: fixed;
-    overflow: hidden;
-    z-index: 10000;
-}
-
-div.header-container {
-    transition: all 0.5s linear;
-}
-
-div.footer {
-    position: absolute;
-    bottom: 0px;
-    width: 100%;
-    height: 20px;
-    font-size: 0.5em;
-    color: #aaa;
-    text-align: center;
-}
-
-div.page-container {
-    transition: all 0.2s linear;
-    padding: 5px;
-}
-
-div.page-container.stretched {
-    margin-left: 40%;
-}
-
-
-    /* settings */
-
-div.settings {
-    background-color: #313131;
-    position: fixed;
-    z-index: 1;
-    top: 50px;
-    left: 0px;
-    width: 0px;
-    height: 100%;
-    transition: all 0.2s linear;
-    overflow: auto;
-}
-
-div.settings.open {
-    width: 40%;
-    min-width: 320px;
-}
-
-div.settings-container {
-    padding-top: 10px;
-    padding-bottom: 60px;
-    display: none;
-    white-space: nowrap;
-}
-
-div.settings.open div.settings-container {
-    display: block;
-}
-
-div.settings-top-bar {
-    position: relative;
-    display: inline-block;
-    width: 40%;
-    height: 50px;
-}
-
-div.settings-top-bar.open {
-    background-color: #414141;
-    min-width: 320px;
-}
-
-div.settings-section-title {
-    position: relative;
-    text-align: right;
-    background-color: rgba(100, 100, 100, 0.3);
-    padding: 5px;
-}
-
-table.settings {
-    width: 100%;
-    padding: 0.5em 0.5em 1em 0.5em;
-}
-
-td.settings-item-label {
-    width: 50%;
-    text-align: right;
-    padding-right: 5px;
-}
-
-td.settings-item-value {
-    width: 50%;
-    text-align: left;
-    padding-left: 5px;
-}
-
-span.settings-item-label {
-    font-size: 0.9em;
-}
-
-span.settings-item-unit {
-    font-size: 0.6em;
-    padding: 0px 0.2em;
-}
-
-div.settings-item-separator {
-    height: 1px;
-    border-top: 1px solid #414141;
-    margin: 5px 0px;
-}
-
-select.video-device {
-    display: none;
-    padding: 4px 1.5em 4px 4px;
-    vertical-align: middle;
-    font-size: 20px;
-    width: auto;
-    max-width: 40%;
-}
-
-div.apply-button {
-    position: relative;
-    display: none;
-    opacity: 0;
-    float: right;
-    width: 80px;
-    height: 30px;
-    line-height: 30px;
-    text-align: center;
-    margin: 10px;
-    color: white;
-    font-weight: bold;
-    font-size: 17px;
-    background-color: #FF6F00;
-    border-radius: 3px;
-    transition: all 0.1s linear;
-}
-
-div.apply-button:HOVER {
-    background-color: #FF7D19;
-}
-
-div.apply-button:ACTIVE {
-    background-color: #F06800;
-}
-
-div.apply-button.progress {
-    background-color: #FF6F00;
-}
-
-img.apply-progress {
-    margin-top: 3px;
-}
-
-div.settings-top-bar.open select.video-device {
-    display: inline;
-}
-
-div.check-box.section {
-    margin: 0px;
-    float: left;
-}
-
-div.check-box.section div.check-box-button {
-    background-color: #515151;
-}
-
-div.check-box.on.section div.check-box-button {
-    background-color: #3498db;
-}
-
-input[type=text].working-schedule.number {
-    width: 50px;
-}
-
-
-    /* media queries */
-
-@media all and (max-width: 1440px) {
-    /* smaller screens */
-    
-    body {
-        font-size: 17px;
-    }
-}
-
-@media all and (max-width: 1000px) {
-    /* small screens (mobile devices) */
-    
-    div.page-container.stretched {
-        margin-left: 0px;
-    }
-    
-    div.settings.open {
-        box-shadow: 0px 0px 10px rgba(0,0,0,0.5);
-        background-color: rgba(40, 40, 40, 0.9);
-    }
-}
-
-@media all and (max-width: 400px) {
-    /* very small screens */
-    
-    body {
-        font-size: 13px;
-    }
-}
-
-
-    /* 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 6214374cf865fb1fb9f2edfdcd11575bd648e088..a4994e20cf32fec81e7311c6b599163fdf3452bc 100644 (file)
@@ -1,4 +1,232 @@
 
+
+    /* fonts */
+
+@font-face {
+    font-family: 'Maven Pro';
+    src: url('../fnt/mavenpro-regular-webfont.eot');
+    src: url('../fnt/mavenpro-regular-webfont.eot?#iefix') format('embedded-opentype'),
+         url('../fnt/mavenpro-regular-webfont.woff') format('woff'),
+         url('../fnt/mavenpro-regular-webfont.ttf') format('truetype'),
+         url('../fnt/mavenpro-regular-webfont.svg#maven_proregular') format('svg');
+    font-weight: normal;
+    font-style: normal;
+}
+@font-face {
+    font-family: 'Maven Pro';
+    src: url('../fnt/mavenpro-bold-webfont.eot');
+    src: url('../fnt/mavenpro-bold-webfont.eot?#iefix') format('embedded-opentype'),
+         url('../fnt/mavenpro-bold-webfont.woff') format('woff'),
+         url('../fnt/mavenpro-bold-webfont.ttf') format('truetype'),
+         url('../fnt/mavenpro-bold-webfont.svg#maven_probold') format('svg');
+    font-weight: bold;
+    font-style: normal;
+}
+
+    /* layout */
+
+html {
+    font-family: 'Maven Pro';
+}
+
+div.page,
+div.header-container {
+    position: relative;
+    min-width: 320px;
+    width: 100%;
+}
+
+div.page {
+    margin-top: 50px;
+    padding-bottom: 20px;
+    font-size: 1em;
+    transition: all 0.5s linear;
+}
+
+div.header {
+    background-color: rgba(64, 64, 64, 0.5);
+    top: 0px;
+    width: 100%;
+    height: 50px;
+    position: fixed;
+    overflow: hidden;
+    z-index: 10000;
+}
+
+div.header-container {
+    transition: all 0.5s linear;
+}
+
+div.footer {
+    position: absolute;
+    bottom: 0px;
+    width: 100%;
+    height: 20px;
+    font-size: 0.5em;
+    color: #aaa;
+    text-align: center;
+}
+
+div.page-container {
+    transition: all 0.2s linear;
+    padding: 5px;
+}
+
+div.page-container.stretched {
+    margin-left: 40%;
+}
+
+
+    /* settings */
+
+div.settings {
+    background-color: #313131;
+    position: fixed;
+    z-index: 1;
+    top: 50px;
+    left: 0px;
+    width: 0px;
+    height: 100%;
+    transition: all 0.2s linear;
+    overflow: auto;
+}
+
+div.settings.open {
+    width: 40%;
+    min-width: 320px;
+}
+
+div.settings-container {
+    padding-top: 10px;
+    padding-bottom: 60px;
+    display: none;
+    white-space: nowrap;
+}
+
+div.settings.open div.settings-container {
+    display: block;
+}
+
+div.settings-top-bar {
+    position: relative;
+    display: inline-block;
+    width: 40%;
+    height: 50px;
+}
+
+div.settings-top-bar.open {
+    background-color: #414141;
+    min-width: 320px;
+}
+
+div.settings-section-title {
+    position: relative;
+    text-align: right;
+    background-color: rgba(100, 100, 100, 0.3);
+    padding: 5px;
+}
+
+table.settings {
+    width: 100%;
+    padding: 0.5em 0.5em 1em 0.5em;
+}
+
+td.settings-item-label {
+    width: 50%;
+    text-align: right;
+    padding-right: 5px;
+}
+
+td.settings-item-value {
+    width: 50%;
+    text-align: left;
+    padding-left: 5px;
+}
+
+span.settings-item-label {
+    font-size: 0.9em;
+}
+
+span.settings-item-unit {
+    font-size: 0.6em;
+    padding: 0px 0.2em;
+}
+
+div.settings-item-separator {
+    height: 1px;
+    border-top: 1px solid #414141;
+    margin: 5px 0px;
+}
+
+select.video-device {
+    display: none;
+    padding: 4px 1.5em 4px 4px;
+    vertical-align: middle;
+    font-size: 20px;
+    width: auto;
+    max-width: 40%;
+}
+
+div.apply-button {
+    position: relative;
+    display: none;
+    opacity: 0;
+    float: right;
+    width: 80px;
+    height: 30px;
+    line-height: 30px;
+    text-align: center;
+    margin: 10px;
+    color: white;
+    font-weight: bold;
+    font-size: 17px;
+    background-color: #FF6F00;
+    border-radius: 3px;
+    transition: all 0.1s linear;
+}
+
+div.apply-button:HOVER {
+    background-color: #FF7D19;
+}
+
+div.apply-button:ACTIVE {
+    background-color: #F06800;
+}
+
+div.apply-button.progress {
+    background-color: #FF6F00;
+}
+
+img.apply-progress {
+    margin-top: 3px;
+}
+
+div.settings-top-bar.open select.video-device {
+    display: inline;
+}
+
+div.check-box.section {
+    margin: 0px;
+    float: left;
+}
+
+div.check-box.section div.check-box-button {
+    background-color: #515151;
+}
+
+div.check-box.on.section div.check-box-button {
+    background-color: #3498db;
+}
+
+input[type=text].working-schedule.number {
+    width: 50px;
+}
+
+
+    /* camera frames */
+
 div.video-list {
     text-align: center;
 }
@@ -71,6 +299,35 @@ img.video {
 
     /* media queries */
 
+@media all and (max-width: 1440px) {
+    /* smaller screens */
+    
+    body {
+        font-size: 17px;
+    }
+}
+
+@media all and (max-width: 1000px) {
+    /* small screens (mobile devices) */
+    
+    div.page-container.stretched {
+        margin-left: 0px;
+    }
+    
+    div.settings.open {
+        box-shadow: 0px 0px 10px rgba(0,0,0,0.5);
+        background-color: rgba(40, 40, 40, 0.9);
+    }
+}
+
+@media all and (max-width: 400px) {
+    /* very small screens */
+    
+    body {
+        font-size: 13px;
+    }
+}
+
 @media all and (max-width: 1900px) {
     /* a bit smaller screens */
     
@@ -86,3 +343,41 @@ 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/base-site.js b/static/js/base-site.js
deleted file mode 100644 (file)
index bd3ab37..0000000
+++ /dev/null
@@ -1,691 +0,0 @@
-
-var pushConfigs = {};
-
-
-    /* utils */
-
-function ajax(method, url, data, callback) {
-    var options = {
-        type: method,
-        url: url,
-        data: data,
-        cache: false,
-        success: callback,
-        error: function (request, options, error) {
-            alert('Request failed with code: ' + request.status);
-            if (callback) {
-                callback();
-            }
-        }
-    };
-    
-    if (data && typeof data === 'object') {
-        options['contentType'] = 'application/json';
-        options['data'] = JSON.stringify(options['data']);
-    }
-    
-    $.ajax(options);
-}
-
-Object.keys = Object.keys || (function () {
-    var hasOwnProperty = Object.prototype.hasOwnProperty;
-    var hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString');
-    var dontEnums = [
-        'toString',
-        'toLocaleString',
-        'valueOf',
-        'hasOwnProperty',
-        'isPrototypeOf',
-        'propertyIsEnumerable',
-        'constructor'
-    ];
-    var dontEnumsLength = dontEnums.length;
-
-    return function (obj) {
-        if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) {
-            return [];
-        }
-
-        var result = [];
-        for (var prop in obj) {
-            if (hasOwnProperty.call(obj, prop)) {
-                result.push(prop);
-            }
-        }
-
-        if (hasDontEnumBug) {
-            for (var i = 0; i < dontEnumsLength; i++) {
-                if (hasOwnProperty.call(obj, dontEnums[i])) {
-                    result.push(dontEnums[i]);
-                }
-            }
-        }
-        
-        return result;
-    };
-})();
-
-
-    /* UI */
-
-function initUI() {
-    /* checkboxes */
-    $('input[type=checkbox].styled').each(function () {
-        makeCheckBox($(this));
-    });
-
-    /* sliders */
-    makeSlider($('#brightnessSlider'), 0, 100, 0, null, 5, 0, '%');
-    makeSlider($('#contrastSlider'), 0, 100, 0, null, 5, 0, '%');
-    makeSlider($('#saturationSlider'), 0, 100, 0, null, 5, 0, '%');
-    makeSlider($('#hueSlider'), 0, 100, 0, null, 5, 0, '%');
-    makeSlider($('#framerateSlider'), 1, 30, 0, [
-        {value: 1, label: '1'},
-        {value: 5, label: '5'},
-        {value: 10, label: '10'},
-        {value: 15, label: '15'},
-        {value: 20, label: '20'},
-        {value: 25, label: '25'},
-        {value: 30, label: '30'}
-    ], null, 0);
-    makeSlider($('#streamingFramerateSlider'), 1, 30, 0, [
-        {value: 1, label: '1'},
-        {value: 5, label: '5'},
-        {value: 10, label: '10'},
-        {value: 15, label: '15'},
-        {value: 20, label: '20'},
-        {value: 25, label: '25'},
-        {value: 30, label: '30'}
-    ], null, 0);
-    makeSlider($('#streamingQualitySlider'), 0, 100, 0, null, 5, 0, '%');
-    makeSlider($('#imageQualitySlider'), 0, 100, 0, null, 5, 0, '%');
-    makeSlider($('#movieQualitySlider'), 0, 100, 0, null, 5, 0, '%');
-    makeSlider($('#frameChangeThresholdSlider'), 0, 10000, 0, null, 3, 0, 'px');
-    makeSlider($('#noiseLevelSlider'), 0, 100, 0, null, 5, 0, '%');
-    
-    /* text validators */
-    makeTextValidator($('#adminUsernameEntry'), true);
-    makeTextValidator($('#adminPasswordEntry'), true);
-    makeTextValidator($('#normalUsernameEntry'), true);
-    makeTextValidator($('#normalPasswordEntry'), true);
-    makeTextValidator($('#deviceNameEntry'), true);
-    makeTextValidator($('#networkServerEntry'), true);
-    makeTextValidator($('#networkShareNameEntry'), true);
-    makeTextValidator($('#networkUsernameEntry'), false);
-    makeTextValidator($('#networkPasswordEntry'), false);
-    makeTextValidator($('#rootDirectoryEntry'), true);
-    makeTextValidator($('#leftTextEntry'), true);
-    makeTextValidator($('#rightTextEntry'), true);
-    makeTextValidator($('#imageFileNameEntry'), true);
-    makeTextValidator($('#movieFileNameEntry'), true);
-    makeTextValidator($('#emailAddressesEntry'), true);
-    
-    /* number validators */
-    makeNumberValidator($('#streamingPortEntry'), 1024, 65535, false, false, true);
-    makeNumberValidator($('#snapshotIntervalEntry'), 1, 86400, false, false, true);
-    makeNumberValidator($('#gapEntry'), 1, 86400, false, false, true);
-    makeNumberValidator($('#preCaptureEntry'), 0, 100, false, false, true);
-    makeNumberValidator($('#postCaptureEntry'), 0, 100, false, false, true);
-    
-    /* time validators */
-    makeTimeValidator($('#mondayFrom'));
-    makeTimeValidator($('#mondayTo'));
-    makeTimeValidator($('#tuesdayFrom'));
-    makeTimeValidator($('#tuesdayTo'));
-    makeTimeValidator($('#wednesdayFrom'));
-    makeTimeValidator($('#wednesdayTo'));
-    makeTimeValidator($('#thursdayFrom'));
-    makeTimeValidator($('#thursdayTo'));
-    makeTimeValidator($('#fridayFrom'));
-    makeTimeValidator($('#fridayTo'));
-    makeTimeValidator($('#saturdayFrom'));
-    makeTimeValidator($('#saturdayTo'));
-    makeTimeValidator($('#sundayFrom'));
-    makeTimeValidator($('#sundayTo'));
-    
-    /* ui elements that enable/disable other ui elements */
-    $('#motionEyeSwitch').change(updateConfigUi);
-    $('#showAdvancedSwitch').change(updateConfigUi);
-    $('#storageDeviceSelect').change(updateConfigUi);
-    $('#autoBrightnessSwitch').change(updateConfigUi);
-    $('#leftTextSelect').change(updateConfigUi);
-    $('#rightTextSelect').change(updateConfigUi);
-    $('#captureModeSelect').change(updateConfigUi);
-    $('#autoNoiseDetectSwitch').change(updateConfigUi);
-    $('#videoDeviceSwitch').change(updateConfigUi);
-    $('#textOverlaySwitch').change(updateConfigUi);
-    $('#videoStreamingSwitch').change(updateConfigUi);
-    $('#stillImagesSwitch').change(updateConfigUi);
-    $('#motionMoviesSwitch').change(updateConfigUi);
-    $('#motionNotificationsSwitch').change(updateConfigUi);
-    $('#workingScheduleSwitch').change(updateConfigUi);
-    
-    /* fetch & push handlers */
-    $('#videoDeviceSelect').change(fetchCameraConfig);
-    $('input.general').change(pushMainConfig);
-    $('input.device, select.device, ' +
-      'input.storage, select.storage, ' +
-      'input.text-overlay, select.text-overlay, ' + 
-      'input.streaming, select.streaming, ' +
-      'input.still-images, select.still-images, ' +
-      'input.motion-movies, select.motion-movies, ' +
-      'input.motion-detection, select.motion-detection, ' +
-      'input.notifications, select.notifications, ' +
-      'input.working-schedule, select.working-schedule').change(pushCameraConfig);
-    
-    /* apply button */
-    $('#applyButton').click(function () {
-        if ($(this).hasClass('progress')) {
-            return; /* in progress */
-        }
-        
-        doApply();
-    });
-}
-
-function updateConfigUi() {
-    var objs = $('tr.settings-item, div.advanced-setting, table.advanced-setting, div.settings-section-title, table.settings');
-    
-    function markHide() {
-        this._hide = true;
-    }
-    
-    function unmarkHide() {
-        this._hide = false;
-    }
-    
-    objs.each(unmarkHide);
-    
-    /* general enable switch */
-    var motionEyeEnabled = $('#motionEyeSwitch').get(0).checked;
-    if (!motionEyeEnabled) {
-        objs.not($('#motionEyeSwitch').parents('div').get(0)).each(markHide);
-    }
-    
-    /* advanced settings */
-    var showAdvanced = $('#showAdvancedSwitch').get(0).checked;
-    if (!showAdvanced) {
-        $('tr.advanced-setting, div.advanced-setting, table.advanced-setting').each(markHide);
-    }
-    
-    /* storage device */
-    if ($('#storageDeviceSelect').val() === 'local-disk') {
-        $('#networkServerEntry').parents('tr:eq(0)').each(markHide);
-        $('#networkUsernameEntry').parents('tr:eq(0)').each(markHide);
-        $('#networkPasswordEntry').parents('tr:eq(0)').each(markHide);
-        $('#networkShareNameEntry').parents('tr:eq(0)').each(markHide);
-    }
-    
-    /* auto brightness */
-    if ($('#autoBrightnessSwitch').get(0).checked) {
-        $('#brightnessSlider').parents('tr:eq(0)').each(markHide);
-    }
-    
-    /* text */
-    if ($('#leftTextSelect').val() !== 'custom-text') {
-        $('#leftTextEntry').parents('tr:eq(0)').each(markHide);
-    }
-    if ($('#rightTextSelect').val() !== 'custom-text') {
-        $('#rightTextEntry').parents('tr:eq(0)').each(markHide);
-    }
-    
-    /* still images capture mode */
-    if ($('#captureModeSelect').val() !== 'interval-snapshots') {
-        $('#snapshotIntervalEntry').parents('tr:eq(0)').each(markHide);
-    }
-    
-    /* auto noise level */
-    if ($('#autoNoiseDetectSwitch').get(0).checked) {
-        $('#noiseLevelSlider').parents('tr:eq(0)').each(markHide);
-    }
-    
-    /* video device switch */
-    if (!$('#videoDeviceSwitch').get(0).checked) {
-        $('#videoDeviceSwitch').parent().nextAll('div.settings-section-title, table.settings').each(markHide);
-    }
-    
-    /* text overlay switch */
-    if (!$('#textOverlaySwitch').get(0).checked) {
-        $('#textOverlaySwitch').parent().next('table.settings').find('tr.settings-item').each(markHide);
-    }
-    
-    /* video streaming switch */
-    if (!$('#videoStreamingSwitch').get(0).checked) {
-        $('#videoStreamingSwitch').parent().next('table.settings').find('tr.settings-item').each(markHide);
-    }
-    
-    /* still images switch */
-    if (!$('#stillImagesSwitch').get(0).checked) {
-        $('#stillImagesSwitch').parent().next('table.settings').find('tr.settings-item').each(markHide);
-    }
-    
-    /* motion movies switch */
-    if (!$('#motionMoviesSwitch').get(0).checked) {
-        $('#motionMoviesSwitch').parent().next('table.settings').find('tr.settings-item').each(markHide);
-    }
-    
-    /* motion notifications switch */
-    if (!$('#motionNotificationsSwitch').get(0).checked) {
-        $('#motionNotificationsSwitch').parent().next('table.settings').find('tr.settings-item').each(markHide);
-    }
-    
-    /* working schedule switch */
-    if (!$('#workingScheduleSwitch').get(0).checked) {
-        $('#workingScheduleSwitch').parent().next('table.settings').find('tr.settings-item').each(markHide);
-    }
-    
-    objs.each(function () {
-        if (this._hide) {
-            $(this).hide(200);
-        }
-        else {
-            $(this).show(200);
-        }
-    });
-    
-    /* re-validate all the input validators */
-    $('div.settings').find('input.text-validator, input.number-validator, input.time-validator').each(function () {
-        this.validate();
-    });
-    
-    /* update all checkboxes and sliders */
-    $('div.settings').find('input[type=checkbox], input.range').each(function () {
-        this.update();
-    });
-    
-    /* select the first option for the selects with no current selection */
-    $('div.settings').find('select').each(function () {
-        if (this.selectedIndex === -1) {
-            this.selectedIndex = 0;
-        }
-    });
-}
-
-function configUiValid() {
-    var valid = true;
-    $('div.settings input, select').each(function () {
-        if (this.invalid) {
-            valid = false;
-            return false;
-        }
-    });
-    
-    return valid;
-}
-
-function mainUi2Dict() {
-    return {
-        'enabled': $('#motionEyeSwitch')[0].checked,
-        'show_advanced': $('#showAdvancedSwitch')[0].checked,
-        'admin_username': $('#adminUsernameEntry').val(),
-        'admin_password': $('#adminPasswordEntry').val(),
-        'normal_username': $('#normalUsernameEntry').val(),
-        'normal_password': $('#normalPasswordEntry').val()
-    };
-}
-
-function dict2MainUi(dict) {
-    $('#motionEyeSwitch')[0].checked = dict['enabled'];
-    $('#showAdvancedSwitch')[0].checked = dict['show_advanced'];
-    $('#adminUsernameEntry').val(dict['admin_username']);
-    $('#adminPasswordEntry').val(dict['admin_password']);
-    $('#normalUsernameEntry').val(dict['normal_username']);
-    $('#normalPasswordEntry').val(dict['normal_password']);
-    
-    updateConfigUi();
-}
-
-function cameraUi2Dict() {
-    return {
-        /* video device */
-        'enabled': $('#videoDeviceSwitch')[0].checked,
-        'name': $('#deviceNameEntry').val(),
-        'device': $('#deviceEntry').val(),
-        'light_switch_detect': $('#lightSwitchDetectSwitch')[0].checked,
-        'auto_brightness': $('#autoBrightnessSwitch')[0].checked,
-        'brightness': $('#brightnessSlider').val(),
-        'contrast': $('#contrastSlider').val(),
-        'saturation': $('#saturationSlider').val(),
-        'hue': $('#hueSlider').val(),
-        'resolution': $('#resolutionSelect').val(),
-        'rotation': $('#rotationSelect').val(),
-        'framerate': $('#framerateSlider').val(),
-        
-        /* file storage */
-        'storage_device': $('#storageDeviceSelect').val(),
-        'network_server': $('#networkServerEntry').val(),
-        'network_share_name': $('#networkShareNameEntry').val(),
-        'network_username': $('#networkUsernameEntry').val(),
-        'network_password': $('#networkPasswordEntry').val(),
-        'root_directory': $('#rootDirectoryEntry').val(),
-        
-        /* text overlay */
-        'text_overlay': $('#textOverlaySwitch')[0].checked,
-        'left_text': $('#leftTextSelect').val(),
-        'custom_left_text': $('#leftTextEntry').val(),
-        'right_text': $('#rightTextSelect').val(),
-        'custom_right_text': $('#rightTextEntry').val(),
-        
-        /* video streaming */
-        'video_streaming': $('#videoStreamingSwitch')[0].checked,
-        'streaming_port': $('#streamingPortEntry').val(),
-        'streaming_framerate': $('#streamingFramerateSlider').val(),
-        'streaming_quality': $('#streamingQualitySlider').val(),
-        'streaming_motion': $('#streamingMotion')[0].checked,
-        
-        /* still images */
-        'still_images': $('#stillImagesSwitch')[0].checked,
-        'image_file_name': $('#imageFileNameEntry').val(),
-        'image_quality': $('#imageQualitySlider').val(),
-        'capture_mode': $('#captureModeSelect').val(),
-        'snapshot_interval': $('#snapshotIntervalEntry').val(),
-        'preserve_images': $('#preserveImagesSelect').val(),
-        
-        /* motion movies */
-        'motion_movies': $('#motionMoviesSwitch')[0].checked,
-        'movie_file_name': $('#movieFileNameEntry').val(),
-        'movie_quality': $('#movieQualitySlider').val(),
-        'preserve_movies': $('#preserveMoviesSelect').val(),
-        
-        /* motion detection */
-        'show_frame_changes': $('#showFrameChangesSwitch')[0].checked,
-        'frame_change_threshold': $('#frameChangeThresholdSlider').val(),
-        'auto_noise_detect': $('#autoNoiseDetectSwitch')[0].checked,
-        'noise_level': $('#noiseLevelSlider').val(),
-        'gap': $('#gapEntry').val(),
-        'pre_capture': $('#preCaptureEntry').val(),
-        'post_capture': $('#postCaptureEntry').val(),
-        
-        /* motion notifications */
-        'motion_notifications': $('#motionNotificationsSwitch')[0].checked,
-        'motion_notifications_emails': $('#emailAddressesEntry').val(),
-        
-        /* working schedule */
-        'working_schedule': $('#workingScheduleSwitch')[0].checked,
-        'monday_from': $('#mondayFrom').val(),
-        'monday_to':$('#mondayTo').val(),
-        'tuesday_from': $('#tuesdayFrom').val(),
-        'tuesday_to': $('#tuesdayTo').val(),
-        'wednesday_from': $('#wednesdayFrom').val(),
-        'wednesday_to': $('#wednesdayTo').val(),
-        'thursday_from': $('#thursdayFrom').val(),
-        'thursday_to': $('#thursdayTo').val(),
-        'friday_from':$('#fridayFrom').val(),
-        'friday_to': $('#fridayTo').val(),
-        'saturday_from':$('#saturdayFrom').val(),
-        'saturday_to': $('#saturdayTo').val(),
-        'sunday_from': $('#sundayFrom').val(),
-        'sunday_to': $('#sundayTo').val(),
-    };
-}
-
-function dict2CameraUi(dict) {
-    /* video device */
-    $('#videoDeviceSwitch')[0].checked = dict['enabled'];
-    $('#deviceNameEntry').val(dict['name']);
-    $('#deviceEntry').val(dict['device']);
-    $('#lightSwitchDetectSwitch')[0].checked = dict['light_switch_detect'];
-    $('#autoBrightnessSwitch')[0].checked = dict['auto_brightness'];
-    $('#brightnessSlider').val(dict['brightness']);
-    $('#contrastSlider').val(dict['contrast']);
-    $('#saturationSlider').val(dict['saturation']);
-    $('#hueSlider').val(dict['hue']);
-    $('#resolutionSelect').val(dict['resolution']);
-    $('#rotationSelect').val(dict['rotation']);
-    $('#framerateSlider').val(dict['framerate']);
-    
-    /* file storage */
-    $('#storageDeviceSelect').val(dict['storage_device']);
-    $('#networkServerEntry').val(dict['network_server']);
-    $('#networkShareNameEntry').val(dict['network_share_name']);
-    $('#networkUsernameEntry').val(dict['network_username']);
-    $('#networkPasswordEntry').val(dict['network_password']);
-    $('#rootDirectoryEntry').val(dict['root_directory']);
-    
-    /* text overlay */
-    $('#textOverlaySwitch')[0].checked = dict['text_overlay'];
-    $('#leftTextSelect').val(dict['left_text']);
-    $('#leftTextEntry').val(dict['custom_left_text']);
-    $('#rightTextSelect').val(dict['right_text']);
-    $('#rightTextEntry').val(dict['custom_right_text']);
-    
-    /* video streaming */
-    $('#videoStreamingSwitch')[0].checked = dict['video_streaming'];
-    $('#streamingPortEntry').val(dict['streaming_port']);
-    $('#streamingFramerateSlider').val(dict['streaming_framerate']);
-    $('#streamingQualitySlider').val(dict['streaming_quality']);
-    $('#streamingMotion')[0].checked = dict['streaming_motion'];
-    
-    /* still images */
-    $('#stillImagesSwitch')[0].checked = dict['still_images'];
-    $('#imageFileNameEntry').val(dict['image_file_name']);
-    $('#imageQualitySlider').val(dict['image_quality']);
-    $('#captureModeSelect').val(dict['capture_mode']);
-    $('#snapshotIntervalEntry').val(dict['snapshot_interval']);
-    $('#preserveImagesSelect').val(dict['preserve_images']);
-    
-    /* motion movies */
-    $('#motionMoviesSwitch')[0].checked = dict['motion_movies'];
-    $('#movieFileNameEntry').val(dict['movie_file_name']);
-    $('#movieQualitySlider').val(dict['movie_quality']);
-    $('#preserveMoviesSelect').val(dict['preserve_movies']);
-    
-    /* motion detection */
-    $('#showFrameChangesSwitch')[0].checked = dict['show_frame_changes'];
-    $('#frameChangeThresholdSlider').val(dict['frame_change_threshold']);
-    $('#autoNoiseDetectSwitch')[0].checked = dict['auto_noise_detect'];
-    $('#noiseLevelSlider').val(dict['noise_level']);
-    $('#gapEntry').val(dict['gap']);
-    $('#preCaptureEntry').val(dict['pre_capture']);
-    $('#postCaptureEntry').val(dict['post_capture']);
-    
-    /* motion notifications */
-    $('#motionNotificationsSwitch')[0].checked = dict['motion_notifications'];
-    $('#emailAddressesEntry').val(dict['motion_notifications_emails']);
-    
-    /* working schedule */
-    $('#workingScheduleSwitch')[0].checked = dict['working_schedule'];
-    $('#mondayFrom').val(dict['monday_from']);
-    $('#mondayTo').val(dict['monday_to']);
-    $('#tuesdayFrom').val(dict['tuesday_from']);
-    $('#tuesdayTo').val(dict['tuesday_to']);
-    $('#wednesdayFrom').val(dict['wednesday_from']);
-    $('#wednesdayTo').val(dict['wednesday_to']);
-    $('#thursdayFrom').val(dict['thursday_from']);
-    $('#thursdayTo').val(dict['thursday_to']);
-    $('#fridayFrom').val(dict['friday_from']);
-    $('#fridayTo').val(dict['friday_to']);
-    $('#saturdayFrom').val(dict['saturday_from']);
-    $('#saturdayTo').val(dict['saturday_to']);
-    $('#sundayFrom').val(dict['sunday_from']);
-    $('#sundayTo').val(dict['sunday_to']);
-    
-    updateConfigUi();
-}
-
-    
-    /* apply button */
-
-function showApply() {
-    if (!$('div.settings-container').is(':visible')) {
-        return; /* settings panel is not open */
-    }
-
-    var applyButton = $('#applyButton');
-    
-    applyButton.html('Apply');
-    applyButton.css('display', 'inline-block');
-    applyButton.animate({'opacity': '1'}, 100);
-    applyButton.removeClass('progress');
-}
-
-function showProgress() {
-    if (!$('div.settings-container').is(':visible')) {
-        return; /* settings panel is not open */
-    }
-
-    var applyButton = $('#applyButton');
-    
-    if (applyButton.hasClass('progress')) {
-        return; /* progress already visible */
-    }
-    
-    applyButton.html('<img class="apply-progress" src="' + staticUrl + 'img/progress.gif">');
-    applyButton.css('display', 'inline-block');
-    applyButton.animate({'opacity': '1'}, 100);
-    applyButton.addClass('progress');
-}
-
-function hideApply() {
-    if (!$('div.settings-container').is(':visible')) {
-        return; /* settings panel is not open */
-    }
-
-    var applyButton = $('#applyButton');
-    
-    applyButton.animate({'opacity': '0'}, 200, function () {
-        applyButton.removeClass('progress');
-        applyButton.css('display', 'none');
-    });
-}
-
-function isProgress() {
-    var applyButton = $('#applyButton');
-    
-    return applyButton.hasClass('progress');
-}
-
-function isApplyVisible() {
-    var applyButton = $('#applyButton');
-    
-    return applyButton.is(':visible');
-}
-
-function doApply() {
-    var finishedCount = 0;
-    var configs = [];
-    
-    function testReady() {
-        if (finishedCount >= configs.length) {
-            if (Object.keys(pushConfigs).length === 0) {
-                hideApply();
-            }
-            else {
-                showApply();
-            }
-        }
-    }
-    
-    for (var key in pushConfigs) {
-        if (pushConfigs.hasOwnProperty(key)) {
-            configs.push({key: key, config: pushConfigs[key]});
-        }
-    }
-    
-    if (configs.length === 0) {
-        return;
-    }
-    
-    showProgress();
-    
-    for (var i = 0; i < configs.length; i++) {
-        var config = configs[i];
-        ajax('POST', '/config/' + config.key + '/set/', config.config, function () {
-            finishedCount++;
-            testReady();
-        });
-        
-        /* update the camera name in the device select */
-        if (config.key !== 'main') {
-            $('#videoDeviceSelect').find('option[value=' + config.key + ']').html(config.config.name);
-        }
-    }
-    
-    pushConfigs = {};
-}
-
-function fetchCurrentConfig() {
-    /* fetch the main configuration */
-    ajax('GET', '/config/main/get/', null, function (data) {
-        dict2MainUi(data);
-    });
-    
-    /* fetch the camera list */
-    ajax('GET', '/config/list/', null, function (data) {
-        var i, cameras = data.cameras;
-        var videoDeviceSelect = $('#videoDeviceSelect');
-        videoDeviceSelect.html('');
-        for (i = 0; i < cameras.length; i++) {
-            var camera = cameras[i];
-            videoDeviceSelect.append('<option value="' + camera['@id'] + '">' + camera['@name'] + '</option>');
-        }
-        
-        if (cameras.length) {
-            videoDeviceSelect[0].selectedIndex = 0;
-            fetchCameraConfig();
-        }
-    });
-}
-
-function fetchCameraConfig() {
-    var cameraId = $('#videoDeviceSelect').val();
-    if (cameraId != null) {
-        ajax('GET', '/config/' + cameraId + '/get/', null, function (data) {
-            dict2CameraUi(data);
-        });
-    }
-    else {
-        dict2CameraUi({});
-    }
-}
-
-function pushMainConfig() {
-    var mainConfig = mainUi2Dict();
-    
-    pushConfigs['main'] = mainConfig;
-    if (!isApplyVisible()) {
-        showApply();
-    }
-}
-
-function pushCameraConfig() {
-    var cameraConfig = cameraUi2Dict();
-    var cameraId = $('#videoDeviceSelect').val();
-
-    pushConfigs[cameraId] = cameraConfig;
-    if (!isApplyVisible()) {
-        showApply();
-    }
-}
-
-$(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');
-        }
-        else {
-            $('div.settings').addClass('open');
-            $('div.page-container').addClass('stretched');
-            $('div.settings-top-bar').addClass('open');
-
-            updateConfigUi();
-        }
-    });
-    
-    /* prevent scroll events on settings div from propagating */
-    $('div.settings').mousewheel(function (e, d) {
-        var t = $(this);
-        if (d > 0 && t.scrollTop() === 0) {
-            e.preventDefault();
-        }
-        else if (d < 0 && (t.scrollTop() === t.get(0).scrollHeight - t.innerHeight())) {
-            e.preventDefault();
-        }
-    });
-    
-    initUI();
-    fetchCurrentConfig();
-});
diff --git a/static/js/main.js b/static/js/main.js
new file mode 100644 (file)
index 0000000..bd3ab37
--- /dev/null
@@ -0,0 +1,691 @@
+
+var pushConfigs = {};
+
+
+    /* utils */
+
+function ajax(method, url, data, callback) {
+    var options = {
+        type: method,
+        url: url,
+        data: data,
+        cache: false,
+        success: callback,
+        error: function (request, options, error) {
+            alert('Request failed with code: ' + request.status);
+            if (callback) {
+                callback();
+            }
+        }
+    };
+    
+    if (data && typeof data === 'object') {
+        options['contentType'] = 'application/json';
+        options['data'] = JSON.stringify(options['data']);
+    }
+    
+    $.ajax(options);
+}
+
+Object.keys = Object.keys || (function () {
+    var hasOwnProperty = Object.prototype.hasOwnProperty;
+    var hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString');
+    var dontEnums = [
+        'toString',
+        'toLocaleString',
+        'valueOf',
+        'hasOwnProperty',
+        'isPrototypeOf',
+        'propertyIsEnumerable',
+        'constructor'
+    ];
+    var dontEnumsLength = dontEnums.length;
+
+    return function (obj) {
+        if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) {
+            return [];
+        }
+
+        var result = [];
+        for (var prop in obj) {
+            if (hasOwnProperty.call(obj, prop)) {
+                result.push(prop);
+            }
+        }
+
+        if (hasDontEnumBug) {
+            for (var i = 0; i < dontEnumsLength; i++) {
+                if (hasOwnProperty.call(obj, dontEnums[i])) {
+                    result.push(dontEnums[i]);
+                }
+            }
+        }
+        
+        return result;
+    };
+})();
+
+
+    /* UI */
+
+function initUI() {
+    /* checkboxes */
+    $('input[type=checkbox].styled').each(function () {
+        makeCheckBox($(this));
+    });
+
+    /* sliders */
+    makeSlider($('#brightnessSlider'), 0, 100, 0, null, 5, 0, '%');
+    makeSlider($('#contrastSlider'), 0, 100, 0, null, 5, 0, '%');
+    makeSlider($('#saturationSlider'), 0, 100, 0, null, 5, 0, '%');
+    makeSlider($('#hueSlider'), 0, 100, 0, null, 5, 0, '%');
+    makeSlider($('#framerateSlider'), 1, 30, 0, [
+        {value: 1, label: '1'},
+        {value: 5, label: '5'},
+        {value: 10, label: '10'},
+        {value: 15, label: '15'},
+        {value: 20, label: '20'},
+        {value: 25, label: '25'},
+        {value: 30, label: '30'}
+    ], null, 0);
+    makeSlider($('#streamingFramerateSlider'), 1, 30, 0, [
+        {value: 1, label: '1'},
+        {value: 5, label: '5'},
+        {value: 10, label: '10'},
+        {value: 15, label: '15'},
+        {value: 20, label: '20'},
+        {value: 25, label: '25'},
+        {value: 30, label: '30'}
+    ], null, 0);
+    makeSlider($('#streamingQualitySlider'), 0, 100, 0, null, 5, 0, '%');
+    makeSlider($('#imageQualitySlider'), 0, 100, 0, null, 5, 0, '%');
+    makeSlider($('#movieQualitySlider'), 0, 100, 0, null, 5, 0, '%');
+    makeSlider($('#frameChangeThresholdSlider'), 0, 10000, 0, null, 3, 0, 'px');
+    makeSlider($('#noiseLevelSlider'), 0, 100, 0, null, 5, 0, '%');
+    
+    /* text validators */
+    makeTextValidator($('#adminUsernameEntry'), true);
+    makeTextValidator($('#adminPasswordEntry'), true);
+    makeTextValidator($('#normalUsernameEntry'), true);
+    makeTextValidator($('#normalPasswordEntry'), true);
+    makeTextValidator($('#deviceNameEntry'), true);
+    makeTextValidator($('#networkServerEntry'), true);
+    makeTextValidator($('#networkShareNameEntry'), true);
+    makeTextValidator($('#networkUsernameEntry'), false);
+    makeTextValidator($('#networkPasswordEntry'), false);
+    makeTextValidator($('#rootDirectoryEntry'), true);
+    makeTextValidator($('#leftTextEntry'), true);
+    makeTextValidator($('#rightTextEntry'), true);
+    makeTextValidator($('#imageFileNameEntry'), true);
+    makeTextValidator($('#movieFileNameEntry'), true);
+    makeTextValidator($('#emailAddressesEntry'), true);
+    
+    /* number validators */
+    makeNumberValidator($('#streamingPortEntry'), 1024, 65535, false, false, true);
+    makeNumberValidator($('#snapshotIntervalEntry'), 1, 86400, false, false, true);
+    makeNumberValidator($('#gapEntry'), 1, 86400, false, false, true);
+    makeNumberValidator($('#preCaptureEntry'), 0, 100, false, false, true);
+    makeNumberValidator($('#postCaptureEntry'), 0, 100, false, false, true);
+    
+    /* time validators */
+    makeTimeValidator($('#mondayFrom'));
+    makeTimeValidator($('#mondayTo'));
+    makeTimeValidator($('#tuesdayFrom'));
+    makeTimeValidator($('#tuesdayTo'));
+    makeTimeValidator($('#wednesdayFrom'));
+    makeTimeValidator($('#wednesdayTo'));
+    makeTimeValidator($('#thursdayFrom'));
+    makeTimeValidator($('#thursdayTo'));
+    makeTimeValidator($('#fridayFrom'));
+    makeTimeValidator($('#fridayTo'));
+    makeTimeValidator($('#saturdayFrom'));
+    makeTimeValidator($('#saturdayTo'));
+    makeTimeValidator($('#sundayFrom'));
+    makeTimeValidator($('#sundayTo'));
+    
+    /* ui elements that enable/disable other ui elements */
+    $('#motionEyeSwitch').change(updateConfigUi);
+    $('#showAdvancedSwitch').change(updateConfigUi);
+    $('#storageDeviceSelect').change(updateConfigUi);
+    $('#autoBrightnessSwitch').change(updateConfigUi);
+    $('#leftTextSelect').change(updateConfigUi);
+    $('#rightTextSelect').change(updateConfigUi);
+    $('#captureModeSelect').change(updateConfigUi);
+    $('#autoNoiseDetectSwitch').change(updateConfigUi);
+    $('#videoDeviceSwitch').change(updateConfigUi);
+    $('#textOverlaySwitch').change(updateConfigUi);
+    $('#videoStreamingSwitch').change(updateConfigUi);
+    $('#stillImagesSwitch').change(updateConfigUi);
+    $('#motionMoviesSwitch').change(updateConfigUi);
+    $('#motionNotificationsSwitch').change(updateConfigUi);
+    $('#workingScheduleSwitch').change(updateConfigUi);
+    
+    /* fetch & push handlers */
+    $('#videoDeviceSelect').change(fetchCameraConfig);
+    $('input.general').change(pushMainConfig);
+    $('input.device, select.device, ' +
+      'input.storage, select.storage, ' +
+      'input.text-overlay, select.text-overlay, ' + 
+      'input.streaming, select.streaming, ' +
+      'input.still-images, select.still-images, ' +
+      'input.motion-movies, select.motion-movies, ' +
+      'input.motion-detection, select.motion-detection, ' +
+      'input.notifications, select.notifications, ' +
+      'input.working-schedule, select.working-schedule').change(pushCameraConfig);
+    
+    /* apply button */
+    $('#applyButton').click(function () {
+        if ($(this).hasClass('progress')) {
+            return; /* in progress */
+        }
+        
+        doApply();
+    });
+}
+
+function updateConfigUi() {
+    var objs = $('tr.settings-item, div.advanced-setting, table.advanced-setting, div.settings-section-title, table.settings');
+    
+    function markHide() {
+        this._hide = true;
+    }
+    
+    function unmarkHide() {
+        this._hide = false;
+    }
+    
+    objs.each(unmarkHide);
+    
+    /* general enable switch */
+    var motionEyeEnabled = $('#motionEyeSwitch').get(0).checked;
+    if (!motionEyeEnabled) {
+        objs.not($('#motionEyeSwitch').parents('div').get(0)).each(markHide);
+    }
+    
+    /* advanced settings */
+    var showAdvanced = $('#showAdvancedSwitch').get(0).checked;
+    if (!showAdvanced) {
+        $('tr.advanced-setting, div.advanced-setting, table.advanced-setting').each(markHide);
+    }
+    
+    /* storage device */
+    if ($('#storageDeviceSelect').val() === 'local-disk') {
+        $('#networkServerEntry').parents('tr:eq(0)').each(markHide);
+        $('#networkUsernameEntry').parents('tr:eq(0)').each(markHide);
+        $('#networkPasswordEntry').parents('tr:eq(0)').each(markHide);
+        $('#networkShareNameEntry').parents('tr:eq(0)').each(markHide);
+    }
+    
+    /* auto brightness */
+    if ($('#autoBrightnessSwitch').get(0).checked) {
+        $('#brightnessSlider').parents('tr:eq(0)').each(markHide);
+    }
+    
+    /* text */
+    if ($('#leftTextSelect').val() !== 'custom-text') {
+        $('#leftTextEntry').parents('tr:eq(0)').each(markHide);
+    }
+    if ($('#rightTextSelect').val() !== 'custom-text') {
+        $('#rightTextEntry').parents('tr:eq(0)').each(markHide);
+    }
+    
+    /* still images capture mode */
+    if ($('#captureModeSelect').val() !== 'interval-snapshots') {
+        $('#snapshotIntervalEntry').parents('tr:eq(0)').each(markHide);
+    }
+    
+    /* auto noise level */
+    if ($('#autoNoiseDetectSwitch').get(0).checked) {
+        $('#noiseLevelSlider').parents('tr:eq(0)').each(markHide);
+    }
+    
+    /* video device switch */
+    if (!$('#videoDeviceSwitch').get(0).checked) {
+        $('#videoDeviceSwitch').parent().nextAll('div.settings-section-title, table.settings').each(markHide);
+    }
+    
+    /* text overlay switch */
+    if (!$('#textOverlaySwitch').get(0).checked) {
+        $('#textOverlaySwitch').parent().next('table.settings').find('tr.settings-item').each(markHide);
+    }
+    
+    /* video streaming switch */
+    if (!$('#videoStreamingSwitch').get(0).checked) {
+        $('#videoStreamingSwitch').parent().next('table.settings').find('tr.settings-item').each(markHide);
+    }
+    
+    /* still images switch */
+    if (!$('#stillImagesSwitch').get(0).checked) {
+        $('#stillImagesSwitch').parent().next('table.settings').find('tr.settings-item').each(markHide);
+    }
+    
+    /* motion movies switch */
+    if (!$('#motionMoviesSwitch').get(0).checked) {
+        $('#motionMoviesSwitch').parent().next('table.settings').find('tr.settings-item').each(markHide);
+    }
+    
+    /* motion notifications switch */
+    if (!$('#motionNotificationsSwitch').get(0).checked) {
+        $('#motionNotificationsSwitch').parent().next('table.settings').find('tr.settings-item').each(markHide);
+    }
+    
+    /* working schedule switch */
+    if (!$('#workingScheduleSwitch').get(0).checked) {
+        $('#workingScheduleSwitch').parent().next('table.settings').find('tr.settings-item').each(markHide);
+    }
+    
+    objs.each(function () {
+        if (this._hide) {
+            $(this).hide(200);
+        }
+        else {
+            $(this).show(200);
+        }
+    });
+    
+    /* re-validate all the input validators */
+    $('div.settings').find('input.text-validator, input.number-validator, input.time-validator').each(function () {
+        this.validate();
+    });
+    
+    /* update all checkboxes and sliders */
+    $('div.settings').find('input[type=checkbox], input.range').each(function () {
+        this.update();
+    });
+    
+    /* select the first option for the selects with no current selection */
+    $('div.settings').find('select').each(function () {
+        if (this.selectedIndex === -1) {
+            this.selectedIndex = 0;
+        }
+    });
+}
+
+function configUiValid() {
+    var valid = true;
+    $('div.settings input, select').each(function () {
+        if (this.invalid) {
+            valid = false;
+            return false;
+        }
+    });
+    
+    return valid;
+}
+
+function mainUi2Dict() {
+    return {
+        'enabled': $('#motionEyeSwitch')[0].checked,
+        'show_advanced': $('#showAdvancedSwitch')[0].checked,
+        'admin_username': $('#adminUsernameEntry').val(),
+        'admin_password': $('#adminPasswordEntry').val(),
+        'normal_username': $('#normalUsernameEntry').val(),
+        'normal_password': $('#normalPasswordEntry').val()
+    };
+}
+
+function dict2MainUi(dict) {
+    $('#motionEyeSwitch')[0].checked = dict['enabled'];
+    $('#showAdvancedSwitch')[0].checked = dict['show_advanced'];
+    $('#adminUsernameEntry').val(dict['admin_username']);
+    $('#adminPasswordEntry').val(dict['admin_password']);
+    $('#normalUsernameEntry').val(dict['normal_username']);
+    $('#normalPasswordEntry').val(dict['normal_password']);
+    
+    updateConfigUi();
+}
+
+function cameraUi2Dict() {
+    return {
+        /* video device */
+        'enabled': $('#videoDeviceSwitch')[0].checked,
+        'name': $('#deviceNameEntry').val(),
+        'device': $('#deviceEntry').val(),
+        'light_switch_detect': $('#lightSwitchDetectSwitch')[0].checked,
+        'auto_brightness': $('#autoBrightnessSwitch')[0].checked,
+        'brightness': $('#brightnessSlider').val(),
+        'contrast': $('#contrastSlider').val(),
+        'saturation': $('#saturationSlider').val(),
+        'hue': $('#hueSlider').val(),
+        'resolution': $('#resolutionSelect').val(),
+        'rotation': $('#rotationSelect').val(),
+        'framerate': $('#framerateSlider').val(),
+        
+        /* file storage */
+        'storage_device': $('#storageDeviceSelect').val(),
+        'network_server': $('#networkServerEntry').val(),
+        'network_share_name': $('#networkShareNameEntry').val(),
+        'network_username': $('#networkUsernameEntry').val(),
+        'network_password': $('#networkPasswordEntry').val(),
+        'root_directory': $('#rootDirectoryEntry').val(),
+        
+        /* text overlay */
+        'text_overlay': $('#textOverlaySwitch')[0].checked,
+        'left_text': $('#leftTextSelect').val(),
+        'custom_left_text': $('#leftTextEntry').val(),
+        'right_text': $('#rightTextSelect').val(),
+        'custom_right_text': $('#rightTextEntry').val(),
+        
+        /* video streaming */
+        'video_streaming': $('#videoStreamingSwitch')[0].checked,
+        'streaming_port': $('#streamingPortEntry').val(),
+        'streaming_framerate': $('#streamingFramerateSlider').val(),
+        'streaming_quality': $('#streamingQualitySlider').val(),
+        'streaming_motion': $('#streamingMotion')[0].checked,
+        
+        /* still images */
+        'still_images': $('#stillImagesSwitch')[0].checked,
+        'image_file_name': $('#imageFileNameEntry').val(),
+        'image_quality': $('#imageQualitySlider').val(),
+        'capture_mode': $('#captureModeSelect').val(),
+        'snapshot_interval': $('#snapshotIntervalEntry').val(),
+        'preserve_images': $('#preserveImagesSelect').val(),
+        
+        /* motion movies */
+        'motion_movies': $('#motionMoviesSwitch')[0].checked,
+        'movie_file_name': $('#movieFileNameEntry').val(),
+        'movie_quality': $('#movieQualitySlider').val(),
+        'preserve_movies': $('#preserveMoviesSelect').val(),
+        
+        /* motion detection */
+        'show_frame_changes': $('#showFrameChangesSwitch')[0].checked,
+        'frame_change_threshold': $('#frameChangeThresholdSlider').val(),
+        'auto_noise_detect': $('#autoNoiseDetectSwitch')[0].checked,
+        'noise_level': $('#noiseLevelSlider').val(),
+        'gap': $('#gapEntry').val(),
+        'pre_capture': $('#preCaptureEntry').val(),
+        'post_capture': $('#postCaptureEntry').val(),
+        
+        /* motion notifications */
+        'motion_notifications': $('#motionNotificationsSwitch')[0].checked,
+        'motion_notifications_emails': $('#emailAddressesEntry').val(),
+        
+        /* working schedule */
+        'working_schedule': $('#workingScheduleSwitch')[0].checked,
+        'monday_from': $('#mondayFrom').val(),
+        'monday_to':$('#mondayTo').val(),
+        'tuesday_from': $('#tuesdayFrom').val(),
+        'tuesday_to': $('#tuesdayTo').val(),
+        'wednesday_from': $('#wednesdayFrom').val(),
+        'wednesday_to': $('#wednesdayTo').val(),
+        'thursday_from': $('#thursdayFrom').val(),
+        'thursday_to': $('#thursdayTo').val(),
+        'friday_from':$('#fridayFrom').val(),
+        'friday_to': $('#fridayTo').val(),
+        'saturday_from':$('#saturdayFrom').val(),
+        'saturday_to': $('#saturdayTo').val(),
+        'sunday_from': $('#sundayFrom').val(),
+        'sunday_to': $('#sundayTo').val(),
+    };
+}
+
+function dict2CameraUi(dict) {
+    /* video device */
+    $('#videoDeviceSwitch')[0].checked = dict['enabled'];
+    $('#deviceNameEntry').val(dict['name']);
+    $('#deviceEntry').val(dict['device']);
+    $('#lightSwitchDetectSwitch')[0].checked = dict['light_switch_detect'];
+    $('#autoBrightnessSwitch')[0].checked = dict['auto_brightness'];
+    $('#brightnessSlider').val(dict['brightness']);
+    $('#contrastSlider').val(dict['contrast']);
+    $('#saturationSlider').val(dict['saturation']);
+    $('#hueSlider').val(dict['hue']);
+    $('#resolutionSelect').val(dict['resolution']);
+    $('#rotationSelect').val(dict['rotation']);
+    $('#framerateSlider').val(dict['framerate']);
+    
+    /* file storage */
+    $('#storageDeviceSelect').val(dict['storage_device']);
+    $('#networkServerEntry').val(dict['network_server']);
+    $('#networkShareNameEntry').val(dict['network_share_name']);
+    $('#networkUsernameEntry').val(dict['network_username']);
+    $('#networkPasswordEntry').val(dict['network_password']);
+    $('#rootDirectoryEntry').val(dict['root_directory']);
+    
+    /* text overlay */
+    $('#textOverlaySwitch')[0].checked = dict['text_overlay'];
+    $('#leftTextSelect').val(dict['left_text']);
+    $('#leftTextEntry').val(dict['custom_left_text']);
+    $('#rightTextSelect').val(dict['right_text']);
+    $('#rightTextEntry').val(dict['custom_right_text']);
+    
+    /* video streaming */
+    $('#videoStreamingSwitch')[0].checked = dict['video_streaming'];
+    $('#streamingPortEntry').val(dict['streaming_port']);
+    $('#streamingFramerateSlider').val(dict['streaming_framerate']);
+    $('#streamingQualitySlider').val(dict['streaming_quality']);
+    $('#streamingMotion')[0].checked = dict['streaming_motion'];
+    
+    /* still images */
+    $('#stillImagesSwitch')[0].checked = dict['still_images'];
+    $('#imageFileNameEntry').val(dict['image_file_name']);
+    $('#imageQualitySlider').val(dict['image_quality']);
+    $('#captureModeSelect').val(dict['capture_mode']);
+    $('#snapshotIntervalEntry').val(dict['snapshot_interval']);
+    $('#preserveImagesSelect').val(dict['preserve_images']);
+    
+    /* motion movies */
+    $('#motionMoviesSwitch')[0].checked = dict['motion_movies'];
+    $('#movieFileNameEntry').val(dict['movie_file_name']);
+    $('#movieQualitySlider').val(dict['movie_quality']);
+    $('#preserveMoviesSelect').val(dict['preserve_movies']);
+    
+    /* motion detection */
+    $('#showFrameChangesSwitch')[0].checked = dict['show_frame_changes'];
+    $('#frameChangeThresholdSlider').val(dict['frame_change_threshold']);
+    $('#autoNoiseDetectSwitch')[0].checked = dict['auto_noise_detect'];
+    $('#noiseLevelSlider').val(dict['noise_level']);
+    $('#gapEntry').val(dict['gap']);
+    $('#preCaptureEntry').val(dict['pre_capture']);
+    $('#postCaptureEntry').val(dict['post_capture']);
+    
+    /* motion notifications */
+    $('#motionNotificationsSwitch')[0].checked = dict['motion_notifications'];
+    $('#emailAddressesEntry').val(dict['motion_notifications_emails']);
+    
+    /* working schedule */
+    $('#workingScheduleSwitch')[0].checked = dict['working_schedule'];
+    $('#mondayFrom').val(dict['monday_from']);
+    $('#mondayTo').val(dict['monday_to']);
+    $('#tuesdayFrom').val(dict['tuesday_from']);
+    $('#tuesdayTo').val(dict['tuesday_to']);
+    $('#wednesdayFrom').val(dict['wednesday_from']);
+    $('#wednesdayTo').val(dict['wednesday_to']);
+    $('#thursdayFrom').val(dict['thursday_from']);
+    $('#thursdayTo').val(dict['thursday_to']);
+    $('#fridayFrom').val(dict['friday_from']);
+    $('#fridayTo').val(dict['friday_to']);
+    $('#saturdayFrom').val(dict['saturday_from']);
+    $('#saturdayTo').val(dict['saturday_to']);
+    $('#sundayFrom').val(dict['sunday_from']);
+    $('#sundayTo').val(dict['sunday_to']);
+    
+    updateConfigUi();
+}
+
+    
+    /* apply button */
+
+function showApply() {
+    if (!$('div.settings-container').is(':visible')) {
+        return; /* settings panel is not open */
+    }
+
+    var applyButton = $('#applyButton');
+    
+    applyButton.html('Apply');
+    applyButton.css('display', 'inline-block');
+    applyButton.animate({'opacity': '1'}, 100);
+    applyButton.removeClass('progress');
+}
+
+function showProgress() {
+    if (!$('div.settings-container').is(':visible')) {
+        return; /* settings panel is not open */
+    }
+
+    var applyButton = $('#applyButton');
+    
+    if (applyButton.hasClass('progress')) {
+        return; /* progress already visible */
+    }
+    
+    applyButton.html('<img class="apply-progress" src="' + staticUrl + 'img/progress.gif">');
+    applyButton.css('display', 'inline-block');
+    applyButton.animate({'opacity': '1'}, 100);
+    applyButton.addClass('progress');
+}
+
+function hideApply() {
+    if (!$('div.settings-container').is(':visible')) {
+        return; /* settings panel is not open */
+    }
+
+    var applyButton = $('#applyButton');
+    
+    applyButton.animate({'opacity': '0'}, 200, function () {
+        applyButton.removeClass('progress');
+        applyButton.css('display', 'none');
+    });
+}
+
+function isProgress() {
+    var applyButton = $('#applyButton');
+    
+    return applyButton.hasClass('progress');
+}
+
+function isApplyVisible() {
+    var applyButton = $('#applyButton');
+    
+    return applyButton.is(':visible');
+}
+
+function doApply() {
+    var finishedCount = 0;
+    var configs = [];
+    
+    function testReady() {
+        if (finishedCount >= configs.length) {
+            if (Object.keys(pushConfigs).length === 0) {
+                hideApply();
+            }
+            else {
+                showApply();
+            }
+        }
+    }
+    
+    for (var key in pushConfigs) {
+        if (pushConfigs.hasOwnProperty(key)) {
+            configs.push({key: key, config: pushConfigs[key]});
+        }
+    }
+    
+    if (configs.length === 0) {
+        return;
+    }
+    
+    showProgress();
+    
+    for (var i = 0; i < configs.length; i++) {
+        var config = configs[i];
+        ajax('POST', '/config/' + config.key + '/set/', config.config, function () {
+            finishedCount++;
+            testReady();
+        });
+        
+        /* update the camera name in the device select */
+        if (config.key !== 'main') {
+            $('#videoDeviceSelect').find('option[value=' + config.key + ']').html(config.config.name);
+        }
+    }
+    
+    pushConfigs = {};
+}
+
+function fetchCurrentConfig() {
+    /* fetch the main configuration */
+    ajax('GET', '/config/main/get/', null, function (data) {
+        dict2MainUi(data);
+    });
+    
+    /* fetch the camera list */
+    ajax('GET', '/config/list/', null, function (data) {
+        var i, cameras = data.cameras;
+        var videoDeviceSelect = $('#videoDeviceSelect');
+        videoDeviceSelect.html('');
+        for (i = 0; i < cameras.length; i++) {
+            var camera = cameras[i];
+            videoDeviceSelect.append('<option value="' + camera['@id'] + '">' + camera['@name'] + '</option>');
+        }
+        
+        if (cameras.length) {
+            videoDeviceSelect[0].selectedIndex = 0;
+            fetchCameraConfig();
+        }
+    });
+}
+
+function fetchCameraConfig() {
+    var cameraId = $('#videoDeviceSelect').val();
+    if (cameraId != null) {
+        ajax('GET', '/config/' + cameraId + '/get/', null, function (data) {
+            dict2CameraUi(data);
+        });
+    }
+    else {
+        dict2CameraUi({});
+    }
+}
+
+function pushMainConfig() {
+    var mainConfig = mainUi2Dict();
+    
+    pushConfigs['main'] = mainConfig;
+    if (!isApplyVisible()) {
+        showApply();
+    }
+}
+
+function pushCameraConfig() {
+    var cameraConfig = cameraUi2Dict();
+    var cameraId = $('#videoDeviceSelect').val();
+
+    pushConfigs[cameraId] = cameraConfig;
+    if (!isApplyVisible()) {
+        showApply();
+    }
+}
+
+$(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');
+        }
+        else {
+            $('div.settings').addClass('open');
+            $('div.page-container').addClass('stretched');
+            $('div.settings-top-bar').addClass('open');
+
+            updateConfigUi();
+        }
+    });
+    
+    /* prevent scroll events on settings div from propagating */
+    $('div.settings').mousewheel(function (e, d) {
+        var t = $(this);
+        if (d > 0 && t.scrollTop() === 0) {
+            e.preventDefault();
+        }
+        else if (d < 0 && (t.scrollTop() === t.get(0).scrollHeight - t.innerHeight())) {
+            e.preventDefault();
+        }
+    });
+    
+    initUI();
+    fetchCurrentConfig();
+});
diff --git a/templates/base-site.html b/templates/base-site.html
deleted file mode 100644 (file)
index 6ac6e03..0000000
+++ /dev/null
@@ -1,425 +0,0 @@
-{% extends "base.html" %}
-
-{% block style %}
-    {{super()}}
-    <link rel="stylesheet" type="text/css" href="{{STATIC_URL}}css/ui.css" />
-    <link rel="stylesheet" type="text/css" href="{{STATIC_URL}}css/base-site.css" />
-{% endblock %}
-
-{% block script %}
-    {{super()}}
-    <script type="text/javascript" src="{{STATIC_URL}}js/ui.js"></script>
-    <script type="text/javascript" src="{{STATIC_URL}}js/base-site.js"></script>
-{% endblock %}
-
-{% block body %}
-    <div class="header">
-        <div class="header-container">
-            <div class="settings-top-bar">
-                <img class="settings-button" src="{{STATIC_URL}}img/settings.png" title="settings">
-                <select class="video-device styled" id="videoDeviceSelect"></select>
-                <div class="button apply-button" id="applyButton">Apply</div>
-            </div>
-            <div class="logo">
-                <a href="/">
-                    <span class="logo">motionEye</span>
-                    <img class="logo" src="{{STATIC_URL}}img/motioneye.png">
-                </a>
-            </div>
-        </div>
-    </div>
-    <div class="page">
-        <div class="settings">
-            <div class="settings-container">
-                <div class="settings-section-title"><input type="checkbox" class="styled section general" id="motionEyeSwitch">General Settings</div>
-                <table class="settings">
-                    <tr class="settings-item">
-                        <td class="settings-item-label"><span class="settings-item-label">Show Advanced Settings</span></td>
-                        <td class="settings-item-value"><input type="checkbox" class="styled general" id="showAdvancedSwitch"></td>
-                        <td><span class="help-mark" title="enable this to be able to access all the advanced settings">?</span></td>
-                    </tr>
-                    <tr class="settings-item">
-                        <td class="settings-item-label"><span class="settings-item-label">Administrator Username</span></td>
-                        <td class="settings-item-value"><input type="text" class="styled general" id="adminUsernameEntry"></td>
-                        <td><span class="help-mark" title="the username supplied to configure motionEye">?</span></td>
-                    </tr>
-                    <tr class="settings-item">
-                        <td class="settings-item-label"><span class="settings-item-label">Administrator Password</span></td>
-                        <td class="settings-item-value"><input type="password" class="styled general" id="adminPasswordEntry"></td>
-                        <td><span class="help-mark" title="administrator's password">?</span></td>
-                    </tr>
-                    <tr class="settings-item">
-                        <td class="settings-item-label"><span class="settings-item-label">Surveillance Username</span></td>
-                        <td class="settings-item-value"><input type="text" class="styled general" id="normalUsernameEntry"></td>
-                        <td><span class="help-mark" title="the username supplied for video surveillance">?</span></td>
-                    </tr>
-                    <tr class="settings-item">
-                        <td class="settings-item-label"><span class="settings-item-label">Surveillance Password</span></td>
-                        <td class="settings-item-value"><input type="password" class="styled general" id="normalPasswordEntry"></td>
-                        <td><span class="help-mark" title="the password for the surveillance user">?</span></td>
-                    </tr>
-                </table>
-                
-                <div class="settings-section-title"><input type="checkbox" class="styled section device" id="videoDeviceSwitch">Video Device</div>
-                <table class="settings">
-                    <tr class="settings-item">
-                        <td class="settings-item-label"><span class="settings-item-label">Camera Name</span></td>
-                        <td class="settings-item-value"><input type="text" class="styled device" id="deviceNameEntry" placeholder="camera name..."></td>
-                        <td><span class="help-mark" title="an alias for this camera device">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Camera Device</span></td>
-                        <td class="settings-item-value"><input type="text" class="styled device" id="deviceEntry" disabled="disabled"></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td colspan="100"><div class="settings-item-separator"></div></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Light Switch Detection</span></td>
-                        <td class="settings-item-value"><input type="checkbox" class="styled device" id="lightSwitchDetectSwitch"></td>
-                        <td><span class="help-mark" title="enable this if you want sudden changes in light to not be treated as motion">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Automatic Brightness</span></td>
-                        <td class="settings-item-value"><input type="checkbox" class="styled device" id="autoBrightnessSwitch"></td>
-                        <td><span class="help-mark" title="enables software automatic brightness (not needed for most cameras)">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Brightness</span></td>
-                        <td class="settings-item-value"><input type="text" class="range styled device" id="brightnessSlider"></td>
-                        <td><span class="help-mark" title="sets a desired brightness level for this camera">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Contrast</span></td>
-                        <td class="settings-item-value"><input type="text" class="range styled device" id="contrastSlider"></td>
-                        <td><span class="help-mark" title="sets a desired contrast level for this camera">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Saturation</span></td>
-                        <td class="settings-item-value"><input type="text" class="range styled device" id="saturationSlider"></td>
-                        <td><span class="help-mark" title="sets a desired saturation (color) level for this camera">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Hue</span></td>
-                        <td class="settings-item-value"><input type="text" class="range styled device" id="hueSlider"></td>
-                        <td><span class="help-mark" title="sets a desired hue (color) for this camera">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td colspan="100"><div class="settings-item-separator"></div></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Video Resolution</span></td>
-                        <td class="settings-item-value">
-                            <select class="video-resolution styled device" id="resolutionSelect">
-                            </select>
-                        </td>
-                        <td><span class="help-mark" title="the video resolution (larger values yield better quality but require larger storage space and bandwidth)">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Video Rotation</span></td>
-                        <td class="settings-item-value">
-                            <select class="video-resolution styled device" id="rotationSelect">
-                                <option>0&deg;</option>
-                                <option>90&deg;</option>
-                                <option>180&deg;</option>
-                                <option>270&deg;</option>
-                            </select>
-                            <td><span class="help-mark" title="use this to rotate the captured image, if your camera is not positioned correctly">?</span></td>
-                        </td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Frame Rate</span></td>
-                        <td class="settings-item-value"><input type="text" class="range styled device" id="framerateSlider"></td>
-                        <td><span class="help-mark" title="sets the number of frames captured by the camera every second (higher values yield smoother videos but require larger storage space and bandwidth)">?</span></td>
-                    </tr>
-                </table>
-                
-                <div class="settings-section-title advanced-setting">File Storage</div>
-                <table class="settings advanced-setting">
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Storage Device</span></td>
-                        <td class="settings-item-value">
-                            <select class="styled storage" id="storageDeviceSelect">
-                                <option value="local-disk">Local Disk</option>
-                                <option value="network-share">Network Share</option>
-                            </select>
-                        </td>
-                        <td><span class="help-mark" title="indicates the storage device where the image and video files will be saved">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Network Server</span></td>
-                        <td class="settings-item-value"><input type="text" class="styled storage" id="networkServerEntry"></td>
-                        <td><span class="help-mark" title="the address of the network server (IP address or hostname)">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Share Name</span></td>
-                        <td class="settings-item-value"><input type="text" class="styled storage" id="networkShareNameEntry"></td>
-                        <td><span class="help-mark" title="the name of the network share">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Share Username</span></td>
-                        <td class="settings-item-value"><input type="text" class="styled storage" id="networkUsernameEntry"></td>
-                        <td><span class="help-mark" title="the username to be supplied when accessing the network share (leave empty if no username is required)">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Share Password</span></td>
-                        <td class="settings-item-value"><input type="password" class="styled storage" id="networkPasswordEntry"></td>
-                        <td><span class="help-mark" title="the password required by the network share (leave empty if no password is required)">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Root Directory</span></td>
-                        <td class="settings-item-value"><input type="text" class="styled storage" id="rootDirectoryEntry"></td>
-                        <td><span class="help-mark" title="the root path (on the selected storage device) where the files will be saved">?</span></td>
-                    </tr>
-                </table>
-                
-                <div class="settings-section-title advanced-setting"><input type="checkbox" class="styled section text-overlay" id="textOverlaySwitch">Text Overlay</div>
-                <table class="settings advanced-setting">
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Left Text</span></td>
-                        <td class="settings-item-value">
-                            <select class="styled text-overlay" id="leftTextSelect">
-                                <option value="camera-name">Camera Name</option>
-                                <option value="timestamp">Timestamp</option>
-                                <option value="custom-text">Custom Text</option>
-                            </select>
-                        </td>
-                        <td><span class="help-mark" title="sets the text displayed on the movies and images, on the lower left corner">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"></td>
-                        <td class="settings-item-value"><input type="text" class="styled text-overlay" id="leftTextEntry" placeholder="custom text..."></td>
-                        <td><span class="help-mark" title="sets a custom left text; the following special tokens are accepted: %Y = year, %m = month, %d = date, %H = hour, %M = minute, %S = second, %T = HH:MM:SS, %q = frame number, \n = new line">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Right Text</span></td>
-                        <td class="settings-item-value">
-                            <select class="styled text-overlay" id="rightTextSelect">
-                                <option value="camera-name">Camera Name</option>
-                                <option value="timestamp">Timestamp</option>
-                                <option value="custom-text">Custom Text</option>
-                            </select>
-                        </td>
-                        <td><span class="help-mark" title="sets the text displayed on the movies and images, on the lower right corner">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"></td>
-                        <td class="settings-item-value"><input type="text" class="styled text-overlay" id="rightTextEntry" placeholder="custom text..."></td>
-                        <td><span class="help-mark" title="sets a custom right text; the following special tokens are accepted: %Y = year, %m = month, %d = date, %H = hour, %M = minute, %S = second, %T = HH:MM:SS, %q = frame number, \n = new line">?</span></td>
-                    </tr>
-                </table>
-
-                <div class="settings-section-title"><input type="checkbox" class="styled section streaming" id="videoStreamingSwitch">Video Streaming</div>
-                <table class="settings">
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Streaming Frame Rate</span></td>
-                        <td class="settings-item-value"><input type="text" class="range styled streaming" id="streamingFramerateSlider"></td>
-                        <td><span class="help-mark" title="sets the number of frames transmitted every second on the live streaming">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Streaming Quality</span></td>
-                        <td class="settings-item-value"><input type="text" class="range styled streaming" id="streamingQualitySlider"></td>
-                        <td><span class="help-mark" title="sets the live streaming quality (higher values yield a better video quality but require more bandwidth)">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Streaming Port</span></td>
-                        <td class="settings-item-value"><input type="text" class="styled streaming" id="streamingPortEntry"></td>
-                        <td><span class="help-mark" title="sets the TCP port on which the webcam streaming server listens">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Motion Optimization</span></td>
-                        <td class="settings-item-value"><input type="checkbox" class="styled streaming" id="streamingMotion"></td>
-                        <td><span class="help-mark" title="enable this if you want a lower frame rate for the live streaming when no motion is detected">?</span></td>
-                    </tr>
-                </table>
-                
-                <div class="settings-section-title"><input type="checkbox" class="styled section still-images" id="stillImagesSwitch">Still Images</div>
-                <table class="settings">
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Image File Name</span></td>
-                        <td class="settings-item-value"><input type="text" class="styled still-images" id="imageFileNameEntry" placeholder="file name pattern..."></td>
-                        <td><span class="help-mark" title="sets the name pattern for the image (JPEG) files; the following special tokens are accepted: %Y = year, %m = month, %d = date, %H = hour, %M = minute, %S = second, %q = frame number, / = subfolder">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Image Quality</span></td>
-                        <td class="settings-item-value"><input type="text" class="range styled still-images" id="imageQualitySlider"></td>
-                        <td><span class="help-mark" title="sets the JPEG image quality (higher values yield a better image quality but require more storage space)">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Capture Mode</span></td>
-                        <td class="settings-item-value">
-                            <select class="styled still-images" id="captureModeSelect">
-                                <option value="motion-triggered">Motion Triggered</option>
-                                <option value="interval-snapshots">Interval Snapshots</option>
-                                <option value="all-frames">All Frames</option>
-                            </select>
-                        </td>
-                        <td><span class="help-mark" title="sets the image capture mode: Motion Triggered = an image captured whenever motion is detected, Automated Snapshots = an image captured every x seconds, All Frames = saves each frame into an image file">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Snapshot Interval</span></td>
-                        <td class="settings-item-value"><input type="text" class="styled number still-images" id="snapshotIntervalEntry"><span class="settings-item-unit">seconds</span></td>
-                        <td><span class="help-mark" title="sets the interval (in seconds) for the automated snapshots">?</span></td>
-                    </tr>
-                    <tr class="settings-item">
-                        <td class="settings-item-label"><span class="settings-item-label">Preserve Images</span></td>
-                        <td class="settings-item-value">
-                            <select class="styled still-images" id="preserveImagesSelect">
-                                <option value="1">For One Day</option>
-                                <option value="7">For One Week</option>
-                                <option value="30">For One Month</option>
-                                <option value="365">For One Year</option>
-                                <option value="0">Forever</option>
-                            </select>
-                        </td>
-                        <td><span class="help-mark" title="images older than the specified duration are automatically deleted to free storage space">?</span></td>
-                    </tr>
-                </table>
-                
-                <div class="settings-section-title"><input type="checkbox" class="styled section motion-movies" id="motionMoviesSwitch">Motion Movies</div>
-                <table class="settings">
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Movie File Name</span></td>
-                        <td class="settings-item-value"><input type="text" class="styled motion-movies" id="movieFileNameEntry" placeholder="file name pattern..."></td>
-                        <td><span class="help-mark" title="sets the name pattern for the movie (MPEG) files; the following special tokens are accepted: %Y = year, %m = month, %d = date, %H = hour, %M = minute, %S = second, %q = frame number, / = subfolder">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Movie Quality</span></td>
-                        <td class="settings-item-value"><input type="text" class="range styled motion-movies" id="movieQualitySlider"></td>
-                        <td><span class="help-mark" title="sets the MPEG video quality (higher values yield a better video quality but require more storage space)">?</span></td>
-                    </tr>
-                    <tr class="settings-item">
-                        <td class="settings-item-label"><span class="settings-item-label">Preserve Movies</span></td>
-                        <td class="settings-item-value">
-                            <select class="styled motion-movies" id="preserveMoviesSelect">
-                                <option value="1">For One Day</option>
-                                <option value="7">For One Week</option>
-                                <option value="30">For One Month</option>
-                                <option value="365">For One Year</option>
-                                <option value="0">Forever</option>
-                            </select>
-                        </td>
-                        <td><span class="help-mark" title="movies older than the specified duration are automatically deleted to free storage space">?</span></td>
-                    </tr>
-                </table>
-                
-                <div class="settings-section-title advanced-setting">Motion Detection</div>
-                <table class="settings advanced-setting">
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Show Frame Changes</span></td>
-                        <td class="settings-item-value"><input type="checkbox" class="styled motion-detection" id="showFrameChangesSwitch"></td>
-                        <td><span class="help-mark" title="if this is enabled, frame changes (number of pixels as well as the changed area) are shown on the video; temporarily enable this option to help adjust the motion detection parameters">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Frame Change Threshold</span></td>
-                        <td class="settings-item-value"><input type="text" class="range styled motion-detection" id="frameChangeThresholdSlider"></td>
-                        <td><span class="help-mark" title="indicates the minimal number of pixels that must change between to frames in order for motion to be detected (smaller values give a more sensitive detection, but are prone to false positives)">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Automatic Noise Detection</span></td>
-                        <td class="settings-item-value"><input type="checkbox" class="styled motion-detection" id="autoNoiseDetectSwitch"></td>
-                        <td><span class="help-mark" title="enable this to automatically adjust the noise level">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Noise Level</span></td>
-                        <td class="settings-item-value"><input type="text" class="range styled motion-detection" id="noiseLevelSlider"></td>
-                        <td><span class="help-mark" title="manually sets the noise level to a fixed value">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td colspan="100"><div class="settings-item-separator"></div></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Motion Gap</span></td>
-                        <td class="settings-item-value"><input type="text" class="number styled motion-detection" id="gapEntry"><span class="settings-item-unit">seconds</span></td>
-                        <td><span class="help-mark" title="sets the number of seconds of silence (i.e. no motion) that mark the end of a motion event">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Captured Before</span></td>
-                        <td class="settings-item-value"><input type="text" class="number styled motion-detection" id="preCaptureEntry"><span class="settings-item-unit">frames</span></td>
-                        <td><span class="help-mark" title="sets the number of frames to be captured (and included in the movie) before a motion event is detected">?</span></td>
-                    </tr>
-                    <tr class="settings-item advanced-setting">
-                        <td class="settings-item-label"><span class="settings-item-label">Captured After</span></td>
-                        <td class="settings-item-value"><input type="text" class="number styled motion-detection" id="postCaptureEntry"><span class="settings-item-unit">frames</span></td>
-                        <td><span class="help-mark" title="sets the number of frames to be captured (and included in the movie) after a motion event is detected">?</span></td>
-                    </tr>
-                </table>
-                
-                <div class="settings-section-title"><input type="checkbox" class="styled section notifications" id="motionNotificationsSwitch">Motion Notifications</div>
-                <table class="settings">
-                    <tr class="settings-item">
-                        <td class="settings-item-label"><span class="settings-item-label">Email Addresses</span></td>
-                        <td class="settings-item-value"><input type="text" class="styled notifications" id="emailAddressesEntry" placeholder="email addresses..."></td>
-                        <td><span class="help-mark" title="email addresses (separated by comma) that are added here will receive notifications whenever a motion event is detected (leave empty to disable email notifications)">?</span></td>
-                    </tr>
-                </table>
-
-                <div class="settings-section-title"><input type="checkbox" class="styled section working-schedule" id="workingScheduleSwitch">Working Schedule</div>
-                <table class="settings">
-                    <tr class="settings-item">
-                        <td class="settings-item-label"><span class="settings-item-label">Monday</span></td>
-                        <td class="settings-item-value">
-                            <span class="settings-item-unit">from</span><input type="text" class="styled working-schedule time" id="mondayFrom">
-                            <span class="settings-item-unit">to</span><input type="text" class="styled working-schedule time" id="mondayTo">
-                        </td>
-                        <td><span class="help-mark" title="sets the working schedule time interval for Mondays">?</span></td>
-                    </tr>
-                    <tr class="settings-item">
-                        <td class="settings-item-label"><span class="settings-item-label">Tuesday</span></td>
-                        <td class="settings-item-value">
-                            <span class="settings-item-unit">from</span><input type="text" class="styled working-schedule time" id="tuesdayFrom">
-                            <span class="settings-item-unit">to</span><input type="text" class="styled working-schedule time" id="tuesdayTo">
-                        </td>
-                        <td><span class="help-mark" title="sets the working schedule time interval for Tuesdays">?</span></td>
-                    </tr>
-                    <tr class="settings-item">
-                        <td class="settings-item-label"><span class="settings-item-label">Wednesday</span></td>
-                        <td class="settings-item-value">
-                            <span class="settings-item-unit">from</span><input type="text" class="styled working-schedule time" id="wednesdayFrom">
-                            <span class="settings-item-unit">to</span><input type="text" class="styled working-schedule time" id="wednesdayTo">
-                        </td>
-                        <td><span class="help-mark" title="sets the working schedule time interval for Wednesdays">?</span></td>
-                    </tr>
-                    <tr class="settings-item">
-                        <td class="settings-item-label"><span class="settings-item-label">Thursday</span></td>
-                        <td class="settings-item-value">
-                            <span class="settings-item-unit">from</span><input type="text" class="styled working-schedule time" id="thursdayFrom">
-                            <span class="settings-item-unit">to</span><input type="text" class="styled working-schedule time" id="thursdayTo">
-                        </td>
-                        <td><span class="help-mark" title="sets the working schedule time interval for Thursdays">?</span></td>
-                    </tr>
-                    <tr class="settings-item">
-                        <td class="settings-item-label"><span class="settings-item-label">Friday</span></td>
-                        <td class="settings-item-value">
-                            <span class="settings-item-unit">from</span><input type="text" class="styled working-schedule time" id="fridayFrom">
-                            <span class="settings-item-unit">to</span><input type="text" class="styled working-schedule time" id="fridayTo">
-                        </td>
-                        <td><span class="help-mark" title="sets the working schedule time interval for Friday">?</span></td>
-                    </tr>
-                    <tr class="settings-item">
-                        <td class="settings-item-label"><span class="settings-item-label">Saturday</span></td>
-                        <td class="settings-item-value">
-                            <span class="settings-item-unit">from</span><input type="text" class="styled working-schedule time" id="saturdayFrom">
-                            <span class="settings-item-unit">to</span><input type="text" class="styled working-schedule time" id="saturdayTo">
-                        </td>
-                        <td><span class="help-mark" title="sets the working schedule time interval for Saturday">?</span></td>
-                    </tr>
-                    <tr class="settings-item">
-                        <td class="settings-item-label"><span class="settings-item-label">Sunday</span></td>
-                        <td class="settings-item-value">
-                            <span class="settings-item-unit">from</span><input type="text" class="styled working-schedule time" id="sundayFrom">
-                            <span class="settings-item-unit">to</span><input type="text" class="styled working-schedule time" id="sundayTo">
-                        </td>
-                        <td><span class="help-mark" title="sets the working schedule time interval for Sunday">?</span></td>
-                    </tr>
-                </table>
-            </div>
-        </div>
-        <div class="page-container">
-            {% block page_content %}{% endblock %}
-        </div>
-        <div class="footer">
-            copyright &copy; Calin Crisan 2013
-        </div> 
-    </div>
-{% endblock %}
index fd8a865ac35b419cd86677918ed432e9f7cf7706..afba521b79af811743392ac9427b16dc8ef4d1bc 100644 (file)
-{% extends "base-site.html" %}
-{% import "macros.html" as macros %}
+{% extends "base.html" %}
 
 {% block style %}
     {{super()}}
+    <link rel="stylesheet" type="text/css" href="{{STATIC_URL}}css/ui.css" />
     <link rel="stylesheet" type="text/css" href="{{STATIC_URL}}css/main.css" />
 {% endblock %}
 
-{% block page_content %}
-    <div class="video-list">
-        {{macros.video("/static/img/video1.jpg")}}
-        {{macros.video("/static/img/video2.jpg")}}
-        {{macros.video("/static/img/video1.jpg")}}
-        {{macros.video("/static/img/video2.jpg")}}
+{% block script %}
+    {{super()}}
+    <script type="text/javascript" src="{{STATIC_URL}}js/ui.js"></script>
+    <script type="text/javascript" src="{{STATIC_URL}}js/main.js"></script>
+{% endblock %}
+
+{% block body %}
+    <div class="header">
+        <div class="header-container">
+            <div class="settings-top-bar">
+                <img class="settings-button" src="{{STATIC_URL}}img/settings.png" title="settings">
+                <select class="video-device styled" id="videoDeviceSelect"></select>
+                <div class="button apply-button" id="applyButton">Apply</div>
+            </div>
+            <div class="logo">
+                <a href="/">
+                    <span class="logo">motionEye</span>
+                    <img class="logo" src="{{STATIC_URL}}img/motioneye.png">
+                </a>
+            </div>
+        </div>
+    </div>
+    <div class="page">
+        <div class="settings">
+            <div class="settings-container">
+                <div class="settings-section-title"><input type="checkbox" class="styled section general" id="motionEyeSwitch">General Settings</div>
+                <table class="settings">
+                    <tr class="settings-item">
+                        <td class="settings-item-label"><span class="settings-item-label">Show Advanced Settings</span></td>
+                        <td class="settings-item-value"><input type="checkbox" class="styled general" id="showAdvancedSwitch"></td>
+                        <td><span class="help-mark" title="enable this to be able to access all the advanced settings">?</span></td>
+                    </tr>
+                    <tr class="settings-item">
+                        <td class="settings-item-label"><span class="settings-item-label">Administrator Username</span></td>
+                        <td class="settings-item-value"><input type="text" class="styled general" id="adminUsernameEntry"></td>
+                        <td><span class="help-mark" title="the username supplied to configure motionEye">?</span></td>
+                    </tr>
+                    <tr class="settings-item">
+                        <td class="settings-item-label"><span class="settings-item-label">Administrator Password</span></td>
+                        <td class="settings-item-value"><input type="password" class="styled general" id="adminPasswordEntry"></td>
+                        <td><span class="help-mark" title="administrator's password">?</span></td>
+                    </tr>
+                    <tr class="settings-item">
+                        <td class="settings-item-label"><span class="settings-item-label">Surveillance Username</span></td>
+                        <td class="settings-item-value"><input type="text" class="styled general" id="normalUsernameEntry"></td>
+                        <td><span class="help-mark" title="the username supplied for video surveillance">?</span></td>
+                    </tr>
+                    <tr class="settings-item">
+                        <td class="settings-item-label"><span class="settings-item-label">Surveillance Password</span></td>
+                        <td class="settings-item-value"><input type="password" class="styled general" id="normalPasswordEntry"></td>
+                        <td><span class="help-mark" title="the password for the surveillance user">?</span></td>
+                    </tr>
+                </table>
+                
+                <div class="settings-section-title"><input type="checkbox" class="styled section device" id="videoDeviceSwitch">Video Device</div>
+                <table class="settings">
+                    <tr class="settings-item">
+                        <td class="settings-item-label"><span class="settings-item-label">Camera Name</span></td>
+                        <td class="settings-item-value"><input type="text" class="styled device" id="deviceNameEntry" placeholder="camera name..."></td>
+                        <td><span class="help-mark" title="an alias for this camera device">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Camera Device</span></td>
+                        <td class="settings-item-value"><input type="text" class="styled device" id="deviceEntry" disabled="disabled"></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td colspan="100"><div class="settings-item-separator"></div></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Light Switch Detection</span></td>
+                        <td class="settings-item-value"><input type="checkbox" class="styled device" id="lightSwitchDetectSwitch"></td>
+                        <td><span class="help-mark" title="enable this if you want sudden changes in light to not be treated as motion">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Automatic Brightness</span></td>
+                        <td class="settings-item-value"><input type="checkbox" class="styled device" id="autoBrightnessSwitch"></td>
+                        <td><span class="help-mark" title="enables software automatic brightness (not needed for most cameras)">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Brightness</span></td>
+                        <td class="settings-item-value"><input type="text" class="range styled device" id="brightnessSlider"></td>
+                        <td><span class="help-mark" title="sets a desired brightness level for this camera">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Contrast</span></td>
+                        <td class="settings-item-value"><input type="text" class="range styled device" id="contrastSlider"></td>
+                        <td><span class="help-mark" title="sets a desired contrast level for this camera">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Saturation</span></td>
+                        <td class="settings-item-value"><input type="text" class="range styled device" id="saturationSlider"></td>
+                        <td><span class="help-mark" title="sets a desired saturation (color) level for this camera">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Hue</span></td>
+                        <td class="settings-item-value"><input type="text" class="range styled device" id="hueSlider"></td>
+                        <td><span class="help-mark" title="sets a desired hue (color) for this camera">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td colspan="100"><div class="settings-item-separator"></div></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Video Resolution</span></td>
+                        <td class="settings-item-value">
+                            <select class="video-resolution styled device" id="resolutionSelect">
+                            </select>
+                        </td>
+                        <td><span class="help-mark" title="the video resolution (larger values yield better quality but require larger storage space and bandwidth)">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Video Rotation</span></td>
+                        <td class="settings-item-value">
+                            <select class="video-resolution styled device" id="rotationSelect">
+                                <option>0&deg;</option>
+                                <option>90&deg;</option>
+                                <option>180&deg;</option>
+                                <option>270&deg;</option>
+                            </select>
+                            <td><span class="help-mark" title="use this to rotate the captured image, if your camera is not positioned correctly">?</span></td>
+                        </td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Frame Rate</span></td>
+                        <td class="settings-item-value"><input type="text" class="range styled device" id="framerateSlider"></td>
+                        <td><span class="help-mark" title="sets the number of frames captured by the camera every second (higher values yield smoother videos but require larger storage space and bandwidth)">?</span></td>
+                    </tr>
+                </table>
+                
+                <div class="settings-section-title advanced-setting">File Storage</div>
+                <table class="settings advanced-setting">
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Storage Device</span></td>
+                        <td class="settings-item-value">
+                            <select class="styled storage" id="storageDeviceSelect">
+                                <option value="local-disk">Local Disk</option>
+                                <option value="network-share">Network Share</option>
+                            </select>
+                        </td>
+                        <td><span class="help-mark" title="indicates the storage device where the image and video files will be saved">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Network Server</span></td>
+                        <td class="settings-item-value"><input type="text" class="styled storage" id="networkServerEntry"></td>
+                        <td><span class="help-mark" title="the address of the network server (IP address or hostname)">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Share Name</span></td>
+                        <td class="settings-item-value"><input type="text" class="styled storage" id="networkShareNameEntry"></td>
+                        <td><span class="help-mark" title="the name of the network share">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Share Username</span></td>
+                        <td class="settings-item-value"><input type="text" class="styled storage" id="networkUsernameEntry"></td>
+                        <td><span class="help-mark" title="the username to be supplied when accessing the network share (leave empty if no username is required)">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Share Password</span></td>
+                        <td class="settings-item-value"><input type="password" class="styled storage" id="networkPasswordEntry"></td>
+                        <td><span class="help-mark" title="the password required by the network share (leave empty if no password is required)">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Root Directory</span></td>
+                        <td class="settings-item-value"><input type="text" class="styled storage" id="rootDirectoryEntry"></td>
+                        <td><span class="help-mark" title="the root path (on the selected storage device) where the files will be saved">?</span></td>
+                    </tr>
+                </table>
+                
+                <div class="settings-section-title advanced-setting"><input type="checkbox" class="styled section text-overlay" id="textOverlaySwitch">Text Overlay</div>
+                <table class="settings advanced-setting">
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Left Text</span></td>
+                        <td class="settings-item-value">
+                            <select class="styled text-overlay" id="leftTextSelect">
+                                <option value="camera-name">Camera Name</option>
+                                <option value="timestamp">Timestamp</option>
+                                <option value="custom-text">Custom Text</option>
+                            </select>
+                        </td>
+                        <td><span class="help-mark" title="sets the text displayed on the movies and images, on the lower left corner">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"></td>
+                        <td class="settings-item-value"><input type="text" class="styled text-overlay" id="leftTextEntry" placeholder="custom text..."></td>
+                        <td><span class="help-mark" title="sets a custom left text; the following special tokens are accepted: %Y = year, %m = month, %d = date, %H = hour, %M = minute, %S = second, %T = HH:MM:SS, %q = frame number, \n = new line">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Right Text</span></td>
+                        <td class="settings-item-value">
+                            <select class="styled text-overlay" id="rightTextSelect">
+                                <option value="camera-name">Camera Name</option>
+                                <option value="timestamp">Timestamp</option>
+                                <option value="custom-text">Custom Text</option>
+                            </select>
+                        </td>
+                        <td><span class="help-mark" title="sets the text displayed on the movies and images, on the lower right corner">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"></td>
+                        <td class="settings-item-value"><input type="text" class="styled text-overlay" id="rightTextEntry" placeholder="custom text..."></td>
+                        <td><span class="help-mark" title="sets a custom right text; the following special tokens are accepted: %Y = year, %m = month, %d = date, %H = hour, %M = minute, %S = second, %T = HH:MM:SS, %q = frame number, \n = new line">?</span></td>
+                    </tr>
+                </table>
+
+                <div class="settings-section-title"><input type="checkbox" class="styled section streaming" id="videoStreamingSwitch">Video Streaming</div>
+                <table class="settings">
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Streaming Frame Rate</span></td>
+                        <td class="settings-item-value"><input type="text" class="range styled streaming" id="streamingFramerateSlider"></td>
+                        <td><span class="help-mark" title="sets the number of frames transmitted every second on the live streaming">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Streaming Quality</span></td>
+                        <td class="settings-item-value"><input type="text" class="range styled streaming" id="streamingQualitySlider"></td>
+                        <td><span class="help-mark" title="sets the live streaming quality (higher values yield a better video quality but require more bandwidth)">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Streaming Port</span></td>
+                        <td class="settings-item-value"><input type="text" class="styled streaming" id="streamingPortEntry"></td>
+                        <td><span class="help-mark" title="sets the TCP port on which the webcam streaming server listens">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Motion Optimization</span></td>
+                        <td class="settings-item-value"><input type="checkbox" class="styled streaming" id="streamingMotion"></td>
+                        <td><span class="help-mark" title="enable this if you want a lower frame rate for the live streaming when no motion is detected">?</span></td>
+                    </tr>
+                </table>
+                
+                <div class="settings-section-title"><input type="checkbox" class="styled section still-images" id="stillImagesSwitch">Still Images</div>
+                <table class="settings">
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Image File Name</span></td>
+                        <td class="settings-item-value"><input type="text" class="styled still-images" id="imageFileNameEntry" placeholder="file name pattern..."></td>
+                        <td><span class="help-mark" title="sets the name pattern for the image (JPEG) files; the following special tokens are accepted: %Y = year, %m = month, %d = date, %H = hour, %M = minute, %S = second, %q = frame number, / = subfolder">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Image Quality</span></td>
+                        <td class="settings-item-value"><input type="text" class="range styled still-images" id="imageQualitySlider"></td>
+                        <td><span class="help-mark" title="sets the JPEG image quality (higher values yield a better image quality but require more storage space)">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Capture Mode</span></td>
+                        <td class="settings-item-value">
+                            <select class="styled still-images" id="captureModeSelect">
+                                <option value="motion-triggered">Motion Triggered</option>
+                                <option value="interval-snapshots">Interval Snapshots</option>
+                                <option value="all-frames">All Frames</option>
+                            </select>
+                        </td>
+                        <td><span class="help-mark" title="sets the image capture mode: Motion Triggered = an image captured whenever motion is detected, Automated Snapshots = an image captured every x seconds, All Frames = saves each frame into an image file">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Snapshot Interval</span></td>
+                        <td class="settings-item-value"><input type="text" class="styled number still-images" id="snapshotIntervalEntry"><span class="settings-item-unit">seconds</span></td>
+                        <td><span class="help-mark" title="sets the interval (in seconds) for the automated snapshots">?</span></td>
+                    </tr>
+                    <tr class="settings-item">
+                        <td class="settings-item-label"><span class="settings-item-label">Preserve Images</span></td>
+                        <td class="settings-item-value">
+                            <select class="styled still-images" id="preserveImagesSelect">
+                                <option value="1">For One Day</option>
+                                <option value="7">For One Week</option>
+                                <option value="30">For One Month</option>
+                                <option value="365">For One Year</option>
+                                <option value="0">Forever</option>
+                            </select>
+                        </td>
+                        <td><span class="help-mark" title="images older than the specified duration are automatically deleted to free storage space">?</span></td>
+                    </tr>
+                </table>
+                
+                <div class="settings-section-title"><input type="checkbox" class="styled section motion-movies" id="motionMoviesSwitch">Motion Movies</div>
+                <table class="settings">
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Movie File Name</span></td>
+                        <td class="settings-item-value"><input type="text" class="styled motion-movies" id="movieFileNameEntry" placeholder="file name pattern..."></td>
+                        <td><span class="help-mark" title="sets the name pattern for the movie (MPEG) files; the following special tokens are accepted: %Y = year, %m = month, %d = date, %H = hour, %M = minute, %S = second, %q = frame number, / = subfolder">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Movie Quality</span></td>
+                        <td class="settings-item-value"><input type="text" class="range styled motion-movies" id="movieQualitySlider"></td>
+                        <td><span class="help-mark" title="sets the MPEG video quality (higher values yield a better video quality but require more storage space)">?</span></td>
+                    </tr>
+                    <tr class="settings-item">
+                        <td class="settings-item-label"><span class="settings-item-label">Preserve Movies</span></td>
+                        <td class="settings-item-value">
+                            <select class="styled motion-movies" id="preserveMoviesSelect">
+                                <option value="1">For One Day</option>
+                                <option value="7">For One Week</option>
+                                <option value="30">For One Month</option>
+                                <option value="365">For One Year</option>
+                                <option value="0">Forever</option>
+                            </select>
+                        </td>
+                        <td><span class="help-mark" title="movies older than the specified duration are automatically deleted to free storage space">?</span></td>
+                    </tr>
+                </table>
+                
+                <div class="settings-section-title advanced-setting">Motion Detection</div>
+                <table class="settings advanced-setting">
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Show Frame Changes</span></td>
+                        <td class="settings-item-value"><input type="checkbox" class="styled motion-detection" id="showFrameChangesSwitch"></td>
+                        <td><span class="help-mark" title="if this is enabled, frame changes (number of pixels as well as the changed area) are shown on the video; temporarily enable this option to help adjust the motion detection parameters">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Frame Change Threshold</span></td>
+                        <td class="settings-item-value"><input type="text" class="range styled motion-detection" id="frameChangeThresholdSlider"></td>
+                        <td><span class="help-mark" title="indicates the minimal number of pixels that must change between to frames in order for motion to be detected (smaller values give a more sensitive detection, but are prone to false positives)">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Automatic Noise Detection</span></td>
+                        <td class="settings-item-value"><input type="checkbox" class="styled motion-detection" id="autoNoiseDetectSwitch"></td>
+                        <td><span class="help-mark" title="enable this to automatically adjust the noise level">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Noise Level</span></td>
+                        <td class="settings-item-value"><input type="text" class="range styled motion-detection" id="noiseLevelSlider"></td>
+                        <td><span class="help-mark" title="manually sets the noise level to a fixed value">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td colspan="100"><div class="settings-item-separator"></div></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Motion Gap</span></td>
+                        <td class="settings-item-value"><input type="text" class="number styled motion-detection" id="gapEntry"><span class="settings-item-unit">seconds</span></td>
+                        <td><span class="help-mark" title="sets the number of seconds of silence (i.e. no motion) that mark the end of a motion event">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Captured Before</span></td>
+                        <td class="settings-item-value"><input type="text" class="number styled motion-detection" id="preCaptureEntry"><span class="settings-item-unit">frames</span></td>
+                        <td><span class="help-mark" title="sets the number of frames to be captured (and included in the movie) before a motion event is detected">?</span></td>
+                    </tr>
+                    <tr class="settings-item advanced-setting">
+                        <td class="settings-item-label"><span class="settings-item-label">Captured After</span></td>
+                        <td class="settings-item-value"><input type="text" class="number styled motion-detection" id="postCaptureEntry"><span class="settings-item-unit">frames</span></td>
+                        <td><span class="help-mark" title="sets the number of frames to be captured (and included in the movie) after a motion event is detected">?</span></td>
+                    </tr>
+                </table>
+                
+                <div class="settings-section-title"><input type="checkbox" class="styled section notifications" id="motionNotificationsSwitch">Motion Notifications</div>
+                <table class="settings">
+                    <tr class="settings-item">
+                        <td class="settings-item-label"><span class="settings-item-label">Email Addresses</span></td>
+                        <td class="settings-item-value"><input type="text" class="styled notifications" id="emailAddressesEntry" placeholder="email addresses..."></td>
+                        <td><span class="help-mark" title="email addresses (separated by comma) that are added here will receive notifications whenever a motion event is detected (leave empty to disable email notifications)">?</span></td>
+                    </tr>
+                </table>
+
+                <div class="settings-section-title"><input type="checkbox" class="styled section working-schedule" id="workingScheduleSwitch">Working Schedule</div>
+                <table class="settings">
+                    <tr class="settings-item">
+                        <td class="settings-item-label"><span class="settings-item-label">Monday</span></td>
+                        <td class="settings-item-value">
+                            <span class="settings-item-unit">from</span><input type="text" class="styled working-schedule time" id="mondayFrom">
+                            <span class="settings-item-unit">to</span><input type="text" class="styled working-schedule time" id="mondayTo">
+                        </td>
+                        <td><span class="help-mark" title="sets the working schedule time interval for Mondays">?</span></td>
+                    </tr>
+                    <tr class="settings-item">
+                        <td class="settings-item-label"><span class="settings-item-label">Tuesday</span></td>
+                        <td class="settings-item-value">
+                            <span class="settings-item-unit">from</span><input type="text" class="styled working-schedule time" id="tuesdayFrom">
+                            <span class="settings-item-unit">to</span><input type="text" class="styled working-schedule time" id="tuesdayTo">
+                        </td>
+                        <td><span class="help-mark" title="sets the working schedule time interval for Tuesdays">?</span></td>
+                    </tr>
+                    <tr class="settings-item">
+                        <td class="settings-item-label"><span class="settings-item-label">Wednesday</span></td>
+                        <td class="settings-item-value">
+                            <span class="settings-item-unit">from</span><input type="text" class="styled working-schedule time" id="wednesdayFrom">
+                            <span class="settings-item-unit">to</span><input type="text" class="styled working-schedule time" id="wednesdayTo">
+                        </td>
+                        <td><span class="help-mark" title="sets the working schedule time interval for Wednesdays">?</span></td>
+                    </tr>
+                    <tr class="settings-item">
+                        <td class="settings-item-label"><span class="settings-item-label">Thursday</span></td>
+                        <td class="settings-item-value">
+                            <span class="settings-item-unit">from</span><input type="text" class="styled working-schedule time" id="thursdayFrom">
+                            <span class="settings-item-unit">to</span><input type="text" class="styled working-schedule time" id="thursdayTo">
+                        </td>
+                        <td><span class="help-mark" title="sets the working schedule time interval for Thursdays">?</span></td>
+                    </tr>
+                    <tr class="settings-item">
+                        <td class="settings-item-label"><span class="settings-item-label">Friday</span></td>
+                        <td class="settings-item-value">
+                            <span class="settings-item-unit">from</span><input type="text" class="styled working-schedule time" id="fridayFrom">
+                            <span class="settings-item-unit">to</span><input type="text" class="styled working-schedule time" id="fridayTo">
+                        </td>
+                        <td><span class="help-mark" title="sets the working schedule time interval for Friday">?</span></td>
+                    </tr>
+                    <tr class="settings-item">
+                        <td class="settings-item-label"><span class="settings-item-label">Saturday</span></td>
+                        <td class="settings-item-value">
+                            <span class="settings-item-unit">from</span><input type="text" class="styled working-schedule time" id="saturdayFrom">
+                            <span class="settings-item-unit">to</span><input type="text" class="styled working-schedule time" id="saturdayTo">
+                        </td>
+                        <td><span class="help-mark" title="sets the working schedule time interval for Saturday">?</span></td>
+                    </tr>
+                    <tr class="settings-item">
+                        <td class="settings-item-label"><span class="settings-item-label">Sunday</span></td>
+                        <td class="settings-item-value">
+                            <span class="settings-item-unit">from</span><input type="text" class="styled working-schedule time" id="sundayFrom">
+                            <span class="settings-item-unit">to</span><input type="text" class="styled working-schedule time" id="sundayTo">
+                        </td>
+                        <td><span class="help-mark" title="sets the working schedule time interval for Sunday">?</span></td>
+                    </tr>
+                </table>
+            </div>
+        </div>
+        <div class="page-container">
+            {% block page_content %}{% endblock %}
+        </div>
+        <div class="footer">
+            copyright &copy; Calin Crisan 2013
+        </div> 
     </div>
 {% endblock %}