Browse Source

add web_interface folder

balbekova 8 years ago
parent
commit
9fc17d407f

+ 120 - 0
web_interface/Gruntfile.js

@@ -0,0 +1,120 @@
+module.exports = function (grunt) {
+
+  grunt.initConfig({
+    pkg: grunt.file.readJSON('package.json'),
+    cssmin: {
+      wui: {
+         expand: true,
+         cwd: 'src/wui',
+         src: ['*.css', '!*.min.css'],
+         dest: 'src/wui',
+         ext: '.min.css'
+      },
+      upload: {
+         expand: true,
+         cwd: 'src/upload',
+         src: ['*.css', '!*.min.css'],
+         dest: 'src/upload',
+         ext: '.min.css'
+      }
+    },
+    uglify: {
+      wui: {
+        files: {
+          'src/wui/main.min.js': ['src/wui/main.js']
+        }
+      },
+      upload: {
+        files: {
+          'src/upload/upload.min.js': ['src/upload/upload.js']
+        }
+      }
+    },
+    compress: {
+      wui: {
+        options: {
+          mode: 'gzip'
+        },
+        files: [
+          {expand: true, cwd: 'src/wui/',src: ['*.min.js'], dest: 'dist/wui', ext: '.js'},
+          {expand: true, cwd: 'src/wui/',src: ['*.min.css'], dest: 'dist/wui', ext: '.css'},
+          {expand: true, cwd: 'src/wui/',src: ['*.min.html'], dest: 'dist/wui', ext: '.html'}
+        ]
+      },
+      upload: {
+        options: {
+          mode: 'gzip'
+        },
+        files: [
+          {expand: true, cwd: 'src/upload/',src: ['*.min.js'], dest: 'dist/upload', ext: '.js'},
+          {expand: true, cwd: 'src/upload/',src: ['*.min.css'], dest: 'dist/upload', ext: '.css'},
+          {expand: true, cwd: 'src/upload/',src: ['*.html'], dest: 'dist/upload', ext: '.html'}
+        ]
+      }
+    },
+    htmlmin: {
+      wui: {
+        options: {
+          removeComments: true,
+          collapseWhitespace: true,
+          minifyJS : true
+        },
+        files: {
+          'src/wui/index.min.html':     'src/wui/index.html',
+          'src/wui/settings.min.html':  'src/wui/settings.html',
+          'src/wui/info.min.html':      'src/wui/info.html'
+        }
+      },
+      upload: {
+        options: {
+          removeComments: true,
+          collapseWhitespace: true,
+          minifyJS : true
+        },
+        files: {
+          'dist/upload/index.html':    'src/upload/index.html',
+          'dist/upload/error.html':    'src/upload/error.html',
+          'dist/upload/success.html':  'src/upload/success.html'
+        }
+      }
+    },
+    imagemin: {
+      wui:{
+        options: {
+          optimizationLevel: 0
+        },
+        files: [
+          {'dist/wui/rotek.png': 'src/wui/rotek.png'},
+          {'dist/wui/favicon.ico': 'src/wui/favicon.ico'},
+          {'dist/upload/favicon.ico': 'src/upload/favicon.ico'}
+        ]
+      }
+    },
+    clean: {
+      wui: {
+        src: ["src/wui/main.min.js", "src/wui/*.min.html", "!src/wui/main.js", "src/wui/main.min.css", "!src/wui/main.css"]
+      },
+      upload: {
+        src: ["src/upload/upload.min.js", "!src/upload/upload.js", "src/upload/upload.min.css", "!src/upload/upload.css"]
+      },
+      clear: {
+        src: 'dist*/*'
+      }
+    },
+    jshint: {
+      all: ['Gruntfile.js', 'src/wui/main.js', 'src/upload/upload.js']
+    }
+  });
+
+  grunt.loadNpmTasks('grunt-contrib-htmlmin');
+  grunt.loadNpmTasks('grunt-contrib-uglify');
+  grunt.loadNpmTasks('grunt-contrib-cssmin');
+  grunt.loadNpmTasks('grunt-contrib-clean');
+  grunt.loadNpmTasks('grunt-contrib-compress');
+  grunt.loadNpmTasks('grunt-contrib-imagemin');
+  grunt.loadNpmTasks('grunt-contrib-jshint');
+  grunt.registerTask('default', ['cssmin','uglify','htmlmin','clean:wui','clean:upload']);
+  grunt.registerTask('build', ['cssmin','uglify','htmlmin','compress','clean:wui','clean:upload','imagemin']);
+  grunt.registerTask('clear', ['clean:clear']);
+  grunt.registerTask('jhint', ['jshint']);
+};

+ 1 - 0
web_interface/dist/readme.md

@@ -0,0 +1 @@
+## Здесь будут лежать собранные файлы

+ 20 - 0
web_interface/package.json

@@ -0,0 +1,20 @@
+{
+  "name": "SmartUPS",
+  "version": "0.1.0",
+  "devDependencies": {
+    "grunt": "^0.4.5",
+    "grunt-contrib-clean": "^0.6.0",
+    "grunt-contrib-compress": "^0.13.0",
+    "grunt-contrib-cssmin": "^0.14.0",
+    "grunt-contrib-htmlmin": "^0.5.0",
+    "grunt-contrib-imagemin": "^1.0.1",
+    "grunt-contrib-jshint": "^0.11.3",
+    "grunt-contrib-nodeunit": "~0.4.1",
+    "grunt-contrib-uglify": "^0.9.2",
+    "grunt-processhtml": "^0.3.8",
+    "grunt-replace": "^0.11.0"
+  },
+  "author": "Avetisyan Karen",
+  "license": "ISC",
+  "description": ""
+}

+ 45 - 0
web_interface/readme.md

@@ -0,0 +1,45 @@
+# WEB Интерфейс для проекта SmartUPS
+
+## Обозначения
+
+wui : основной интерфейс
+
+upload: интерфейс загрузчика
+
+## Подготовка к сборке
+
+Для сборки проекта необходим node.js (https://nodejs.org/en/download/).
+После установки node.js выполняем след. команды:
+
+`npm install -g grunt-cli`
+
+Находясь в директории проекта выполняем:
+
+`npm install`
+
+ждем окончания загрузки модулей.
+
+## Сборка автоматическая (только Windows)
+
+Запускаем файл `run.cmd`
+
+## Сборка ручная
+
+Выполняем:
+
+`grunt build`
+
+Выше указанная команда:
+
+- минимизирует CSS файл и сохраняет как `*.min.css`
+- минимизирует JS файл и сохраняет как `*.min.js`
+- минимизирует HTML файл и сохраняет в `/dist/wui`
+- сжимает CSS (`/src/<ui_name>/*.min.css`) в GZIP, переименовывает как `*.css` и кидает в папку `/dist/<ui_name>`
+- сжимает JS (`/src/<ui_name>/*.min.js`) в GZIP, переименовывает как `*.js` и кидает в папку `/dist/<ui_name>`
+
+
+Далее из директории `/dist/<ui_name>` переносим все файлы в папку `/fs` и используем makefsdata для переоброзования файлов в `fsdata.c`
+
+------------------------------------------------------------
+
+Для очистки директории `/dist` можно выполнить `grunt clean`

+ 19 - 0
web_interface/run.cmd

@@ -0,0 +1,19 @@
+call grunt build
+set web=%cd%
+cd ..\HTTP_Server\fs\
+set fs=%cd%
+del %fs%/* /Y
+cd %web%
+xcopy %web%\dist\wui\* %fs%\ /S /Y
+cd ..\HTTP_Server\
+call makefsdata.exe -11
+cd %web%
+
+rem cd ..\Modules\Ethernet\fs\
+rem set upload=%cd%
+rem del %upload%/* /Y
+rem cd %web%
+rem xcopy %web%\dist\upload\* %upload%\ /S /Y
+rem cd ..\Modules\Ethernet\
+rem call makefsdata.exe -11
+rem cd %web%

+ 8 - 0
web_interface/smartUPS.sublime-project

@@ -0,0 +1,8 @@
+{
+	"folders":
+	[
+		{
+			"path": "."
+		}
+	]
+}

BIN
web_interface/src/rotek.psd


+ 25 - 0
web_interface/src/upload/error.html

@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="">
+<head>
+<meta charset="utf-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
+<link rel="stylesheet" href="upload.css">
+<title>Ошибка обновления</title>
+</head>
+<body>
+<nav class="navbar"></nav>
+<div class="wrapper">
+<h1>Обновление прошивки</h1>
+<div class="upload-form">
+<div class="guide" style="text-align:center;">
+  <p style="color:#d9534f;"><b>При обновлении программного обеспечения возникла ошибка.</b><br> Пожалуйста, попробуйте ещё раз через <span id="count-number">5</span> секунд</p>
+</div>
+</div>
+<script type="text/javascript" src="upload.js"></script>
+<script>
+  countdown();
+</script>
+</div>
+</body>
+</html>

BIN
web_interface/src/upload/favicon.ico


+ 44 - 0
web_interface/src/upload/index.html

@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html lang="">
+<head>
+<meta charset="utf-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
+<link rel="stylesheet" href="upload.css">
+<title>Обновление прошивки</title>
+</head>
+<body>
+<nav class="navbar"></nav>
+<div id="count-wrap">
+  <div id='countdown'>
+    <p>Контроллер будет перезагружен через <span id="count-number">5</span> секунд.</p>
+  </div>
+</div>
+<div class="wrapper">
+<h1>Обновление прошивки</h1>
+<form  action="/upload.cgi" method="post" enctype="multipart/form-data" onsubmit="return Validate(this);">
+<div class="upload-form">
+<div class="guide">
+  <p>Для обновления прошивки необходимо:</p>
+  <ol>
+    <li>Скопировать на компьютер или внешний носитель, файл прошивки с расширением *.bin</li>
+    <li>Указать путь к файлу прошивки</li>
+    <li>Нажать кнопку "Загрузить"</li>
+  </ol>
+  <p>Для возврата в основной интерфейс контроллера нажмите "Отменить"</p>
+</div>
+  <div class="upload-wrapper">
+    <input id="uploadFile" placeholder="Файл" disabled="disabled" class="file-name" />
+    <div class="fileUpload btn btn-primary">
+      <span>Выбор</span>
+      <input id="uploadBtn" type="file" class="upload" name="datafile"/>
+    </div>
+  </div>
+</div>
+  <input class="btn btn-primary" type="submit" value="Загрузить">
+  <input class="btn btn-danger" type="button" value="Отменить" id="goback" >
+</form>
+<script type="text/javascript" src="upload.js"></script>
+</div>
+</body>
+</html>

+ 20 - 0
web_interface/src/upload/login.html

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="">
+<head>
+<meta charset="utf-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<link rel="stylesheet" href="fwstyle.css">
+<title></title>
+</head>
+<body>
+<h2 style="text-align: center; padding-top: 100px;">Авторизация</h2>
+<form class="upload-form" action="/checklogin.cgi" method="post">
+    <input id="uploadFile" type="text" placeholder="Логин" class="login" size="20" name="username"/>
+    <input id="uploadFile" type="password" placeholder="Пароль" class="login" size="20" name="password"/>
+  <ul>
+    <input class="btn" type="submit" value="Войти">
+  </ul>
+</form>
+</body>
+</html>

+ 25 - 0
web_interface/src/upload/success.html

@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="">
+<head>
+<meta charset="utf-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
+<link rel="stylesheet" href="upload.css">
+<title>Успешное обновление</title>
+</head>
+<body>
+<nav class="navbar"></nav>
+<div class="wrapper">
+<h1>Обновление прошивки</h1>
+<div class="upload-form">
+<div class="guide" style="text-align:center;">
+  <p style="color:#33CC00;"><b>Обновление программного обеспечения успешно завершено.</b><br> Контроллер будет перезагружен через <span id="count-number">5</span> секунд</p>
+</div>
+</div>
+<script type="text/javascript" src="upload.js"></script>
+<script>
+  countdown();
+</script>
+</div>
+</body>
+</html>

+ 119 - 0
web_interface/src/upload/upload.css

@@ -0,0 +1,119 @@
+/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
+img,legend{border:0}legend,td,th{padding:0}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,optgroup,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre,textarea{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit;}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}table{border-spacing:0;border-collapse: separate;}
+* {margin: 0; padding: 0;}
+
+html{background-color:#E8EBF1;min-height:720px;}
+body{color: #333;}
+.navbar {position: relative;min-height: 100px;margin-bottom: 20px;}
+.wrapper{width:960px;margin: 0 auto;}
+.guide{
+  display: block;
+  padding: 0 30px;
+}
+.guide > ul {
+  list-style: none;
+}
+.guide p{padding: 10px 0;}
+h1{font-family:Arial, sans-serif;text-transform: uppercase;font-weight: normal;color: #747589; margin: .67em 0;}
+.upload-form{font-family:sans-serif;background-color:#fff;padding:30px;margin:20px auto;box-shadow: 0 1px 5px rgba(0,0,0,0.15);position: relative;}
+.fileUpload {position: relative; overflow: hidden;}
+.fileUpload input.upload {position: absolute;top: 0;right: 0;margin: 0;padding: 0;font-size: 20px;cursor: pointer;opacity: 0;filter: alpha(opacity=0);}
+button,html input[type="button"],input[type="reset"],input[type="submit"] {-webkit-appearance: button;cursor: pointer;}
+/*button[disabled],html input[disabled] {cursor: default; background-color: rgb(235, 235, 228);}*/
+button::-moz-focus-inner,input::-moz-focus-inner {padding: 0;border: 0;}
+input {line-height: normal;}
+input.file-name{height: 30px;font-size: 12px;line-height: 1.5;padding:3px 10px;border: 1px solid #337ab7; border-radius: 0;display: block;float: left;}
+input.login{height: 30px;font-size: 12px;line-height: 1.5;padding:3px 10px;border: none; border-radius: 0; margin-bottom: 25px;width: 280px}
+.upload-wrapper{padding:10px 0px 30px 30px;width: 100%;}
+/*  .upload-form ul{list-style:none;margin:0 -30px -30px; border-top:1px solid #2b2e31;}
+.upload-form ul > input{width:100%;height: 50px;color: #fff;background-color: #337ab7;}
+.upload-form ul > input:hover{color: #d2d2d2;background-color: #2e6ea5;}*/
+.btn {
+  display: inline-block;
+  padding: 0.51em 12px;
+  margin-bottom: 0;
+  font-size: 0.9em;
+  font-weight: normal;
+  line-height: 1.5em;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: middle;
+  cursor: pointer;
+  -ms-touch-action: manipulation;touch-action: manipulation;
+  -webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;
+  background-image: none;
+  border: 1px solid transparent;
+  border-radius: 0;
+}
+.btn.disabled,
+.btn[disabled],
+fieldset[disabled] .btn {
+  pointer-events: none;
+  cursor: not-allowed;
+  filter: alpha(opacity=65);
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  opacity: .65;
+}
+.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus {outline: thin dotted;outline: 5px auto -webkit-focus-ring-color;outline-offset: -2px;}
+.btn:hover,.btn:focus,.btn.focus {color: #333;text-decoration: none;}
+.btn-primary {color: #fff;background-color: #337ab7;border-color: #3d7ab7;}
+.btn-primary:hover,.btn-primary:focus,.btn-primary.focus,.btn-primary:active,
+.btn-primary.active,.open > .dropdown-toggle.btn-primary {color: #fff;background-color: #2e6ea5;border-color: #2C5884;}
+.btn-primary:active,.btn-primary.active,.open > .dropdown-toggle.btn-primary {background-image: none;}
+.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,
+.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,
+fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus,
+.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,
+.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active {
+  background-color: #337ab7;
+  border-color: #2C5884;
+}
+.btn-danger {color: #fff;background-color: #d9534f;border-color: #d43f3a;}
+.btn-danger:hover,.btn-danger:focus,.btn-danger.focus,.btn-danger:active,.btn-danger.active,.open > .dropdown-toggle.btn-danger {color: #fff;background-color: #c9302c;border-color: #ac2925;}
+.btn-danger:active,.btn-danger.active,.open > .dropdown-toggle.btn-danger {background-image: none;}
+.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,
+fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,
+.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,
+.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active {
+  background-color: #d9534f;
+  border-color: #d43f3a;
+}
+a.btn {text-decoration: none;}
+[role="button"] {cursor: pointer;}
+#count-wrap {
+  display: none;
+  position: fixed;
+  top: 0%;
+  left: 0%;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0,0,0,0.8);
+  z-index:101;
+/*  -moz-opacity: 0.8;
+  opacity:.80;
+  filter: alpha(opacity=80);*/
+}
+
+#countdown{
+  display: none;
+  position: relative;
+  text-align: center;
+  top: 40%;
+  margin: 0 auto;
+/*  left: 33%;
+  width: 25%;*/
+  padding: 16px;
+  border: 16px solid #f0ad4e;
+  background-color: white;
+  box-shadow:0 1px 5px rgba(0,0,0,0.15);
+  z-index:102;
+  overflow: auto;
+}
+@media only screen and (max-width : 768px) {
+  .navbar {display: none;}
+  .wrapper{
+    width:95%;
+    padding: 0 0.5em;
+  }
+}

+ 80 - 0
web_interface/src/upload/upload.js

@@ -0,0 +1,80 @@
+function $(id) {return document.getElementById(id);}
+var timeout;
+document.onmousemove = function(){
+  clearTimeout(timeout);
+  timeout = setTimeout(function(){
+    if (getCGI('goback.cgi?') === true) {
+      $('count-wrap').style.display = 'block';
+      $('countdown').style.display = 'block';
+      countdown();
+    }
+  }, 1800000);
+};
+function countdown() {
+  var countDown = 5;
+  setInterval(function () {
+    if (countDown == 1) {
+      window.location.href = '/';
+    }
+    if (countDown > 0){countDown--;}
+    document.getElementById('count-number').innerHTML = countDown;
+    return countDown;
+  }, 1000);
+}
+function getCGI(url) {
+  var q,a;
+  if (url == 'goback.cgi') {
+    q = confirm("Вы уверены что хотите вернуться в основной интерфейс?");
+    a = "Возврат отменен.";
+  } else {q = true;}
+  if (q) {
+    if (window.XMLHttpRequest) {
+      xmlhttp = new XMLHttpRequest();
+    } else {
+      xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
+    }
+    // xmlhttp.onreadystatechange = true;
+    xmlhttp.open("GET", url, true);
+    xmlhttp.send();
+    return true;
+  } else {
+    alert(a);
+  }
+}
+var _validFileName = 'IBP.bin';
+function Validate(oForm) {
+  var arrInputs = oForm.getElementsByTagName('input');
+  for (var i = 0; i < arrInputs.length; i++) {
+    var oInput = arrInputs[i];
+    var blnValid = false;
+    if (oInput.type == 'file') {
+      if (oInput.files.length === 0) {
+        alert('Путь к файлу не указан');
+        blnValid = false;
+        return false;
+      }else{
+        var sFileName = oInput.files[0].name;
+        if (sFileName == _validFileName) {
+          blnValid = true;
+          $('goback').className += ' disabled';
+              break;
+        }
+        if (!blnValid) {
+          alert('Файл ' + sFileName + ' не валиден, расширение и имя файла должно соответствовать: [ ' + _validFileName + ' ]');
+          return false;
+        }
+      }
+    }
+  }
+  
+  return true;
+}
+$('goback').onclick = function(){
+  a = 'goback.cgi';
+  if (getCGI(a) === true) {
+    $('count-wrap').style.display = 'block';
+    $('countdown').style.display = 'block';
+    countdown();
+  }
+};
+$("uploadBtn").onchange = function () {document.getElementById("uploadFile").value = this.files[0].name;};

+ 15 - 0
web_interface/src/wui/checkpwd.cgi

@@ -0,0 +1,15 @@
+#!C:\Python27\python.exe
+import cgi
+form = cgi.FieldStorage()
+print "Content-Type: text/html"
+print ""
+# print "<html>"
+# print "<p>"
+if form["password"].value == "12345":
+	print "1"
+else:
+	print "0"
+# print "The user entered data are:<br>"
+# print "<b>Password:</b> " + type(form["password"].value) + "<br>"
+# print "</p>"
+# print "</html>"

+ 3 - 0
web_interface/src/wui/confirm.cgi

@@ -0,0 +1,3 @@
+import cgi
+
+print(argv)

BIN
web_interface/src/wui/favicon.ico


+ 43 - 0
web_interface/src/wui/getJson.cgi

@@ -0,0 +1,43 @@
+#!C:\Python34\python.exe
+# -*- coding: utf-8 -*-
+import json
+import random
+print("Content-Type: text/html")
+print("")
+A0  = 1
+A1  = 1
+A2  = 1
+A3  = 1
+A4  = 1
+A5  = 1
+A6  = 1
+A7  = 1
+A8  = 1
+A9  = 1
+A10 = 1
+A11 = 1
+A12 = 1
+
+bitmask = A0 + A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 + A9 + A10 + A11 + A12
+
+JSON = {
+  "AC": 218.8,
+  "DC": 230.4,
+  "in_freq": 50.0,
+  "out_freq": 50.0,
+  "pwr": 40.0,
+  "line_fail": 1,
+  "low_battery": 1,
+  "bat_cap": 92.0,
+  "inner_temp": 27.0,
+  "bat_time_left": 230.0,
+  "batsym": "Норма",
+  "connect_monitor": 1,
+  "load_monitor": 0,
+  "temp_monitor": 0,
+  "mode": "Профилактика сульфатации",
+  "alarm": bitmask,
+  "utc": "3.0",
+  "netsettings_changed": "false"
+}
+print(json.dumps(JSON))

+ 80 - 0
web_interface/src/wui/index.html

@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<html lang="">
+<head>
+<meta charset="utf-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
+<title>Параметры</title>
+<link href="main.css" rel="stylesheet">
+</head>
+<body>
+  <div class="navbar navbar-default navbar-fixed-top">
+    <div class="navbar-header">
+      <div><a href="index.html" class="logo"></a></div>
+    </div>
+    <a href="#" id="menu-icon"></a>
+    <ul class="nav navbar-nav" id="nav">
+      <li><a href="index.html" class="active">Параметры</a></li>
+      <li><a href="settings.html">Настройки</a></li>
+      <li><a href="info.html">Информация</a></li>
+    </ul>
+  </div>
+  <div class="wrapper" id="content">
+<!--START-->
+<h1>Параметры</h1>
+  <div class="panel panel-default">
+    <div class="panel-heading" id="sPower">Источник питания</div>
+    <div class="panel-body">
+      <table class="table tb-stat">
+        <tr>
+          <td>Входное напряжение (VAC)</td>
+          <td ><span id="AC"></span></td>
+        </tr>
+        <tr>
+          <td>Выходное напряжение (VAC)</td>
+          <td ><span id="DC"></span></td>
+        </tr>
+        <tr>
+          <td>Входная частота</td>
+          <td ><span id="in_freq"></span></td>
+        </tr>
+        <tr>
+          <td>Выходная частота</td>
+          <td ><span id="out_freq"></span></td>
+        </tr>
+        <tr>
+          <td>Нагрузка</td>
+          <td ><span id="pwr"></span></td>
+        </tr>
+        <tr class="hidden">
+          <td>Индикация аварийных ситуаций</td>
+          <td><span id="alarm"></span></td>
+        </tr>
+      </table>
+    </div>
+    <div class="panel-heading" id="sBattery">Аккумуляторные батареи</div>
+    <div class="panel-body">
+      <table class="table tb-stat">
+        <tr>
+          <td>Емкость батареи</td>
+          <td ><span id="bat_cap"></span></td>
+        </tr>
+        <tr>
+          <td>Внутренняя температура</td>
+          <td ><span id="inner_temp"></span></td>
+        </tr>
+        <tr>
+          <td>Оставшееся время работы</td>
+          <td ><span id="bat_time_left"></span></td>
+        </tr>
+      </table>
+    </div>
+  </div>
+<!--END-->
+</body>
+<script type="text/javascript" src="main.js"></script>
+<script type="text/javascript">
+  dataFadeIn();
+  paramsRefresh();
+</script>
+</html>

+ 19 - 0
web_interface/src/wui/info.cgi

@@ -0,0 +1,19 @@
+#!C:\Python34\python.exe
+# -*- coding: utf-8 -*-
+import json
+print("Content-Type: text/html")
+print("")
+JSON = {
+  "uptime": "2 часа",
+  "model": "BT-6037(v2)",
+  "prodate": "10.10.2015",
+  "fwversion": "beta v 0.8",
+  "macaddr": "FF:00:FF:00:FF:00",
+  "serno": "2650475",
+  "owner": "Karen",
+  "sysLocation": "Moscow",
+  "comment": "No comments",
+  "utc": "3.0",
+  "netsettings_changed": "false"
+}
+print(json.dumps(JSON))

+ 89 - 0
web_interface/src/wui/info.html

@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<html lang="">
+<head>
+<meta charset="utf-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
+<title>Информация</title>
+<link href="main.css" rel="stylesheet">
+</head>
+<body>
+  <div class="navbar navbar-default navbar-fixed-top">
+    <div class="navbar-header">
+      <div><a href="index.html" class="logo"></a></div>
+    </div>
+    <a href="#" id="menu-icon"></a>
+    <ul class="nav navbar-nav" id="nav">
+      <li><a href="index.html">Параметры</a></li>
+      <li><a href="settings.html">Настройки</a></li>
+      <li><a href="info.html" class="active">Информация</a></li>
+    </ul>
+  </div>
+  <div class="wrapper" id="content">
+<!--START-->
+<h1>Информация</h1>
+<ul id="validation-box"></ul>
+<form id="SNMPinfo" action="info.cgi" method="GET">
+<table class="table wrapper-default tb-info">
+  <tr>
+    <td class="none-border">Время работы</td>
+    <td class="none-border" id="uptime"></td>
+  </tr>
+  <tr>
+    <td>Модель</td>
+    <td id="model"></td> 
+  </tr>
+  <tr>
+    <td>Дата производства</td>
+    <td id="prodate"></td> 
+  </tr>
+  <tr>
+    <td>Версия ПО</td>
+    <td id="fwversion"></td> 
+  </tr>
+  <tr>
+    <td>MAC адрес</td>
+    <td id="macaddr"></td> 
+  </tr>
+  <tr>
+    <td>Серийный номер</td>
+    <td id="serno"></td>
+  </tr>
+  <tr>
+    <td>Владелец</td>
+    <td><input type="text" name="owner" id="owner" class="form-control" maxlength="100"></td>
+  </tr>
+  <tr>
+    <td>Местоположение</td>
+    <td><input type="text" name="sysLocation" id="location" class="form-control" maxlength="100"></td>
+  </tr>
+  <tr>
+    <td>Комментарии</td>
+    <td><input type="text" name="comment" id="comment" class="form-control" maxlength="100"></td>
+  </tr>
+</table>
+  <button type="button" onclick="submitInfo(); return false;" class="btn btn-primary">Сохранить</button>
+</form>
+<!--END-->
+</div>
+</body>
+<script type="text/javascript" src="main.js"></script>
+<script type="text/javascript">
+  getJSON('info.cgi', function(data) {
+    // Cotroller Info
+    $('uptime').innerHTML    = data.uptime;
+    $('model').innerHTML     = data.model;
+    $('prodate').innerHTML   = data.prodate;
+    $('fwversion').innerHTML = data.fwversion;
+    $('macaddr').innerHTML   = data.macaddr;
+    $('serno').innerHTML     = data.serno;
+    $('owner').value         = data.owner;
+    $('location').value      = data.sysLocation;
+    $('comment').value       = data.comment;
+    utcParam                 = data.utc;
+    netsettings_changed      = data.netsettings_changed;
+  }, function(status) {
+    alert('Не удалось получить данные.');
+  });
+</script>
+</html>

+ 1181 - 0
web_interface/src/wui/main.css

@@ -0,0 +1,1181 @@
+/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
+img,legend{border:0}legend,td,th{padding:0}html{font-family:Arial, Helvetica, sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,optgroup,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre,textarea{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit;}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}table{border-spacing:0;border-collapse: separate;}
+
+
+/* Main style */
+body {background-color: #E8EBF1; color: #333;overflow-y: scroll;}
+
+.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical > .btn-group:before,.btn-group-vertical > .btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-footer:before,.modal-footer:after 
+{display: table;content: " ";}
+div > .logo{
+  width: 245px;
+  height: 85px;
+  margin: 10px;
+}
+.logo{
+  display: block;
+  background: url('rotek.png') no-repeat -5px -15px;
+  width: 100%;
+  height: auto;
+  background-size: cover;
+}
+.mark-ok{
+  display: block;
+  background: url('rotek.png') no-repeat 0 -73px;
+  background-size: 172px 87px;
+  width: 16px;
+  height: 14px;
+  float: left;
+  margin-right:10px;
+  margin-top: 2px;
+}
+.mark-almin{
+  display: block;
+  background: url('rotek.png') no-repeat -52px -73px;
+  background-size: 172px 87px;
+  width: 16px;
+  height: 14px;
+  float: left;
+  margin-right:10px;
+  margin-top: 2px;
+}
+.mark-almaj{
+  display: block;
+  background: url('rotek.png') no-repeat -32px -73px;
+  background-size: 172px 87px;
+  width: 16px;
+  height: 14px;
+  float: left;
+  margin-right:10px;
+  margin-top: 2px;
+}
+.mark-bad{
+  display: block;
+  background: url('rotek.png') no-repeat -16px -73px;
+  background-size: 172px 87px;
+  width: 16px;
+  height: 14px;
+  float: left;
+  margin-right:10px;
+  margin-top: 2px;
+}
+.navbar {
+  position: relative;
+  min-height: 100px;
+  margin-bottom: 20px;
+  /*border-bottom: 1px solid transparent;*/
+}
+.navbar-default {
+  background-color: #fff;
+  /*border-color: #e7e7e7;*/
+  box-shadow: 0 1px 5px rgba(0,0,0,0.2);
+}
+.navbar-default .navbar-brand {
+  color: #337ab7;
+}
+.navbar-default .navbar-brand:hover,
+.navbar-default .navbar-brand:focus {
+  color: #fa1127;
+  background-color: transparent;
+}
+.navbar-default .navbar-text {
+  color: #777;
+}
+.navbar-default .navbar-nav > li > a {
+  line-height: 1em;
+  color: #337ab7;
+  font-size: 16px;
+  text-decoration: none;
+}
+.navbar-header {
+  float: left;
+}
+.navbar-nav {
+  float: right;
+  margin: 0;
+}
+.navbar-nav > li {
+  float: left;
+}
+.navbar-nav > li > a {
+  padding-top: 10px;
+  padding-bottom: 10px;
+}
+.nav {
+  margin-bottom: 0;
+  list-style: none;
+  display: inline;
+}
+.nav > li {
+  position: relative;
+  display: block;
+}
+.nav > li > a {
+  text-transform: uppercase;
+  letter-spacing: .02em;
+  position: relative;
+  display: block;
+  padding: 42px 10px;
+}
+.nav > li > a:hover,
+.nav > li > a:focus {
+  text-decoration: none;
+  /*background: #eee;*/
+}
+.nav > li > a.active{
+  /*background: #e7e7e7;*/
+  color: #424f54;
+  border-bottom: 4px solid #01a1be;
+  padding: 42px 10px 38px;
+}
+table {
+  background-color: transparent;
+}
+caption {
+  padding-top: 8px;
+  padding-bottom: 8px;
+  color: #777;
+  text-align: left;
+}
+th {
+  text-align: left;
+}
+.table {
+  width: 100%;
+  max-width: 100%;
+  margin-bottom: 20px;
+}
+.table > thead > tr > th,
+.table > tbody > tr > th,
+.table > tfoot > tr > th,
+.table > thead > tr > td,
+.table > tbody > tr > td,
+.table > tfoot > tr > td {
+  padding: 8px;
+  line-height: 1.42857143;
+  vertical-align: middle;
+  border-top: 1px solid #ddd;
+}
+.table > thead > tr > th {
+  vertical-align: bottom;
+  border-bottom: 2px solid #ddd;
+}
+.table > caption + thead > tr:first-child > th,
+.table > colgroup + thead > tr:first-child > th,
+.table > thead:first-child > tr:first-child > th,
+.table > caption + thead > tr:first-child > td,
+.table > colgroup + thead > tr:first-child > td,
+.table > thead:first-child > tr:first-child > td {
+  border-top: 0;
+}
+
+.table > tbody + tbody {
+  border-top: 2px solid #ddd;
+}
+.table .table {
+  background-color: #fff;
+}
+.table-condensed > thead > tr > th,
+.table-condensed > tbody > tr > th,
+.table-condensed > tfoot > tr > th,
+.table-condensed > thead > tr > td,
+.table-condensed > tbody > tr > td,
+.table-condensed > tfoot > tr > td {
+  padding: 5px;
+}
+.table-bordered {
+  border: 1px solid #ddd;
+}
+.table-bordered > thead > tr > th,
+.table-bordered > tbody > tr > th,
+.table-bordered > tfoot > tr > th,
+.table-bordered > thead > tr > td,
+.table-bordered > tbody > tr > td,
+.table-bordered > tfoot > tr > td {
+  border: 1px solid #ddd;
+}
+.table-bordered > thead > tr > th,
+.table-bordered > thead > tr > td {
+  border-bottom-width: 2px;
+}
+.table-striped > tbody > tr:nth-of-type(odd) {
+  background-color: #f9f9f9;
+}
+.table-hover > tbody > tr:hover {
+  background-color: #f5f5f5;
+}
+table col[class*="col-"] {
+  position: static;
+  display: table-column;
+  float: none;
+}
+table td[class*="col-"],
+table th[class*="col-"] {
+  position: static;
+  display: table-cell;
+  float: none;
+}
+.table > tbody > tr > td.ph{
+  text-align: center;
+  color: #ACB3C0;
+  background-color: #f8f8f8;
+  letter-spacing:0.02em;
+  text-transform: uppercase;
+}
+.table > thead > tr > th.ph{
+  font-weight: normal;
+  text-align: left;
+  color: #ACB3C0;
+  background-color: #f8f8f8;
+  letter-spacing:0.02em;
+  text-transform: uppercase;
+}
+
+.panel {
+  margin-bottom: 20px;
+  background-color: #fff;
+  border: 1px solid transparent;
+  -webkit-box-shadow: 0 1px 5px rgba(0,0,0,0.15);
+          box-shadow: 0 1px 5px rgba(0,0,0,0.15);
+}
+.panel-heading {
+  padding: 10px 15px;
+  border-bottom: 1px solid transparent;
+  color: #ACB3C0;
+  letter-spacing: .02em;
+  text-transform: uppercase;
+}
+.panel-heading > .dropdown .dropdown-toggle {
+  color: inherit;
+}
+.panel-title {
+  margin-top: 0;
+  margin-bottom: 0;
+  font-size: 16px;
+  color: inherit;
+}
+.panel-title > a,
+.panel-title > small,
+.panel-title > .small,
+.panel-title > small > a,
+.panel-title > .small > a {
+  color: inherit;
+}
+.panel-footer {
+  padding: 10px 15px;
+  background-color: #f8f8f8;
+  border-top: 1px solid #ddd;
+}
+.panel > .list-group,
+.panel > .panel-collapse > .list-group {
+  margin-bottom: 0;
+}
+.panel > .list-group .list-group-item,
+.panel > .panel-collapse > .list-group .list-group-item {
+  border-width: 1px 0;
+  border-radius: 0;
+}
+.panel > .list-group:first-child .list-group-item:first-child,
+.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {
+  border-top: 0;
+  border-top-left-radius: 3px;
+  border-top-right-radius: 3px;
+}
+.panel > .list-group:last-child .list-group-item:last-child,
+.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {
+  border-bottom: 0;
+  border-bottom-right-radius: 3px;
+  border-bottom-left-radius: 3px;
+}
+.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child {
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+.panel-heading + .list-group .list-group-item:first-child {
+  border-top-width: 0;
+}
+.list-group + .panel-footer {
+  border-top-width: 0;
+}
+.panel .table,
+.panel > .table-responsive > .table,
+.panel > .panel-collapse > .table {
+  margin-bottom: 0px !important;
+}
+.panel > .table caption,
+.panel > .table-responsive > .table caption,
+.panel > .panel-collapse > .table caption {
+  padding-right: 15px;
+  padding-left: 15px;
+}
+.panel > .panel-body + .table,
+.panel > .panel-body + .table-responsive,
+.panel > .table + .panel-body,
+.panel > .table-responsive + .panel-body {
+  border-top: 1px solid #ddd;
+}
+.panel > .table > tbody:first-child > tr:first-child th,
+.panel > .table > tbody:first-child > tr:first-child td {
+  border-top: 0;
+}
+.panel > .table > tbody:last-child > tr:last-child th,
+.panel > .table > tbody:last-child > tr:last-child td {
+  border-bottom: 1px solid #ddd;
+}
+.panel > .table-responsive {
+  margin-bottom: 0;
+  border: 0;
+}
+.panel-group {
+  margin-bottom: 20px;
+}
+.panel-group .panel {
+  margin-bottom: 0;
+  border-radius: 4px;
+}
+.panel-group .panel + .panel {
+  margin-top: 5px;
+}
+.panel-group .panel-heading {
+  border-bottom: 0;
+}
+.panel-group .panel-heading + .panel-collapse > .panel-body,
+.panel-group .panel-heading + .panel-collapse > .list-group {
+  border-top: 1px solid #ddd;
+}
+.panel-group .panel-footer {
+  border-top: 0;
+}
+.panel-group .panel-footer + .panel-collapse .panel-body {
+  border-bottom: 1px solid #ddd;
+}
+.panel-default {
+  border-color: #ddd;
+}
+.panel-default > .panel-heading {
+  color: #ACB3C0;
+  background-color: #f8f8f8;
+  border-top: 1px solid #ddd;
+  /*border-color: #ddd;*/
+}
+.panel-default > .panel-heading + .panel-collapse > .panel-body {
+  border-top-color: #ddd;
+}
+.panel-default > .panel-heading .badge {
+  color: #f5f5f5;
+  background-color: #333;
+}
+.panel-default > .panel-footer + .panel-collapse > .panel-body {
+  border-bottom-color: #ddd;
+}
+.wrapper{
+  width:960px;
+  margin: 0 auto 15px;
+}
+.wrapper h1 {
+  text-transform: uppercase;
+  font-weight: normal;
+  color: #747589;
+}
+.wrapper-default{
+  background-color: #fff;
+  box-shadow: 0 1px 5px rgba(0,0,0,0.15);
+}
+.wrapper-default > tbody > tr > th{
+  border: none;
+}
+.wrapper-default .none-border{
+  border: none;
+}
+.active{text-decoration: none; color: red;}
+
+.cont-2,.cont-3 {float: left; padding-right: 20px;}
+.cont-2 {width: 47%;}
+.cont-3 {width: 33.3%}
+.hidden{display: none; opacity: 0;}
+.edit-btn{
+  background: transparent;
+  border: 1px solid #d2d2d2;
+  color: #2e2e2e;
+  border-radius: 2px;
+  float: right;
+}
+
+.save-btn{
+  background: transparent;
+  border: 1px solid #d2d2d2;
+  color: #2e2e2e;
+  border-radius: 2px;
+
+}
+#menu-icon {
+  display: none;
+  width: 50px;
+  height: 100px;
+  float: right;
+  background:#01a1be;
+}
+a:hover#menu-icon {
+  background: #444;
+}
+
+.tb-stat > tbody > tr > td:last-child {
+  width: 34%;
+  text-align: center;
+}
+.tb-info > tbody > tr > td:last-child {
+  width: 34%;
+}
+/*MEDIA QUERY*/
+@media only print{
+  .navbar > ul, .navbar:active > ul { 
+    display: none;
+  }
+}
+@media only screen and (max-width : 768px) {
+  .tb-stat > tbody > tr > td:last-child {
+    /*width: auto;*/
+  }
+  h1{font-size: 1.7em;}
+  /*.navbar{position: fixed ; width: 100%; height: 100px; top: 0; z-index: 1000;}*/
+  /*.wrapper{margin-top: 121px;}*/
+  #menu-icon {display:inline-block;}
+  .navbar > ul, .navbar:active > ul { 
+    display: none;
+    position: absolute;
+    padding: 0;
+    background: #fff;
+    border: 5px solid #01a1be;
+    right: 20px;
+    top: 60px;
+    width: 50%;
+    z-index: 100;
+  }
+  .navbar > li, .navbar-nav > li {
+    text-align: center;
+    float: none;
+    width: 100%;
+    padding:0;
+  }
+  .nav > li {display: inline-block;}
+  .navbar > li > a {
+    font-size: 1.5em;
+    padding: 0;
+  }
+  .nav > li > a {
+    padding: 1em 0;
+  }
+  .nav > li > a.active{
+    background: #e7e7e7;
+    color: #424f54;
+    border: none;
+    padding: 1em 0;
+  }
+  .navbar:hover > ul {display: block;}
+  .wrapper{
+    width:95%;
+    padding: 0 0.5em;
+  }
+  /*.cont-2 {width: 100%; padding: 0;}
+  .cont-3 {width: 100%; padding: 0;}*/
+}
+
+@media (max-width: 720px) {
+  .cont-2 {width: 100%; padding: 0;}
+  .cont-3 {width: 100%; padding: 0;}
+}
+
+.btn {
+  display: inline-block;
+  padding: 6px 12px;
+  margin-bottom: 0;
+  font-size: 0.9em;
+  font-weight: normal;
+  line-height: 1.6em;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: middle;
+  -ms-touch-action: manipulation;
+      touch-action: manipulation;
+  cursor: pointer;
+  -webkit-user-select: none;
+     -moz-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+  background-image: none;
+  border: 1px solid transparent;
+  border-radius: 0;
+}
+.btn:focus,
+.btn:active:focus,
+.btn.active:focus,
+.btn.focus,
+.btn:active.focus,
+.btn.active.focus {
+  outline: thin dotted;
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+.btn:hover,
+.btn:focus,
+.btn.focus {
+  color: #333;
+  text-decoration: none;
+}
+.btn:active,
+.btn.active {
+  background-image: none;
+  outline: 0;
+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+}
+.btn.disabled,
+.btn[disabled],
+fieldset[disabled] .btn {
+  pointer-events: none;
+  cursor: not-allowed;
+  filter: alpha(opacity=65);
+  -webkit-box-shadow: none;
+          box-shadow: none;
+  opacity: .65;
+}
+.btn-default {
+  color: #333;
+  background-color: #fff;
+  border-color: #ccc;
+}
+.btn-default:hover,
+.btn-default:focus,
+.btn-default.focus,
+.btn-default:active,
+.btn-default.active,
+.open > .dropdown-toggle.btn-default {
+  color: #333;
+  background-color: #e6e6e6;
+  border-color: #adadad;
+}
+.btn-default:active,
+.btn-default.active,
+.open > .dropdown-toggle.btn-default {
+  background-image: none;
+}
+.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active
+{background-color: #fff;border-color: #ccc;}
+
+.btn-default .badge {
+  color: #fff;
+  background-color: #333;
+}
+.btn-primary {
+  color: #fff;
+  background-color: #337ab7;
+  border-color: #3d7ab7;
+}
+.btn-primary:hover,
+.btn-primary:focus,
+.btn-primary.focus,
+.btn-primary:active,
+.btn-primary.active,
+.open > .dropdown-toggle.btn-primary {
+  color: #fff;
+  background-color: #2e6ea5;
+  border-color: #2C5884;
+}
+.btn-primary:active,
+.btn-primary.active,
+.open > .dropdown-toggle.btn-primary {
+  background-image: none;
+}
+.btn-primary.disabled,
+.btn-primary[disabled],
+fieldset[disabled] .btn-primary,
+.btn-primary.disabled:hover,
+.btn-primary[disabled]:hover,
+fieldset[disabled] .btn-primary:hover,
+.btn-primary.disabled:focus,
+.btn-primary[disabled]:focus,
+fieldset[disabled] .btn-primary:focus,
+.btn-primary.disabled.focus,
+.btn-primary[disabled].focus,
+fieldset[disabled] .btn-primary.focus,
+.btn-primary.disabled:active,
+.btn-primary[disabled]:active,
+fieldset[disabled] .btn-primary:active,
+.btn-primary.disabled.active,
+.btn-primary[disabled].active,
+fieldset[disabled] .btn-primary.active {
+  background-color: #337ab7;
+  border-color: #3d7ab7;
+}
+.btn-primary .badge {
+  color: #337ab7;
+  background-color: #fff;
+}
+.btn-success {
+  color: #fff;
+  background-color: #5cb85c;
+  border-color: #4cae4c;
+}
+.btn-success:hover,
+.btn-success:focus,
+.btn-success.focus,
+.btn-success:active,
+.btn-success.active,
+.open > .dropdown-toggle.btn-success {
+  color: #fff;
+  background-color: #449d44;
+  border-color: #398439;
+}
+.btn-success:active,
+.btn-success.active,
+.open > .dropdown-toggle.btn-success {
+  background-image: none;
+}
+.btn-success.disabled,
+.btn-success[disabled],
+fieldset[disabled] .btn-success,
+.btn-success.disabled:hover,
+.btn-success[disabled]:hover,
+fieldset[disabled] .btn-success:hover,
+.btn-success.disabled:focus,
+.btn-success[disabled]:focus,
+fieldset[disabled] .btn-success:focus,
+.btn-success.disabled.focus,
+.btn-success[disabled].focus,
+fieldset[disabled] .btn-success.focus,
+.btn-success.disabled:active,
+.btn-success[disabled]:active,
+fieldset[disabled] .btn-success:active,
+.btn-success.disabled.active,
+.btn-success[disabled].active,
+fieldset[disabled] .btn-success.active {
+  background-color: #5cb85c;
+  border-color: #4cae4c;
+}
+.btn-success .badge {
+  color: #5cb85c;
+  background-color: #fff;
+}
+.btn-info {
+  color: #fff;
+  background-color: #5bc0de;
+  border-color: #46b8da;
+}
+.btn-info:hover,
+.btn-info:focus,
+.btn-info.focus,
+.btn-info:active,
+.btn-info.active,
+.open > .dropdown-toggle.btn-info {
+  color: #fff;
+  background-color: #31b0d5;
+  border-color: #269abc;
+}
+.btn-info:active,
+.btn-info.active,
+.open > .dropdown-toggle.btn-info {
+  background-image: none;
+}
+.btn-info.disabled,
+.btn-info[disabled],
+fieldset[disabled] .btn-info,
+.btn-info.disabled:hover,
+.btn-info[disabled]:hover,
+fieldset[disabled] .btn-info:hover,
+.btn-info.disabled:focus,
+.btn-info[disabled]:focus,
+fieldset[disabled] .btn-info:focus,
+.btn-info.disabled.focus,
+.btn-info[disabled].focus,
+fieldset[disabled] .btn-info.focus,
+.btn-info.disabled:active,
+.btn-info[disabled]:active,
+fieldset[disabled] .btn-info:active,
+.btn-info.disabled.active,
+.btn-info[disabled].active,
+fieldset[disabled] .btn-info.active {
+  background-color: #5bc0de;
+  border-color: #46b8da;
+}
+.btn-info .badge {
+  color: #5bc0de;
+  background-color: #fff;
+}
+.btn-warning {
+  color: #fff;
+  background-color: #f0ad4e;
+  border-color: #eea236;
+}
+.btn-warning:hover,
+.btn-warning:focus,
+.btn-warning.focus,
+.btn-warning:active,
+.btn-warning.active,
+.open > .dropdown-toggle.btn-warning {
+  color: #fff;
+  background-color: #ec971f;
+  border-color: #d58512;
+}
+.btn-warning:active,
+.btn-warning.active,
+.open > .dropdown-toggle.btn-warning {
+  background-image: none;
+}
+.btn-warning.disabled,
+.btn-warning[disabled],
+fieldset[disabled] .btn-warning,
+.btn-warning.disabled:hover,
+.btn-warning[disabled]:hover,
+fieldset[disabled] .btn-warning:hover,
+.btn-warning.disabled:focus,
+.btn-warning[disabled]:focus,
+fieldset[disabled] .btn-warning:focus,
+.btn-warning.disabled.focus,
+.btn-warning[disabled].focus,
+fieldset[disabled] .btn-warning.focus,
+.btn-warning.disabled:active,
+.btn-warning[disabled]:active,
+fieldset[disabled] .btn-warning:active,
+.btn-warning.disabled.active,
+.btn-warning[disabled].active,
+fieldset[disabled] .btn-warning.active {
+  background-color: #f0ad4e;
+  border-color: #eea236;
+}
+.btn-warning .badge {
+  color: #f0ad4e;
+  background-color: #fff;
+}
+.btn-danger {
+  color: #fff;
+  background-color: #d9534f;
+  border-color: #d43f3a;
+}
+.btn-danger:hover,
+.btn-danger:focus,
+.btn-danger.focus,
+.btn-danger:active,
+.btn-danger.active,
+.open > .dropdown-toggle.btn-danger {
+  color: #fff;
+  background-color: #c9302c;
+  border-color: #ac2925;
+}
+.btn-danger:active,
+.btn-danger.active,
+.open > .dropdown-toggle.btn-danger {
+  background-image: none;
+}
+.btn-danger.disabled,
+.btn-danger[disabled],
+fieldset[disabled] .btn-danger,
+.btn-danger.disabled:hover,
+.btn-danger[disabled]:hover,
+fieldset[disabled] .btn-danger:hover,
+.btn-danger.disabled:focus,
+.btn-danger[disabled]:focus,
+fieldset[disabled] .btn-danger:focus,
+.btn-danger.disabled.focus,
+.btn-danger[disabled].focus,
+fieldset[disabled] .btn-danger.focus,
+.btn-danger.disabled:active,
+.btn-danger[disabled]:active,
+fieldset[disabled] .btn-danger:active,
+.btn-danger.disabled.active,
+.btn-danger[disabled].active,
+fieldset[disabled] .btn-danger.active {
+  background-color: #d9534f;
+  border-color: #d43f3a;
+}
+.btn-danger .badge {
+  color: #d9534f;
+  background-color: #fff;
+}
+.btn-link {
+  font-weight: normal;
+  color: #337ab7;
+  border-radius: 0;
+}
+.btn-link,
+.btn-link:active,
+.btn-link.active,
+.btn-link[disabled],
+fieldset[disabled] .btn-link {
+  background-color: transparent;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+.btn-link,
+.btn-link:hover,
+.btn-link:focus,
+.btn-link:active {
+  border-color: transparent;
+}
+.btn-link:hover,
+.btn-link:focus {
+  color: #ec051a;
+  text-decoration: underline;
+  background-color: transparent;
+}
+.btn-link[disabled]:hover,
+fieldset[disabled] .btn-link:hover,
+.btn-link[disabled]:focus,
+fieldset[disabled] .btn-link:focus {
+  color: #777;
+  text-decoration: none;
+}
+label {
+  line-height: 1;
+  display: inline-block;
+  padding-top: 5px; 
+}
+.checkbox{
+  display: block;
+  padding-top: 5px;
+}
+.w1h1ie {
+  width: 100%;
+  height: 100%;
+}
+
+
+
+.spinBox{
+  position      : relative;
+  display       : inline-block;
+  padding-bottom: 5px;
+  -webkit-appearance: none;
+}
+
+.spinBox input{
+  display       : block;
+  border        : 0.0625em solid rgb(224,224,224);
+  border-right  : none;
+  text-align    : center;
+  -webkit-appearance: none;
+  padding: 8px 12px;
+  font-size: 14px;
+  color: #555;
+  background-color: #fff;
+  background-image: none;
+  border: 1px solid #ccc;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+}
+.spinBox input:focus {
+  border-color: #66afe9;
+  outline: 0;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+          box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+}
+.spinBox input[disabled]{
+  background : rgb(240,240,240);
+}
+
+.spinBoxUp,
+.spinBoxDown{
+  position         : absolute;
+  display          : block;
+  width            : 2em;
+  height           : 2em;
+  border           : 0.0625em solid rgb(224,224,224);
+  background       : rgb(248,248,248);
+  cursor           : pointer;
+  font-weight: bold;
+}
+
+.spinBox input[disabled] + span,
+.spinBox input[disabled] + span + span{
+  background : rgb(240,240,240);
+  cursor     : auto;
+}
+
+.spinBoxUp span,
+.spinBoxDown span{
+  display : block;
+  width   : 1em;
+  height  : 1em;
+}
+
+.spinBoxUp{
+  text-align: center;
+  line-height: 2em;
+  vertical-align:middle;
+  right: 0;
+  top: 0;
+}
+.spinBoxDown{
+  vertical-align:middle;
+  line-height: 2em;
+  text-align: center;
+  top: 0;
+  left: 0;
+}
+.spinboxMark{
+  position: absolute;
+  text-align: center;
+  font-style: italic;
+  padding: 0.3em;
+  height  : 1.59em;
+}
+a.btn {text-decoration: none;}
+[role="button"] {cursor: pointer;}
+
+div.tabset {
+  margin:1em 0;
+  width:40em; /* need width for ie < 8 */
+  min-width: 100%;
+}
+div.tabpane { 
+  display:none;
+  clear:both;
+  padding:10px 15px;
+  border:solid 1px #e7e7e7;
+  background-color: #fff;
+  box-shadow:0 1px 5px rgba(0,0,0,0.15);
+}
+div.tabpane:after{content: "&nbsp;"; visibility: hidden;}
+div.activePane { 
+  display:block;
+  z-index: 2;
+  overflow: hidden;
+}
+ul.tabnavs {
+  display:block; /* display:none initially */
+  margin:0 0 -1px 0;
+  padding:0 10px;
+  overflow: hidden;
+  list-style:none;
+  width: 100%;
+}
+ul.tabnavs li { 
+  display:inline;
+  z-index: 3;
+}
+ul.tabnavs li a {
+  text-decoration:none;
+  background-color:#f8f8f8;
+  border:solid 1px #e7e7e7;
+  padding:10px 15px; 
+  margin:0 -1px 0 0; /* gap between tabs */
+  float:left; width:auto;
+  
+  position:relative; top:1px; /* brings down over pane border-top */
+}
+ul.tabnavs li a:link, ul.tabnavs li a:visited {
+  color:#868686;
+}
+ul.tabnavs li a:hover {
+  background-color:#fff;
+}
+ul.tabnavs li a.activeTab:link, 
+ul.tabnavs li a.activeTab:visited {
+  color: #337ab7;
+  background-color:#fff;
+  border-bottom: 1px solid #fff;
+  box-shadow: 0 1px 5px rgba(0,0,0,0.15);
+  z-index: 1;
+}
+
+@media (max-width: 768px) {
+  ul.tabnavs {display: none;}
+  div.tabpane {display: block; border: none; margin-bottom: 10px;}
+  div.tabset {width: 100%;}
+}
+
+ul#validation-box{
+  display: none;
+  padding: 10px 30px;
+  background: rgba(235, 69, 69, 0.80);
+  border: 1px solid rgba(235, 69, 69, 1);
+  color: #fff;
+}
+
+
+
+.form-control {
+  display: inline-block;
+  /*width: 100%;*/
+  /*height: 26px;*/
+  padding: 6px 12px;
+  font-size: 14px;
+  line-height: 1.42857143;
+  color: #555;
+  background-color: #fff;
+  background-image: none;
+  border: 1px solid #ccc;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+}
+.form-control:focus {
+  border-color: #66afe9;
+  outline: 0;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+          box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
+}
+.form-control::-moz-placeholder {color: #999;opacity: 1;}
+.form-control:-ms-input-placeholder {color: #999;}
+.form-control::-webkit-input-placeholder {color: #999;}
+.form-control::-ms-expand {background-color: transparent;border: 0;}
+.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control {background-color: #eee;opacity: 1;}
+.form-control[disabled],fieldset[disabled] .form-control {cursor: not-allowed;}
+textarea.form-control {height: auto;}
+input[type="search"] {-webkit-appearance: none;}
+@media screen and (-webkit-min-device-pixel-ratio: 0) {
+  input[type="date"].form-control,
+  input[type="time"].form-control,
+  input[type="datetime-local"].form-control,
+  input[type="month"].form-control {
+    line-height: 34px;
+  }
+}
+/*.form-group {
+  margin-bottom: 15px;
+}*/
+.radio,
+.checkbox {
+  position: relative;
+  display: block;
+  margin-top: 10px;
+  margin-bottom: 10px;
+}
+.radio label,
+.checkbox label {
+  min-height: 20px;
+  padding-left: 20px;
+  margin-bottom: 0;
+  font-weight: normal;
+  cursor: pointer;
+}
+.radio input[type="radio"],
+.radio-inline input[type="radio"],
+.checkbox input[type="checkbox"],
+.checkbox-inline input[type="checkbox"] {
+  position: absolute;
+  margin-top: 4px \9;
+  margin-left: -20px;
+}
+.radio + .radio,
+.checkbox + .checkbox {
+  margin-top: -5px;
+}
+.radio-inline,
+.checkbox-inline {
+  position: relative;
+  display: inline-block;
+  padding-left: 20px;
+  margin-bottom: 0;
+  font-weight: normal;
+  vertical-align: middle;
+  cursor: pointer;
+}
+.radio-inline + .radio-inline,
+.checkbox-inline + .checkbox-inline {
+  margin-top: 0;
+  margin-left: 10px;
+}
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+input[type="radio"].disabled,
+input[type="checkbox"].disabled,
+fieldset[disabled] input[type="radio"],
+fieldset[disabled] input[type="checkbox"] {
+  cursor: not-allowed;
+}
+.radio-inline.disabled,
+.checkbox-inline.disabled,
+fieldset[disabled] .radio-inline,
+fieldset[disabled] .checkbox-inline {
+  cursor: not-allowed;
+}
+.radio.disabled label,
+.checkbox.disabled label,
+fieldset[disabled] .radio label,
+fieldset[disabled] .checkbox label {
+  cursor: not-allowed;
+}
+.form-control-static {
+  min-height: 34px;
+  padding-top: 7px;
+  padding-bottom: 7px;
+  margin-bottom: 0;
+}
+
+#count-wrap {
+  display: none;
+  position: fixed;
+  top: 0%;
+  left: 0%;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0,0,0,0.8);
+  z-index:1001;
+}
+
+#countdown{
+  display: none;
+  position: relative;
+  text-align: center;
+  top: 40%;
+  margin: 0 auto;
+/*  left: 33%;
+  width: 25%;*/
+  padding: 16px;
+  border: 16px solid #f0ad4e;
+  background-color: white;
+  box-shadow:0 1px 5px rgba(0,0,0,0.15);
+  z-index:102;
+  overflow: auto;
+}
+#checkUpdatePass{
+  display: none;
+  position: relative;
+  text-align: center;
+  top: 30%;
+  margin: 0 auto;
+  width: 300px;
+  padding: 16px;
+  border: 16px solid #5cb85c;
+  background-color: white;
+  box-shadow:0 1px 5px rgba(0,0,0,0.15);
+  z-index:103;
+  overflow: auto;
+}
+
+#apply-settings {
+  width: 100%;
+  display: none;
+  text-align: center;
+  margin: 0 auto;
+  padding: 1em 0;
+  background: #ffcc00;
+  color: #333;
+  font-weight: 500;
+}
+#device-profilaction {
+  width: 100%;
+  display: none;
+  text-align: center;
+  margin: 0 auto;
+  padding: 1em 0;
+  background: #009966;
+  color: #fff;
+  font-weight: 500;
+}
+#device-error {
+  width: 100%;
+  display: none;
+  text-align: center;
+  margin: 0 auto;
+  padding: 1em 0;
+  background: #ec0035;
+  color: #fff;
+  font-weight: 500;
+}
+
+#row-batvolt1,
+#row-batvolt2,
+#row-batvolt3,
+#row-batvolt4{
+  transition: all 1s ease-in-out;
+}

+ 1538 - 0
web_interface/src/wui/main.js

@@ -0,0 +1,1538 @@
+function $(id) {return document.getElementById(id);}
+
+function getCookie(name){
+  "use strict";
+  var strCookie = document.cookie,
+      arrCookie = strCookie.split('; ');
+  for (var i = 0; i < arrCookie.length; i++){
+    var arr = arrCookie[i].split('=');
+    if (arr[0] == name) return window.unescape(arr[1]);
+  }
+  return '';
+}
+
+function setCookie(name,value,expirehours){
+  var cookieString = name + '=' + window.escape(value);
+  if (expirehours > 0) {
+    var date = new Date();
+    date.setTime(date.getTime() + expirehours * 3600 * 1000);
+    cookieString = cookieString + '; expires=' + date.toGMTString();
+  }
+  document.cookie = cookieString;
+}
+
+function deleteCookie(name) {
+  document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;';
+}
+
+// ############### Mobile Device ################
+
+if (window.screen.width < 769) {
+  $('nav').style.display = 'none';
+}
+
+$('menu-icon').onclick = function(){
+  $('nav').style.display = $('nav').style.display == 'none' ? 'block' : 'none';
+};
+
+//########## Main Page Alert Handler ############
+
+// fadeIn animation special for alerts
+function fadeIns(el){
+  el.style.opacity = 0;
+  (function fade() {
+    var val = parseFloat(el.style.opacity);
+    if (((val += 0.03) > 1) !== true) {
+      el.style.opacity = val;
+      var request = requestFrame('request');
+      request(fade);
+    }
+  })();
+}
+
+var ac          = $('AC'),
+    dc          = $('DC'),
+    in_freq     = $('in_freq'),
+    out_freq    = $('out_freq'),
+    pwr         = $('pwr'),
+    bat_cap     = $('bat_cap'),
+    inner_temp  = $('inner_temp'),
+    battimeleft = $('bat_time_left'),
+    alarm       = $('alarm'),
+    sPower      = $('sPower'),
+    sBattery    = $('sBattery');
+var battempCtrlChecked;
+
+function dataFadeIn(){
+  var x = [
+    ac,
+    dc,
+    in_freq,
+    out_freq,
+    pwr,
+    bat_cap,
+    inner_temp,
+    battimeleft,
+    sPower,
+    sBattery
+  ];
+  for (var i = 0; i < x.length; i++){
+    fadeIns(x[i]);
+  }
+}
+
+// Alarm colors
+var ALARM_RED    = '#ff5050',
+    ALARM_YELLOW = '#f0ad4e',
+    ALARM_GREEN  = '#33cc00';
+
+function checkState() {
+
+  // Asign variable to alarm bit mask number
+  var INPUT_VOLTAGE_H = 0,
+      INPUT_VOLTAGE_L = 1,
+      OUTPUT_VOLTAGE  = 2,
+      CURRENT_CONS    = 3,
+      LOAD_VOLTAGE    = 4,
+      LOAD_CURRENT    = 5,
+      INNER_TEMP      = 6,
+      DOOR_SENS       = 7,
+      ACB_TEMP        = 8,
+      ACB_VOLTAGE     = 9,
+      ACB_CURRENT     = 10,
+      ACB_SYMMETRY    = 11,
+      CURRENT_CONS_A  = 12;
+
+  // Alarm bit mask parser
+  function A (num) {
+    var a = alarm.innerHTML;
+    var b = parseInt(a.split('').reverse().join(''), 2);
+    var answ = (b & (1 << num)) >> num;
+    return answ;
+  }
+  function rgb2hex(rgb) {
+    if (/^#[0-9A-F]{6}$/i.test(rgb)) return rgb;
+
+    rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
+    function hex(x) {
+      return ("0" + parseInt(x).toString(16)).slice(-2);
+    }
+    return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
+  }
+}
+function panelState(stateControl, panelID){
+  var title = panelID.textContent;
+  if (stateControl){
+    panelID.style.color = ALARM_GREEN;
+    panelID.innerHTML = '<span class="mark-ok"></span>' + title;
+    return;
+  } else {
+    panelID.style.color = ALARM_RED;
+    panelID.innerHTML = '<span class="mark-almaj"></span>' + title;
+    return;
+  }
+}
+
+
+//########## Settings Inform Boxes ############
+
+function HTMLcreate(htmlStr) {
+    var frag = document.createDocumentFragment(),
+        temp = document.createElement('div');
+    temp.innerHTML = htmlStr;
+    while (temp.firstChild) {
+        frag.appendChild(temp.firstChild);
+    }
+    return frag;
+}
+// fade out animation
+function fadeOut(el){
+  el.style.opacity = 1;
+
+  (function fade() {
+    if ((val -= 0.05) < 0) {
+      el.style.display = "none";
+    } else {
+      var request = requestFrame('request');
+      request(fade);
+    }
+  })();
+}
+// fade in animation
+function fadeIn(el, display){
+  el.style.opacity = 0;
+  el.style.display = display || 'block';
+
+  (function fade() {
+    var val = parseFloat(el.style.opacity);
+    if (((val += 0.05) > 1) !== true) {
+      el.style.opacity = val;
+      var request = requestFrame('request');
+      request(fade);
+    }
+  })();
+}
+// Shows confirm box if Network settings were changed
+var netsettings_changed;
+var netsettings_executed = false;
+function netsettings_changedCheck() {
+  if (netsettings_changed == 'true') {
+    if (!netsettings_executed) {
+      netsettings_executed = true;
+      var ch_element = HTMLcreate(
+        '<div id="apply-settings">'+
+          '<p>Внимание, для применения сетевых настроек необходимо их подтвердить.'+
+          ' Иначе они будут сброшены в течении 10 минут.</p>'+
+          '<a href="confirm.cgi" class="btn btn-danger">Подтвердить</a>'+
+        '</div>'
+        );
+      document.body.insertBefore(ch_element, document.body.childNodes[0]);
+      fadeIn($('apply-settings'));
+    }
+  }
+}
+setInterval(netsettings_changedCheck, 100);
+
+var connect_monitor;
+var conmon_executed = false;
+function connect_monitorCheck() {
+  if (connect_monitor) {
+    if (!conmon_executed) {
+      conmon_executed = true;
+      var ch_element = HTMLcreate(
+        '<div>'+
+          '<div id="device-error">'+
+            '<b>Внимание, связь с UPS не установлена!</b>'+
+          '</div>'+
+        '</div>'
+        );
+      document.body.insertBefore(ch_element, document.body.childNodes[0]);
+      fadeIn($('device-error'));
+    }
+  } else {
+    if (conmon_executed) {
+      conmon_executed = false;
+      $('device-error').parentNode.innerHTML = '';
+    }
+  }
+}
+setInterval(connect_monitorCheck, 100);
+
+//########## Settings Form ############
+
+function formValidation(){
+  var read_community   = $('read_community'),
+      write_community  = $('write_community'),
+      managerIP        = $('managerIP'),
+      managerIP2       = $('managerIP2'),
+      managerIP3       = $('managerIP3'),
+      ipaddr           = $('ipaddr'),
+      gw               = $('gw'),
+      mask             = $('mask'),
+      ntpservip        = $('ntpservip');
+
+  var flag = true;
+  if (!$('dhcp').checked) {
+    if(!ValidateIPaddress(ipaddr, ' IP-адрес устройства')) flag = false;
+    if(!ValidateIPaddress(gw,     ' IP-адрес шлюза')) flag = false;
+    if(!ValidateIPaddress(mask,   'а Маска подсети')) flag = false;
+  }
+  if(!ValidateIPaddress(managerIP, ' Сервер SNMP 1')) flag = false;
+  if(!ValidateAlphanumeric(read_community, 'Read Community')) flag = false;
+  if(!ValidateAlphanumeric(write_community, 'Write Community')) flag = false;
+  if(!ValidateIPaddress(managerIP2, ' Сервер SNMP 2')) flag = false;
+  if(!ValidateIPaddress(managerIP3, ' Сервер SNMP 3')) flag = false;
+
+  return flag;
+}
+
+function ValidateIPaddress(ipaddress, z) {
+  var warn = document.createElement('li');
+  warn.innerHTML = 'Неправильно задан' + z + '!';
+  if (/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ipaddress.value)) {
+    return true;
+  }
+  $('validation-box').appendChild(warn);
+  return false;
+}
+
+function ValidateTemperature(minTemp, maxTemp) {
+  var warn = document.createElement('li');
+  warn.innerHTML = 'Верхняя граница температуры должна превышать нижнюю!';
+  if (Number(minTemp.value) < Number(maxTemp.value)) {
+    return true;
+  }
+  $('validation-box').appendChild(warn);
+  return false;
+}
+
+function ValidateMinMax(elem, ID, name) {
+  var warn = document.createElement('li');
+  var minValue = elem.options.minimum;
+  var maxValue = elem.options.maximum;
+  var curValue = $(ID).childNodes[0].value; 
+  var boolVale = true;
+  if (curValue < minValue) {
+    warn.innerHTML = 'Значение ' + name + ' не может быть ниже ' + minValue;
+    $('validation-box').appendChild(warn);
+    boolVale = false;
+  }
+  else if (curValue > maxValue) {
+    warn.innerHTML = 'Значение ' + name + ' не может быть выше ' + maxValue;
+    $('validation-box').appendChild(warn);
+    boolVale = false;
+  } else { boolVale = true; }
+  return boolVale;
+}
+
+function ValidateAlphanumeric(uadd, z) {
+  var warn = document.createElement('li');
+  var letter = /^[0-9a-zA-Z]+$/;
+  warn.innerHTML = 'Поле ' + z + ' может содержать только латинские буквы и цифры';
+  if (letter.test(uadd.value)) {return true;}
+  $('validation-box').appendChild(warn);
+  return false;
+}
+
+function submitForms() {
+  if (confirm("Вы уверены что хотите применить настройки?")) {
+    if ($('ntp-inp')) {
+      $('ntpservip').options[3].value = $('ntp-inp').value;
+    }
+    if (formValidation()) {
+      $('form1').submit();
+      netsettings_changedCheck();
+      connect_monitor_changedCheck();
+    }
+    else{
+      $('validation-box').style.display = 'block';
+    }
+  }
+}
+
+var pwdIsCorrect;
+function loadXMLDoc(url, method, callback) {
+  var xmlhttp;
+  if (window.XMLHttpRequest) {
+    xmlhttp = new XMLHttpRequest();
+  } else {
+    xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
+  }
+  xmlhttp.open(method, url, true);
+  var status;
+  xmlhttp.onreadystatechange = function () {
+    if (xmlhttp.readyState == 4) {
+      status = xmlhttp.status;
+      if (status == 200) {
+        pwdIsCorrect = xmlhttp.responseText;
+        if (typeof callback == 'function') {
+          callback.apply(xmlhttp);
+        }
+      } else {
+        console.log('Error');
+      }
+    }
+  };
+  xmlhttp.send();
+}
+function checkPWD(){
+  var pass = $('pwd').value;
+  var letter = /^[0-9a-zA-Z]+$/;
+  var postPass;
+  if (letter.test(pass)) {
+    loadXMLDoc(
+      'checkpwd.cgi?password='+pass,
+      'POST',
+      function() {
+        postPass  = parseInt(this.responseText);
+        if (postPass == 1) {
+          $('checkUpdatePass').style.display = 'none';
+          $('countdown').style.display = 'block';
+          countdown();
+        } else {
+          alert('Пароль задан неправильно');
+        }
+      }
+    );
+  } else if (pass.length === 0){
+    alert('Введите пароль');
+  }else {
+    alert('Пароль задан неправильно');
+  }
+}
+
+function countdown(rel) {
+  var countDown = 5;
+  setInterval(function () {
+    if (countDown == 1 && rel === true) {
+      location.reload(true);
+    }
+    else if (countDown == 1 && rel !== true){
+      window.location.href = '/';
+    }
+    if (countDown > 0){countDown--;}
+    $('count-number').innerHTML = countDown;
+    return countDown;
+  }, 1000);
+}
+
+function dhcpState() {
+  if ($('dhcp').checked) {
+    $('ipaddr').setAttribute('disabled', 'disabled');
+    $('gw').setAttribute('disabled', 'disabled');
+    $('mask').setAttribute('disabled', 'disabled');
+  } else {
+    $('ipaddr').removeAttribute('disabled');
+    $('gw').removeAttribute('disabled');
+    $('mask').removeAttribute('disabled');
+  }
+}
+
+//########## Info Form Validation ############
+
+function infoValidation(){
+  var owner = $('owner').value,
+      location = $('location').value,
+      comment = $('comment').value;
+  var flag = true;
+  if(!ValidateAlphanumeric2(owner, 'Владелец')){flag = false;}
+  if(!ValidateAlphanumeric2(location, 'Местоположение')){flag = false;}
+  if(!ValidateAlphanumeric2(comment, 'Комментарии')){flag = false;}
+
+  return flag;
+}
+
+function ValidateAlphanumeric2(uadd, z) {
+  var warn = document.createElement('li');
+  // var letter = /^[0-9a-zA-Z]+[^\u0430-\u044f\u0410-\u042f\u0451\u0401\?]+$/m;
+  var letter = /^[0-9a-zA-Z\ ]+$/m;
+  warn.innerHTML = 'Поле ' + z + ' может содержать только латинские буквы и цифры';
+  if (letter.test(uadd) || !uadd) {
+    return true;
+  }
+  $('validation-box').appendChild(warn);
+  return false;
+}
+
+function submitInfo() {
+  if (confirm("Вы уверены что хотите применить изменения?")) {
+    if (infoValidation()) {
+      $('SNMPinfo').submit();
+      // loadXMLDoc('info.cgi?owner='+ $('owner').value + '&sysLocation='+ $('location').value + '&comment='+ decodeURIComponent($('comment').value), 'GET', function(){
+      //   console.log('Сохранено');
+      // });
+    }
+    else {
+      $('validation-box').style.display = 'block';
+    }
+  }
+}
+
+function getJSON(url, successHandler, errorHandler) {
+  var xhr;
+  xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
+  xhr.open('GET', url, true);
+  xhr.onreadystatechange = function () {
+    var status;
+    var data;
+    if (xhr.readyState == 4) {
+      status = xhr.status;
+      if (status == 200) {
+        data = JSON.parse(xhr.responseText);
+        successHandler(data);
+      } else {
+        errorHandler(status);
+      }
+    }
+  };
+  xhr.send();
+}
+
+function getCGI(url) {
+  var xmlhttp;
+  var q, a;
+  if (url == 'reset.cgi') {
+    q = confirm("Вы уверены что хотите сбросить на заводские настройки?");
+    // a = "Сброс отменен.";
+  }
+  if (url == 'reboot.cgi') {
+    q = confirm("Вы уверены что хотите перезагрузить устройство?");
+    // a = "Перезагрузка отменена.";
+  }
+  if (url == 'update.cgi') {
+    q = confirm("Вы уверены что хотите обновить прошивку устройства?");
+    // a = "Обновление отменено.";
+  }
+  if (q) {
+    if (window.XMLHttpRequest) {
+      xmlhttp = new XMLHttpRequest();
+    } else {
+      xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
+    }
+    // xmlhttp.onreadystatechange = true;
+    xmlhttp.open("GET", url, true);
+    xmlhttp.send();
+    return true;
+  }
+  // else {alert(a);}
+}
+
+function paramsRefresh() {
+  getJSON('getJson.cgi'+'?'+Math.random(), function (data) {
+    panelState(!data.line_fail, $('sPower'));
+    panelState(!data.low_battery, $('sBattery'));
+    // PPS
+    $('AC').innerHTML            = parseFloat(data.AC) + ' В';
+    $('AC').style.color          = data.line_fail ? ALARM_RED : '#222';
+    $('DC').innerHTML            = parseFloat(data.DC) + ' В';
+    $('DC').style.color          = data.line_fail ? ALARM_RED : '#222';
+    $('in_freq').innerHTML       = parseFloat(data.in_freq) + ' Гц';
+    $('in_freq').style.color     = data.line_fail ? ALARM_RED : '#222';
+    $('out_freq').innerHTML      = parseFloat(data.out_freq) + ' Гц';
+    $('out_freq').style.color    = data.line_fail ? ALARM_RED : '#222';
+    $('pwr').innerHTML           = parseFloat(data.pwr) + ' %';
+    $('pwr').style.color         = data.load_monitor ? ALARM_RED : '#222';
+    // ACB
+    $('bat_cap').innerHTML       = parseFloat(data.bat_cap) + ' %';
+    $('bat_cap').style.color          = data.low_battery ? ALARM_RED : '#222';
+    $('inner_temp').innerHTML    = parseFloat(data.inner_temp) + ' °C';
+    $('inner_temp').style.color  = data.temp_monitor ? ALARM_RED : '#222';
+    $('bat_time_left').innerHTML = parseFloat(data.bat_time_left) + ' мин';
+    $('bat_time_left').style.color          = data.low_battery ? ALARM_RED : '#222';
+    // State
+    $('alarm').innerHTML         = data.alarm;
+    // General
+    utcParam                     = data.utc;
+    netsettings_changed          = data.netsettings_changed;
+    connect_monitor              = data.connect_monitor;
+    checkState();
+    setTimeout(paramsRefresh, 1000);
+  }, function (status) {
+    if (status !== 0){
+      alert('Не удалось получить данные.');
+      setTimeout(paramsRefresh, 15000);
+    }
+  });
+}
+
+function msieversion() {
+  var ua = window.navigator.userAgent; 
+  var msie = ua.indexOf("MSIE ");
+  if (msie != -1 || !!navigator.userAgent.match(/Trident.*rv\:11\./)) /*If IE, return version number*/ return true;
+  else return false;
+  return false; 
+}
+
+
+// ################################################################################
+
+function settingsGET(){
+  getJSON('settings.cgi', function(data) {
+    //SNMP params
+    $('managerIP').value = data.managerIP;
+    $('read_community').value = data.read_community;
+    $('write_community').value = data.write_community;
+    $('managerIP2').value = data.managerIP2;
+    $('managerIP3').value = data.managerIP3;
+    // Network params
+    $('ipaddr').value   = data.ipaddr;
+    $('gw').value       = data.gw;
+    $('mask').value     = data.mask;
+    $('dhcp').checked   = data.dhcp;
+    dhcpState();
+    netsettings_changed     = data.netsettings_changed;
+    // profilaction_changed    = data.NeedProfilaction;
+  }, function(status) {
+    alert('Не удалось получить данные.');
+  });
+}
+
+// ################################################################################
+
+/*
+
+SpinBox.js
+
+Implements a spin box interface for a text field
+
+Created by Stephen Morley - http://code.stephenmorley.org/ - and released under
+the terms of the CC0 1.0 Universal legal code:
+
+http://creativecommons.org/publicdomain/zero/1.0/legalcode
+
+*/
+
+/* Creates a spin box. The parameter is:
+ *
+ * container - either the DOM node to contain the spin box or the ID of the node
+ * options   - an object containing various parameters; this optional parameter
+ *             defaults to the empty object
+ *
+ * The options object may contain the following keys:
+ *
+ * className - a class name to apply to the container; the class name with the
+ *             suffixes 'Up' and 'Down' will be applied to the up and down
+ *             buttons. The default value is 'spinBox'.
+ * value     - the initial value for the spin box. The default is 0 (subject to
+ *             restrictions on the minimum and maximum value) if the input
+ *             element did not already exist, or the existing value if the input
+ *             element did already exist.
+ * step      - the value by which to increment or decrement the value. The
+ *             default value is 1.
+ * minimum   - the minimum allowed value. The default is not to have a minimum
+ *             value.
+ * maximum   - the maximum allowed value. The default is not to have a maximum
+ *             value.
+ * decimals  - the number of decimal places allowed. The default is 0.
+ * name      - input name attribute.
+ *
+ * Note that the minimum, maximum, and decimal places restrictions are enforced
+ * for values set by the spin box, but a value outside of these restrictions may
+ * be typed by the user.
+ */
+function SpinBox(container, options) {
+
+  // fetch the DOM node if a string was supplied
+  if (typeof container == 'string') {
+    container = document.getElementById(container);
+  }
+
+  // store the options and set the default values
+  this.options = (options ? options : {});
+  if (!('className' in this.options)) this.options.className = 'spinBox';
+  if (!('step'      in this.options)) this.options.step      = 1;
+  if (!('decimals'  in this.options)) this.options.decimals  = 0;
+
+  // check whether the input field should be created
+  var inputs = container.getElementsByTagName('input');
+  if (inputs.length === 0) {
+
+    // create the input node
+    this.input = document.createElement('input');
+    this.setValue('value' in this.options ? this.options.value : 0);
+    this.setName('name'   in this.options ? this.options.name : '');
+    container.appendChild(this.input);
+
+  } else {
+
+    // store a reference to the input node
+    this.input = inputs[0];
+    this.setValue(this.options.value ? this.options.value : this.input.value);
+
+  }
+
+  // create the up button
+  var upButton = document.createElement('span');
+  upButton.appendChild(document.createElement('span'));
+  upButton.innerHTML = '+';
+  container.appendChild(upButton);
+
+  // create the down button
+  var downButton = document.createElement('span');
+  downButton.appendChild(document.createElement('span'));
+  downButton.innerHTML = '-';
+  container.appendChild(downButton);
+
+  // apply the classes
+  container.className += ' ' + this.options.className;
+  upButton.className = this.options.className + 'Up';
+  downButton.className = this.options.className + 'Down';
+
+  // add the listeners
+  this.addEventListener(this.input, 'mousewheel',     this.handleMouseWheel, [], true);
+  this.addEventListener(this.input, 'DOMMouseScroll', this.handleMouseWheel, [], true);
+  this.addEventListener(this.input, 'keydown',        this.handleKeyDown, [], true);
+  this.addEventListener(this.input, 'keypress',       this.handleKeyPress, [], true);
+  this.addEventListener(this.input, 'keyup',          this.stop);
+  this.addEventListener(upButton,   'mousedown',      this.start, [true]);
+  this.addEventListener(upButton,   'mouseup',        this.stop);
+  this.addEventListener(upButton,   'mouseout',       this.stop);
+  this.addEventListener(downButton, 'mousedown',      this.start, [false]);
+  this.addEventListener(downButton, 'mouseup',        this.stop);
+  this.addEventListener(downButton, 'mouseout',       this.stop);
+
+}
+
+/* Returns the current value. This will be a number, or the value NaN if the
+ * current contents of the input field do not start with a valid number.
+ */
+SpinBox.prototype.getValue = function () {
+
+  // parse and return the value
+  return parseFloat(this.input.value);
+
+};
+
+SpinBox.prototype.setName = function (name) {
+
+  // asign input name
+  this.input.name = name;
+};
+/* Sets the value. Restrictions on the minimum and maximum value are enforced.
+ * The parameter is:
+ *
+ * value - the value
+ */
+SpinBox.prototype.setValue = function (value) {
+
+  // ensure the value is within the permitted range
+  if ('minimum' in this.options) value = Math.max(this.options.minimum, value);
+  if ('maximum' in this.options) value = Math.min(this.options.maximum, value);
+
+  // store the sign
+  var sign = (value < 0 ? '-' : '');
+  value = Math.abs(value);
+
+  // determine the multiplier for rounding
+  var multiplier = Math.pow(10, this.options.decimals);
+
+  // split the value in to integer and fractional parts
+  value = Math.round(value * multiplier);
+  var integer = (value - value % multiplier) / multiplier;
+  var fractional = '' + value % multiplier;
+
+  // add leading zeros to the fractional part
+  while (fractional.length < this.options.decimals) {
+    fractional = '0' + fractional;
+  }
+
+  // set the value
+  this.input.value =
+      sign + integer + (this.options.decimals > 0 ? '.' + fractional : '');
+
+  // check whether the browser can dispatch events
+  if ('dispatchEvent' in this.input){
+
+    // create the event
+    try{
+      var event = new Event('change', {bubbles : true, cancelable : true});
+    }catch (e){
+      var event = document.createEvent('Event');
+      event.initEvent('change', true, true);
+    }
+
+    // dispatch the event
+    this.input.dispatchEvent(event);
+
+  }
+
+};
+
+/* Adds an event listener to a node. The event listener is bound to the current
+ * value of 'this'. The parameters are:
+ *
+ * node         - the node
+ * event        - the event name
+ * listener     - the listener functions
+ * parameters   - an array of additional parameters to pass to the listener;
+ *                these are placed after the event parameter
+ * allowDefault - true if the default action should not be prevented
+ */
+SpinBox.prototype.addEventListener = function (
+node, event, listener, parameters, allowDefault) {
+
+  // store a reference to the 'this' object
+  var thisObject = this;
+
+  // create the bound listener
+  function boundListener(e) {
+
+    // get the event if it is not supplied
+    if (!e) e = window.event;
+
+    // call the listener
+    listener.apply(thisObject, [e].concat(parameters));
+
+    // prevent the default action if necessary
+    if (!allowDefault) {
+      if (e.preventDefault) {
+        e.preventDefault();
+      } else {
+        e.returnValue = false;
+      }
+    }
+
+  }
+
+  // add the event listener
+  if (node.addEventListener) {
+    node.addEventListener(event, boundListener, false);
+  } else {
+    node.attachEvent('on' + event, boundListener);
+  }
+
+};
+
+/* Handles a mouse wheel event by updating the value if the field is active. The
+ * parameter is:
+ *
+ * e - the event object
+ */
+SpinBox.prototype.handleMouseWheel = function (e) {
+
+  // check whether the field is active
+  if (document.activeElement == this.input) {
+
+    // update the value
+    if (e.wheelDelta) {
+      this.start(e, e.wheelDelta > 1);
+    } else if (e.detail) {
+      this.start(e, e.detail < 1);
+    }
+    this.stop();
+
+    // prevent the default action
+    if (e.preventDefault) {
+      e.preventDefault();
+    } else {
+      e.returnValue = false;
+    }
+
+  }
+
+};
+
+/* Handles a key down event by starting updating if appropriate. The parameter
+ * is:
+ *
+ * e - the event object
+ */
+SpinBox.prototype.handleKeyDown = function (e) {
+
+  // if the up or down keys were pressed, start updating
+  if (e.keyCode == 38) this.start(e, true);
+  if (e.keyCode == 40) this.start(e, false);
+
+};
+
+/* Handles a key press event by filtering out invalid characters. The parameter
+ * is:
+ *
+ * e - the event object
+ */
+SpinBox.prototype.handleKeyPress = function (e) {
+
+  // determine the character code
+  var charCode = ('charCode' in e ? e.charCode : e.keyCode);
+
+  // allow special key presses
+  if (charCode === 0 || e.altKey || e.ctrlKey || e.metaKey) return;
+
+  // allow a minus sign if the value can be negative
+  if (charCode == 45 && (!('minimum' in this.options) || this.options.minimum < 0)) {
+    return;
+  }
+
+  // allow a decimal point if the value may contain decimals
+  if (charCode == 46 && this.options.decimals > 0) return;
+
+  // allow digits
+  if (charCode >= 48 && charCode <= 57) return;
+
+  // prevent the default action
+  if (e.preventDefault) {
+    e.preventDefault();
+  } else {
+    e.returnValue = false;
+  }
+
+};
+
+/* Starts updating the value. The parameters are:
+ *
+ * e  - the event object
+ * up - true to increment the value, false to decrement the value
+ */
+SpinBox.prototype.start = function (e, up) {
+
+  // if the field is disabled or we are already updating, return immediately
+  if (this.input.disabled || 'timeout' in this) return;
+
+  // set the update step
+  this.updateStep = (up ? this.options.step : -this.options.step);
+
+  // initialise the timeout delay
+  this.timeoutDelay = 500;
+
+  // update the value
+  this.update();
+
+};
+
+// Stops update the value.
+SpinBox.prototype.stop = function () {
+
+  // clear the timeout if it exists
+  if ('timeout' in this) {
+    window.clearTimeout(this.timeout);
+    delete this.timeout;
+  }
+
+};
+
+// Updates the value.
+SpinBox.prototype.update = function () {
+
+  // determine the current value
+  var value = parseFloat(this.input.value);
+  if (isNaN(value)) value = 0;
+
+  // update the value
+  this.setValue(value + this.updateStep);
+
+  // reduce the delay
+  this.timeoutDelay = Math.max(20, Math.floor(this.timeoutDelay * 0.9));
+
+  // call this function again
+  var thisObject = this;
+  this.timeout = window.setTimeout(function () { thisObject.update(); }, this.timeoutDelay);
+
+};
+
+
+/*
+  This code is from Dynamic Web Coding at dyn-web.com
+  Copyright 2009-12 by Sharon Paine 
+  See Terms of Use at www.dyn-web.com/business/terms.php
+  regarding conditions under which you may use this code.
+  This notice must be retained in the code as is!
+*/
+
+// DYN_WEB is namespace used for code from dyn-web.com
+// replacing previous use of dw_ prefix for object names
+var DYN_WEB = DYN_WEB || {};
+
+DYN_WEB.Event = {
+  add: document.addEventListener ? function (obj, etype, fp, cap) {
+    cap = cap || false;
+    obj.addEventListener(etype, fp, cap);
+  } : function (obj, etype, fp) {
+    obj.attachEvent("on" + etype, fp);
+  },
+  remove: document.removeEventListener ? function (obj, etype, fp, cap) {
+    cap = cap || false;
+    obj.removeEventListener(etype, fp, cap);
+  } : function (obj, etype, fp) {
+    obj.detachEvent("on" + etype, fp);
+  },
+  DOMit: function (e) {
+    e = e ? e : window.event;
+    if (!e.target) e.target = e.srcElement;
+    if (!e.preventDefault) e.preventDefault = function () {
+      e.returnValue = false;
+      return false;
+    };
+    if (!e.stopPropagation) e.stopPropagation = function () {
+      e.cancelBubble = true;
+    };
+    return e;
+  },
+  getTarget: function (e) {
+    e = dw_Event.DOMit(e);
+    var tgt = e.target;
+    if (tgt.nodeType != 1) tgt = tgt.parentNode;
+    return tgt;
+  }
+};
+
+DYN_WEB.Cookie = {
+  set: function (name, value, days, path, domain, secure) {
+    var date, expires;
+    if (typeof days == "number") {
+      date = new Date();
+      date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
+      expires = date.toGMTString();
+    }
+    document.cookie = name + "=" + encodeURIComponent(value) + ((expires) ? "; expires=" + expires : "") + ((path) ? "; path=" + path : "") + ((domain) ? "; domain=" + domain : "") + ((secure) ? "; secure" : "");
+  },
+  get: function (name) {
+    var c, cookies = document.cookie.split(/;\s/g);
+    for (var i = 0; cookies[i]; i++) {
+      c = cookies[i];
+      if (c.indexOf(name + '=') === 0) {
+        return decodeURIComponent(c.slice(name.length + 1, c.length));
+      }
+    }
+    return null;
+  },
+  del: function (name, path, domain) {
+    if (dw_Cookie.get(name)) {
+      document.cookie = name + "=" + ((path) ? "; path=" + path : "") + ((domain) ? "; domain=" + domain : "") + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
+    }
+  }
+};
+
+if (!DYN_WEB.Util) {
+  DYN_WEB.Util = {
+    // add props of Obj2 to Obj1
+    augment: function (Obj1, Obj2) {
+      var prop;
+      for (prop in Obj2) {
+        if (Obj2.hasOwnProperty(prop) && !Obj1[prop]) {
+          Obj1[prop] = Obj2[prop];
+        }
+      }
+    },
+    $: function (id) {
+      return document.getElementById(id);
+    }
+  };
+}
+
+DYN_WEB.Tabs = (function () {
+
+  // obj holds props: id, useCookies, etc.
+  function Constr(obj) {
+    this.useCookies = obj.useCookies;
+    this.before_hide = obj.before_hide || function () {};
+    this.on_activate = obj.on_activate || function () {};
+    this.on_init = obj.on_init || function () {};
+    Constr.col[obj.id] = this;
+    Constr.init(obj.id);
+  }
+
+  Constr.col = {};
+
+  Constr.prototype = {
+    current: null // id of currently active pane
+  };
+
+  return Constr;
+})();
+
+(function () {
+  var Ut = DYN_WEB.Util,
+    Ev = DYN_WEB.Event,
+    Cookie = DYN_WEB.Cookie,
+    Tabs = DYN_WEB.Tabs;
+
+  Ut.augment(Tabs, {
+    // static props/methods for DYN_WEB.Tabs
+    setup: function () {
+      var obj, settings = [];
+      if (Tabs.hasBrowserSupport()) {
+        for (var i = 0; obj = arguments[i]; i++) {
+          if (obj.css) {
+            Ut.writeStyleSheet(obj.css);
+          }
+          settings[i] = obj; // save to pass to constructor onload
+        }
+        Ev.add(window, 'load', function () {
+          for (var i = 0; settings[i]; i++) {
+            new Tabs(settings[i]);
+          }
+        });
+      }
+    },
+    hasBrowserSupport: function () {
+      if (document.getElementById && document.getElementsByTagName && typeof decodeURI !== 'undefined' && (document.addEventListener || document.attachEvent)) {
+        return true;
+      }
+      return false;
+    },
+
+    init: function (tabsetId) {
+      var _this = Tabs.col[tabsetId],
+        links = Tabs.getTabs(tabsetId),
+        // code uses hash in tab links but tabs can also link to other pages
+        // so compare current location to link's href
+        loc = Ut.getURLtoHash(location),
+        lnk, pg, paneId, pane, sel, dflt, cur, c, q, hash;
+
+      q = Ut.getValueFromQueryString(tabsetId);
+      if (_this.useCookies) {
+        c = Tabs.checkCookie(tabsetId);
+      }
+      // tab in query string or cookie?
+      sel = q ? q : c ? c : null;
+
+      // add ids and event handlers to links ...
+      for (var i = 0; lnk = links[i]; i++) {
+        paneId = '', pane = ''; // reset
+        pg = Ut.getURLtoHash(lnk);
+        hash = Ut.getHash(lnk);
+        if (lnk.hash && (loc == pg)) { // has hash and points to current page
+          paneId = hash;
+          lnk.id = tabsetId + '__' + paneId; // double underscore between
+        }
+        // if tab associated with tabpane, set up onclick fn
+        if (paneId) {
+          try {
+            pane = Ut.$(paneId);
+          } catch (err) { // showClicked fn throws
+          }
+
+          // first tab/pane is default 
+          if (!dflt) {
+            dflt = paneId;
+          }
+
+          // if set active in markup, hold as current
+          if (pane && Ut.hasClass(lnk, 'activeTab') && Ut.hasClass(pane, 'activePane')) {
+            _this.current = cur = paneId;
+          }
+
+          // to pass paneId, tabsetId to showClicked...
+          Ev.add(lnk, 'click', function (paneId, tabsetId) {
+            return function (e) {
+              Tabs.showClicked(e, paneId, tabsetId);
+            };
+          }(paneId, tabsetId));
+        }
+      }
+
+      // check if tab and pane for sel
+      if (sel && (lnk = Ut.$(tabsetId + '__' + sel)) && (pane = Ut.$(sel))) {
+        if (cur && cur !== sel) {
+          Tabs.hideCurrent(tabsetId, cur);
+        }
+        Ut.addClass(lnk, 'activeTab');
+        Ut.addClass(pane, 'activePane');
+        _this.current = sel;
+      } else if (!cur && dflt) {
+        Ut.addClass(Ut.$(tabsetId + '__' + dflt), 'activeTab');
+        Ut.addClass(Ut.$(dflt), 'activePane');
+        _this.current = dflt;
+      }
+      _this.on_init();
+    },
+
+    getTabs: function (id) {
+      var set = Ut.$(id),
+        list, links;
+      if (!set) {
+        throw new Error('No element matching tabset id found.');
+        return [];
+      }
+      list = Ut.getElementsByClassName('tabnavs', 'ul', set);
+      if (list.length === 0) {
+        throw new Error('No tabnavs were found in the tabset.');
+        return [];
+      }
+      links = list[0].getElementsByTagName('a');
+      return links;
+    },
+
+    showClicked: function (e, paneId, tabsetId) {
+      var tabId = tabsetId + '__' + paneId;
+      var _this = Tabs.col[tabsetId];
+      _this.before_hide(tabId, paneId, tabsetId);
+      Tabs.hideCurrent(tabsetId, _this.current);
+      Ut.addClass(Ut.$(tabId), 'activeTab');
+
+      try {
+        Ut.addClass(Ut.$(paneId), 'activePane');
+      } catch (err) {
+        throw new Error('showClicked can\'t find pane associated with tab. Check tab hash and pane id.');
+        return false;
+      }
+
+      _this.current = paneId;
+      if (_this.useCookies) {
+        Tabs.setCookie(tabsetId, paneId);
+      }
+
+      _this.on_activate(tabId, paneId, tabsetId);
+      if (e) {
+        Ev.DOMit(e);
+        e.preventDefault();
+      }
+      return false;
+    },
+
+    hideCurrent: function (tabsetId, cur) {
+      var tab = Ut.$(tabsetId + '__' + cur),
+        pane = Ut.$(cur);
+      Ut.removeClass(tab, 'activeTab');
+      Ut.removeClass(pane, 'activePane');
+    },
+
+    checkCookie: function (tabsetId) {
+      var c, cookies, tab_cookies = Cookie.get('dw_Tabs');
+      if (tab_cookies) {
+        cookies = tab_cookies.split(',');
+        for (var i = 0; cookies[i]; i++) {
+          c = cookies[i];
+          if (c.indexOf(tabsetId + ':') === 0) {
+            return decodeURI(c.slice(tabsetId.length + 1, c.length));
+          }
+        }
+      }
+      return null;
+    },
+
+    setCookie: function (tabsetId, paneId) {
+      // format for cookies (multiple tabsets supported): dw_Tabs=tabsetId:paneId,tabsetId:paneId;
+      var new_tab_cookies = '',
+        cookies,
+        tab_cookies = Cookie.get('dw_Tabs');
+      if (tab_cookies) {
+        cookies = tab_cookies.split(',');
+        for (var i = 0; cookies[i]; i++) {
+          if (cookies[i].indexOf(tabsetId + ':') === 0) {
+            cookies[i] = tabsetId + ':' + paneId;
+            new_tab_cookies = cookies.join(',');
+            break;
+          }
+        }
+        if (!new_tab_cookies) { // if no match for this tabsetId
+          new_tab_cookies = tab_cookies + ',' + tabsetId + ':' + paneId;
+        }
+      } else { // no dw_Tabs set yet
+        new_tab_cookies = tabsetId + ':' + paneId;
+      }
+      Cookie.set('dw_Tabs', new_tab_cookies, null, '/');
+    }
+
+  });
+})();
+
+
+// util
+
+(function () {
+  var Ut = DYN_WEB.Util;
+  // add methods to DYN_WEB.Util
+  Ut.augment(Ut, {
+    trim: function (str) {
+      if (String.prototype.trim) {
+        return str.trim(); // ECMAScript 5
+      } else {
+        var re = /^\s+|\s+$/g;
+        return str.replace(re, '');
+      }
+    },
+
+    normalizeString: function (str) {
+      var re = /\s\s+/g;
+      return Ut.trim(str).replace(re, ' ');
+    },
+
+    hasClass: function (el, cl) {
+      var re = new RegExp('\\b' + cl + '\\b', 'i');
+      if (re.test(el.className)) {
+        return true;
+      }
+      return false;
+    },
+
+    addClass: function (el, cl) {
+      if (!Ut.hasClass(el, cl)) {
+        el.className = Ut.trim(el.className + ' ' + cl);
+      }
+    },
+
+    removeClass: function (el, cl) {
+      el.className = Ut.normalizeString(el.className.replace(cl, " "));
+    },
+
+    getElementsByClassName: function (sClass, sTag, oCont) {
+      oCont = oCont ? oCont : document;
+      if (oCont.getElementsByClassName) {
+        return oCont.getElementsByClassName(sClass); // html5
+      } else {
+        sTag = sTag || '*';
+        var result = [],
+          re = new RegExp('\\b' + sClass + '\\b', 'i'),
+          list = oCont.getElementsByTagName(sTag);
+        for (var i = 0; list[i]; i++) {
+          if (re.test(list[i].className)) result.push(list[i]);
+        }
+        return result;
+      }
+    },
+
+    // obj: link or window.location
+    getValueFromQueryString: function (name, obj) {
+      obj = obj ? obj : window.location;
+      var pairs, set;
+      if (obj.search && obj.search.indexOf(name != -1)) {
+        pairs = obj.search.slice(1).split('&'); // name/value pairs
+        for (var i = 0; pairs[i]; i++) {
+          set = pairs[i].split('='); // Check each pair for match on name 
+          if (set[0] == name && set[1]) {
+            return set[1];
+          }
+        }
+      }
+      return '';
+    },
+
+    getURLtoHash: function (obj) {
+      obj = obj ? obj : window.location;
+      var href = obj.href,
+        pt = href.indexOf('#'),
+        url = (pt != -1) ? href.slice(0, pt) : href; // remove hash from href 
+      return url;
+    },
+
+    getHash: function (obj) {
+      obj = obj ? obj : window.location;
+      var hash = '';
+      if (obj.hash) { // some browsers say true if just #, some '' (ff)
+        hash = obj.hash.slice(1);
+      }
+      return hash;
+    },
+
+    // Alternate functions are available that use DOM methods instead of document.write
+    writeStyleSheet: function (file, bScreen) {
+      var screen = (bScreen !== false) ? '" media="screen" />\n' : '"/>\n';
+      document.write('\n<link rel="stylesheet" href="' + file + screen);
+    },
+
+    // used to set min-height (as on dyn-web home page)
+    writeStyleRule: function (rule, bScreen) {
+      var screen = (bScreen !== false) ? ' media="screen">' : '>';
+      document.write('\n<style type="text/css"' + screen + rule + '</style>');
+    }
+  });
+})();
+
+
+(function (window) {
+
+/**
+ * @param  {String} type - request | cancel | native.
+ * @return {Function} Timing function.
+ */
+function requestFrame(type) {
+    // The only vendor prefixes required.
+    var vendors = ['moz', 'webkit'],
+
+        // Disassembled timing function abbreviations.
+        aF = 'AnimationFrame',
+        rqAF = 'Request' + aF,
+
+        // Final assigned functions.
+        assignedRequestAnimationFrame,
+        assignedCancelAnimationFrame,
+
+        // Initial time of the timing lapse.
+        previousTime = 0,
+
+        mozRAF = window.mozRequestAnimationFrame,
+        mozCAF = window.mozCancelAnimationFrame,
+
+        // Checks for firefox 4 - 10 function pair mismatch.
+        hasMozMismatch = mozRAF && !mozCAF,
+
+        func;
+
+    // Date.now polyfill, mainly for legacy IE versions.
+    if (!Date.now) {
+        Date.now = function() {
+            return new Date().getTime();
+        };
+    }
+
+    /**
+     * hasIOS6RequestAnimationFrameBug.
+     * @See {@Link https://gist.github.com/julienetie/86ac394ec41f1271ff0a}
+     * - for Commentary.
+     * @Copyright 2015 - Julien Etienne. 
+     * @License: MIT.
+     */
+    function hasIOS6RequestAnimationFrameBug() {
+        var webkitRAF = window.webkitRequestAnimationFrame,
+            rAF = window.requestAnimationFrame,
+
+            // CSS/ Device with max for iOS6 Devices.
+            hasMobileDeviceWidth = screen.width <= 768 ? true : false,
+
+            // Only supports webkit prefixed requestAnimtionFrane.
+            requiresWebkitprefix = !(webkitRAF && rAF),
+
+            // iOS6 webkit browsers don't support performance now.
+            hasNoNavigationTiming = window.performance ? false : true,
+
+            iOS6Notice = 'setTimeout is being used as a substitiue for' +
+            'requestAnimationFrame due to a bug within iOS 6 builds',
+
+            hasIOS6Bug = requiresWebkitprefix && hasMobileDeviceWidth &&
+            hasNoNavigationTiming;
+
+        function bugCheckresults(timingFnA, timingFnB, notice) {
+            if (timingFnA || timingFnB) {
+                console.warn(notice);
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        function displayResults() {
+            if (hasIOS6Bug) {
+                return bugCheckresults(webkitRAF, rAF, iOS6Notice);
+            } else {
+                return false;
+            }
+        }
+
+        return displayResults();
+    }
+
+    /**
+     * Native clearTimeout function.
+     * @return {Function}
+     */
+    function clearTimeoutWithId(id) {
+        clearTimeout(id);
+    }
+
+    /**
+     * Based on a polyfill by Erik, introduced by Paul Irish & 
+     * further improved by Darius Bacon.
+     * @see  {@link http://www.paulirish.com/2011/
+     * requestanimationframe-for-smart-animating}
+     * @see  {@link https://github.com/darius/requestAnimationFrame/blob/
+     * master/requestAnimationFrame.js}
+     * @callback {Number} Timestamp.
+     * @return {Function} setTimeout Function.
+     */
+    function setTimeoutWithTimestamp(callback) {
+        var immediateTime = Date.now(),
+            lapsedTime = Math.max(previousTime + 16, immediateTime);
+        return setTimeout(function() {
+                callback(previousTime = lapsedTime);
+            },
+            lapsedTime - immediateTime);
+    }
+
+    /**
+     * Queries the native function, prefixed function 
+     * or use the setTimeoutWithTimestamp function.
+     * @return {Function}
+     */
+    function queryRequestAnimationFrame() {
+        if (Array.prototype.filter) {
+            assignedRequestAnimationFrame = window['request' + aF] ||
+                window[vendors.filter(function(vendor) {
+                    if (window[vendor + rqAF] !== undefined)
+                        return vendor;
+                }) + rqAF] || setTimeoutWithTimestamp;
+        } else {
+            return setTimeoutWithTimestamp;
+        }
+        if (!hasIOS6RequestAnimationFrameBug()) {
+            return assignedRequestAnimationFrame;
+        } else {
+            return setTimeoutWithTimestamp;
+        }
+    }
+
+    /**
+     * Queries the native function, prefixed function 
+     * or use the clearTimeoutWithId function.
+     * @return {Function}
+     */
+    function queryCancelAnimationFrame() {
+        var cancellationNames = [];
+        if (Array.prototype.map) {
+            vendors.map(function(vendor) {
+                return ['Cancel', 'CancelRequest'].map(
+                    function(cancellationNamePrefix) {
+                        cancellationNames.push(vendor +
+                            cancellationNamePrefix + aF);
+                    });
+            });
+        } else {
+            return clearTimeoutWithId;
+        }
+
+        /**
+         * Checks for the prefixed cancelAnimationFrame implementation.
+         * @param  {Array} prefixedNames - An array of the prefixed names. 
+         * @param  {Number} i - Iteration start point.
+         * @return {Function} prefixed cancelAnimationFrame function.
+         */
+        function prefixedCancelAnimationFrame(prefixedNames, i) {
+            var cancellationFunction;
+            for (; i < prefixedNames.length; i++) {
+                if (window[prefixedNames[i]]) {
+                    cancellationFunction = window[prefixedNames[i]];
+                    break;
+                }
+            }
+            return cancellationFunction;
+        }
+
+        // Use truthly function
+        assignedCancelAnimationFrame = window['cancel' + aF] ||
+            prefixedCancelAnimationFrame(cancellationNames, 0) ||
+            clearTimeoutWithId;
+
+        // Check for iOS 6 bug
+        if (!hasIOS6RequestAnimationFrameBug()) {
+            return assignedCancelAnimationFrame;
+        } else {
+            return clearTimeoutWithId;
+        }
+    }
+
+    function getRequestFn() {
+        if (hasMozMismatch) {
+            return setTimeoutWithTimestamp;
+        } else {
+            return queryRequestAnimationFrame();
+        }
+    }
+
+    function getCancelFn() {
+        return queryCancelAnimationFrame();
+    }
+
+    function setNativeFn() {
+        if (hasMozMismatch) {
+            window.requestAnimationFrame = setTimeoutWithTimestamp;
+            window.cancelAnimationFrame = clearTimeoutWithId;
+        } else {
+            window.requestAnimationFrame = queryRequestAnimationFrame();
+            window.cancelAnimationFrame = queryCancelAnimationFrame();
+        }
+    }
+
+    /**
+     * The type value "request" singles out firefox 4 - 10 and 
+     * assigns the setTimeout function if plausible.
+     */
+
+    switch (type) {
+        case 'request':
+        case '':
+            func = getRequestFn();
+            break;
+
+        case 'cancel':
+            func = getCancelFn();
+            break;
+
+        case 'native':
+            setNativeFn();
+            break;
+        default:
+            throw new Error('RequestFrame parameter is not a type.');
+    }
+    return func;
+}
+
+
+// Node.js/ CommonJS
+if (typeof module === 'object' && typeof module.exports === 'object') {
+module.exports = exports = requestFrame;
+}
+
+// AMD
+else if (typeof define === 'function' && define.amd) {
+define(function() {
+  return requestFrame;
+});
+}
+
+// Default to window as global
+else if (typeof window === 'object') {
+window.requestFrame = requestFrame;
+}
+/* global -module, -exports, -define */
+
+}((typeof window === "undefined" ? {} : window)));

+ 1 - 0
web_interface/src/wui/reset.cgi

@@ -0,0 +1 @@
+print(argv)

BIN
web_interface/src/wui/rotek.png


+ 36 - 0
web_interface/src/wui/settings.cgi

@@ -0,0 +1,36 @@
+#!C:\Python34\python.exe
+# -*- coding: utf-8 -*-
+import json
+print("Content-Type: text/html")
+print("")
+JSON = {
+  "managerIP":"0.0.0.0",
+  "read_community":"public",
+  "write_community":"public",
+  "managerIP2":"0.0.0.0",
+  "managerIP3":"0.0.0.0",
+  "batvoltage_min":42,
+  "batvoltage_min_hist":0.25, 
+  "batcap":7,
+  "batcharge_volt":2.275,
+  "tempcomp":False,
+  "tempcomp_k_buf":3,
+  "tempcomp_k_cycle":4,
+  "batsym":False,
+  "batcurrlim":False,
+  "ipaddr":"192.168.10.254",
+  "gw":"192.168.10.1",
+  "mask":"255.255.255.0",
+  "dhcp":True,
+  "loadvolt_min":43,
+  "loadvolt_min_hist":0.5,
+  "pps_min":5.0,
+  "pps_min_hist":0.5,
+  "utc":"3.0",
+  "ntp":True,
+  "ntpservip":"88.147.254.236",
+  "lastsynctime":"21.09.2015 12:32:14",
+  "netsettings_changed":"false",
+  "NeedProfilaction":"false"
+}
+print(json.dumps(JSON))

+ 162 - 0
web_interface/src/wui/settings.html

@@ -0,0 +1,162 @@
+<!DOCTYPE html>
+<html lang="">
+<head>
+<meta charset="utf-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
+<title>Настройки</title>
+<link href="main.css" rel="stylesheet">
+</head>
+<body>
+<div id="count-wrap">
+  <div id='countdown'>
+    <p>Контроллер будет перезагружен через <span id="count-number">5</span> секунд.</p>
+  </div>
+  <div id='checkUpdatePass'>
+    <form action="" method="post">
+      <p>Введите пароль для входа в режим обновления прошивки.</p>
+      <input id="pwd" type="password" class="form-control" placeholder="Пароль" class="login" size="20" maxlength="16" name="password"><br><br>
+      <input type="button"  id="close-pass"class="btn btn-danger" value="Отмена">
+      <input type="submit" class="btn btn-success" onclick="checkPWD(); return false;" value="Отправить">
+    </form>
+  </div>
+</div>
+  <div class="navbar navbar-default navbar-fixed-top">
+    <div class="navbar-header">
+      <div><a href="index.html" class="logo"></a></div>
+    </div>
+    <a href="#" id="menu-icon"></a>
+    <ul class="nav navbar-nav" id="nav">
+      <li><a href="index.html">Параметры</a></li>
+      <li><a href="settings.html" class="active">Настройки</a></li>
+      <li><a href="info.html">Информация</a></li>
+    </ul>
+  </div>
+  <div class="wrapper" id="content">
+<!--START-->
+<h1>Настройки</h1>
+<ul id="validation-box"></ul>
+<form id="form1" action="settings.cgi" method="GET">
+<div id="tabset1" class="tabset">
+<div>
+  <ul class="tabnavs">
+    <li><a href="#snmpt" class="activeTab">SNMP</a></li>
+    <li><a href="#netw">Сетевые параметры</a></li>
+  </ul>
+</div>
+<div id="temp" class="tabpane">
+  <div class="panel-heading">Контроль температуры</div>
+  <div class="panel-body">
+    <div class="cont-2">
+      <b>В шкафу</b>
+      <div class="form-group">
+        <label for="exttemp_min"> Нижняя граница </label>
+        <select class="form-control input-sm" id="exttemp_min" name="exttemp_min"></select><span> °C</span><br>
+        <label for="">Гистерезис</label><br>
+        <span id="exttemp_min_hist"></span><br>
+      </div>
+      <div class="form-group">
+        <label for="exttemp_max"> Верхняя граница </label>
+        <select class="form-control input-sm" id="exttemp_max" name="exttemp_max"></select><span> °C</span><br>
+        <label for="">Гистерезис</label><br>
+        <span id="exttemp_max_hist"></span>
+      </div>
+    </div>
+    <div class="cont-2">
+      <b>Источник бесперебойного питания</b>
+      <div class="form-group">
+        <div class="checkbox">
+          <label>
+            <input type="checkbox" name="battemp_ctrl" id="battemp_ctrl" onchange="battempState();"> Включить
+          </label>
+        </div>
+        <label for="battemp_min"> Нижняя граница </label>
+        <select class="form-control input-sm" id="battemp_min" name="battemp_min"></select><span> °C</span><br>
+        <label for="">Гистерезис</label><br>
+        <span id="battemp_min_hist"></span><br>
+      </div>
+      <div class="form-group">
+        <label for="battemp_max"> Верхняя граница </label>
+        <select class="form-control input-sm" id="battemp_max" name="battemp_max"></select><span> °C</span><br>
+        <label for="">Гистерезис</label><br>
+        <span id="battemp_max_hist"></span>
+      </div>
+    </div>
+  </div>
+</div>
+<div id="snmpt" class="tabpane activePane">
+  <div class="panel-heading">SNMP</div>
+  <div class="panel-body">
+    <label for="read_community">Read Community</label><br>
+    <input type="text" class="form-control" id="read_community" name="read_community" maxlength="15"><br>
+    <label for="write_community">Write Community</label><br>
+    <input type="text" class="form-control" id="write_community" name="write_community" maxlength="15"><br>
+    <label for="managerIP">Сервер 1</label><br>
+    <input type="text" class="form-control" id="managerIP" name="managerIP"><br>
+    <label for="managerIP2">Сервер 2</label><br>
+    <input type="text" class="form-control" id="managerIP2" name="managerIP2"><br>
+    <label for="managerIP3">Сервер 3</label><br>
+    <input type="text" class="form-control" id="managerIP3" name="managerIP3">
+  </div>
+</div>
+<div id="netw" class="tabpane">
+  <div class="panel-heading">Сетевые параметры</div>
+  <div class="panel-body">
+    <div class="checkbox">
+      <label>
+        <input type="checkbox" name="dhcp" id="dhcp" onchange="dhcpState();"> Получить IP-адрес автоматически
+      </label><br>
+    </div>
+    <label for="ipaddr">IP-адрес устройства</label><br>
+    <input type="text" class="form-control" id="ipaddr" name="ipaddr"><br>
+    <label for="gw">IP-адрес шлюза</label><br>
+    <input type="text" class="form-control" id="gw" name="gw"><br>
+    <label for="mask">Маска подсети</label><br>
+    <input type="text" class="form-control" id="mask" name="mask"><br>
+  </div>
+</div>
+
+</div>
+<p style="width:100%; ">
+  <button type="button" class="btn btn-primary" onclick="submitForms(); return false;">Применить</button>
+  <button type="button" class="btn btn-danger"  id="dev-reset">Сброс</button>
+  <button type="button" class="btn btn-warning" id="dev-reboot">Перезагрузка</button>
+  <button type="button" class="btn btn-success" id="dev-update">Обновление прошивки</button>
+</p>
+</form>
+<!--END-->
+</div>
+</body>
+<script type="text/javascript" src="main.js"></script>
+<script type="text/javascript">
+DYN_WEB.Tabs.setup({
+  id: 'tabset1', // id of tabset (required)
+  useCookies: true // optional
+});
+var ntpservipValue;
+
+settingsGET();
+$('dev-update').onclick = function(){
+  $('count-wrap').style.display = 'block';
+  $('checkUpdatePass').style.display = 'block';
+};
+$('close-pass').onclick = function(){
+  $('count-wrap').style.display = 'none';
+  $('checkUpdatePass').style.display = 'none';
+}
+$('dev-reset').onclick = function(){
+  if (getCGI('reset.cgi') == true) {
+    function foo(){window.location.href = '/settings.html';}
+    setTimeout(foo, 1000);
+  };
+};
+$('dev-reboot').onclick = function(){
+  a = 'reboot.cgi';
+  if (getCGI(a) == true) {
+    $('count-wrap').style.display = 'block';
+    $('countdown').style.display = 'block';
+    countdown(true);
+  };
+};
+</script>
+</html>