Added source code, MZ6500 module still not complete
This commit is contained in:
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/css/bootstrap.min.css.gz
|
||||
BIN
webserver/css/bootstrap.min.css.gz
Normal file
BIN
webserver/css/bootstrap.min.css.gz
Normal file
Binary file not shown.
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/css
|
||||
1
webserver/css/jquery.edittable.min.css
vendored
1
webserver/css/jquery.edittable.min.css
vendored
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/css/jquery.edittable.min.css
|
||||
18
webserver/css/jquery.edittable.min.css
vendored
Normal file
18
webserver/css/jquery.edittable.min.css
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
table.inputtable{width:100%;border:1px solid #ddd;border-collapse:collapse;border-spacing:0;-moz-box-shadow:0 1px 3px rgba(0,0,0,.075);-webkit-box-shadow:0 1px 3px rgba(0,0,0,.075);box-shadow:0 1px 3px rgba(0,0,0,.075);margin:15px 0}
|
||||
table.inputtable a.icon-button{background-color:#ccc;display:inline-block;width:18px;height:18px;text-decoration:none;color:#fff;font-weight:800;line-height:16px;text-align:center;font-size:14px;-moz-border-radius:1px;-webkit-border-radius:1px;border-radius:1px;-moz-box-shadow:0 0 1px rgba(0,0,0,0.2);-webkit-box-shadow:0 0 1px rgba(0,0,0,0.2);box-shadow:0 0 1px rgba(0,0,0,0.2)}
|
||||
table.inputtable a.icon-button.addcol,table.inputtable a.icon-button.addrow{background-color:#81b71a}
|
||||
table.inputtable a.icon-button.delcol,table.inputtable a.icon-button.delrow{background-color:#db4a39}
|
||||
table.inputtable a.icon-button.disabled{background-color:#eee}
|
||||
table.inputtable td:last-child,table.inputtable th:last-child{width:54px;border:1px solid #eee;border-right:thick;}
|
||||
table.inputtable td,table.inputtable th{border:1px solid #eee;text-align:center;height:40px;vertical-align:middle;font-size:14px}
|
||||
table.inputtable th{background-color:#f1f1f1;border-top:none;border-bottom:2px solid #ddd;border-color:#ddd}
|
||||
table.inputtable td input[type=text]{border:0;width:90%;height:100%;text-align:center;padding:0 5%}
|
||||
table.inputtable tr td input:focus{background-color:#fafafa}
|
||||
table.inputtable.wh tbody tr:nth-child(1),table.inputtable.wh tbody tr:nth-child(1) input{background-color:#fdfdfd;font-weight:800}
|
||||
table.inputtable th:first-child,table.inputtable td:first-child{border-left:none}
|
||||
table.inputtable tr:last-child td{border-bottom:none}
|
||||
@media only screen and max-width 480px {
|
||||
table.inputtable td,table.inputtable th{min-width:40px;height:80px}
|
||||
table.inputtable a.icon-button{width:40px;height:40px;font-size:18px;min-width:40px;line-height:40px;margin:3px 0}
|
||||
table.inputtable td input{height:80px}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/css/sb-admin.css
|
||||
159
webserver/css/sb-admin.css
Normal file
159
webserver/css/sb-admin.css
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
Author: Start Bootstrap - http://startbootstrap.com
|
||||
'SB Admin' HTML Template by Start Bootstrap
|
||||
|
||||
All Start Bootstrap themes are licensed under Apache 2.0.
|
||||
For more info and more free Bootstrap 3 HTML themes, visit http://startbootstrap.com!
|
||||
*/
|
||||
|
||||
/* ATTN: This is mobile first CSS - to update 786px and up screen width use the media query near the bottom of the document! */
|
||||
|
||||
/* Global Styles */
|
||||
|
||||
body {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
#page-wrapper {
|
||||
width: 100%;
|
||||
padding: 5px 15px;
|
||||
}
|
||||
|
||||
/* Nav Messages */
|
||||
|
||||
.messages-dropdown .dropdown-menu .message-preview .avatar,
|
||||
.messages-dropdown .dropdown-menu .message-preview .name,
|
||||
.messages-dropdown .dropdown-menu .message-preview .message,
|
||||
.messages-dropdown .dropdown-menu .message-preview .time {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.messages-dropdown .dropdown-menu .message-preview .avatar {
|
||||
float: left;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.messages-dropdown .dropdown-menu .message-preview .name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.messages-dropdown .dropdown-menu .message-preview .message {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.messages-dropdown .dropdown-menu .message-preview .time {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
|
||||
/* Nav Announcements */
|
||||
|
||||
.announcement-heading {
|
||||
font-size: 50px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.announcement-text {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Table Headers */
|
||||
|
||||
table.tablesorter thead {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
table.tablesorter thead tr th:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
/* Flot Chart Containers */
|
||||
|
||||
.flot-chart {
|
||||
display: block;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.flot-chart-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Edit Below to Customize Widths > 768px */
|
||||
@media (min-width:768px) {
|
||||
|
||||
/* Wrappers */
|
||||
|
||||
#wrapper {
|
||||
padding-left: 225px;
|
||||
}
|
||||
|
||||
#page-wrapper {
|
||||
padding: 15px 25px;
|
||||
}
|
||||
|
||||
/* Side Nav */
|
||||
|
||||
.side-nav {
|
||||
margin-left: -225px;
|
||||
left: 225px;
|
||||
width: 225px;
|
||||
position: fixed;
|
||||
top: 50px;
|
||||
height: 100%;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
background-color: #222222;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* Bootstrap Default Overrides - Customized Dropdowns for the Side Nav */
|
||||
|
||||
.side-nav>li.dropdown>ul.dropdown-menu {
|
||||
position: relative;
|
||||
min-width: 225px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
}
|
||||
|
||||
.side-nav>li.dropdown>ul.dropdown-menu>li>a {
|
||||
color: #999999;
|
||||
padding: 15px 15px 15px 25px;
|
||||
}
|
||||
|
||||
.side-nav>li.dropdown>ul.dropdown-menu>li>a:hover,
|
||||
.side-nav>li.dropdown>ul.dropdown-menu>li>a.active,
|
||||
.side-nav>li.dropdown>ul.dropdown-menu>li>a:focus {
|
||||
color: #fff;
|
||||
background-color: #080808;
|
||||
}
|
||||
|
||||
.side-nav>li>a {
|
||||
width: 225px;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-nav>li>a:hover,
|
||||
.navbar-inverse .navbar-nav>li>a:focus {
|
||||
background-color: #080808;
|
||||
}
|
||||
|
||||
/* Nav Messages */
|
||||
|
||||
.messages-dropdown .dropdown-menu {
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.messages-dropdown .dropdown-menu li a {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/css/sharpkey.css
|
||||
266
webserver/css/sharpkey.css
Normal file
266
webserver/css/sharpkey.css
Normal file
@@ -0,0 +1,266 @@
|
||||
.wm-button {
|
||||
width: 95px;
|
||||
height: 25px;
|
||||
background: blue;
|
||||
border: none;
|
||||
vertical-align: top;
|
||||
margin-left: 25px;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
transition: 2s;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.wm-button:disabled {
|
||||
background: #999;
|
||||
color: #555;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.wm-button:hover {
|
||||
background-color: #81ecec;
|
||||
border: 2px solid #00cec9;
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.firmware-file-upload {
|
||||
width: 95px;
|
||||
height: 25px;
|
||||
background: blue;
|
||||
border: none;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
line-height: 25px;
|
||||
margin-left: 35px;
|
||||
margin-bottom: 0px;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
transition: 2s;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.firmware-file-upload:hover {
|
||||
background-color: #81ecec;
|
||||
border: 2px solid #00cec9;
|
||||
}
|
||||
|
||||
|
||||
.keymap-file-upload {
|
||||
width: 95px;
|
||||
height: 25px;
|
||||
background: blue;
|
||||
border: none;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
line-height: 25px;
|
||||
margin-left: 35px;
|
||||
margin-bottom: 0px;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
transition: 2s;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.keymap-file-upload:hover {
|
||||
background-color: #81ecec;
|
||||
border: 2px solid #00cec9;
|
||||
}
|
||||
|
||||
input[type=file]::file-selector-button {
|
||||
border: 2px solid #6c5ce7;
|
||||
padding: .2em .4em;
|
||||
border-radius: .2em;
|
||||
background-color: #a29bfe;
|
||||
transition: 1s;
|
||||
}
|
||||
|
||||
input[type=file]::file-selector-button:hover {
|
||||
background-color: #81ecec;
|
||||
border: 2px solid #00cec9;
|
||||
}
|
||||
|
||||
.sk-modules-table {
|
||||
border: none;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.sk-modules-table caption {
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
.sk-modules-table th, .sk-modules-table td {
|
||||
border: none;
|
||||
padding: 0.2rem 2rem;
|
||||
}
|
||||
.sk-modules-table td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.sk-modules-table th {
|
||||
font-weight: normal;
|
||||
}
|
||||
.sk-modules-table td {
|
||||
border-style: none;
|
||||
vertical-align: top;
|
||||
}
|
||||
.sk-modules-table th {
|
||||
padding: 0.2em;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sk-modules-table tbody td:first-child::after {
|
||||
content: leader(". ");
|
||||
}
|
||||
|
||||
.sk-client-wifi-config-table {
|
||||
border: none;
|
||||
border-collapse: collapse;
|
||||
width: 1px !important;
|
||||
table-layout: auto !important;
|
||||
}
|
||||
.sk-client-wifi-config-table caption {
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
.sk-client-wifi-config-table th, .sk-client-wifi-config-table td {
|
||||
border: none;
|
||||
padding: 0.2rem 2rem;
|
||||
padding-left: 0;
|
||||
padding-right: 4rem;
|
||||
}
|
||||
.sk-client-wifi-config-table td {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
border-style: none;
|
||||
vertical-align: top;
|
||||
width: auto !important;
|
||||
}
|
||||
.sk-client-wifi-config-table th {
|
||||
font-weight: normal;
|
||||
padding: 0.2em;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
#client-wifi-config-area {
|
||||
overflow-x: scroll;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#firmware-revision-area {
|
||||
overflow-x: scroll;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#esp32-partitions-area {
|
||||
overflow-x: scroll;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.sk-partitions-table {
|
||||
border: none;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.sk-partitions-table caption {
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
.sk-partitions-table th, .sk-partitions-table td {
|
||||
border: none;
|
||||
padding: 0.2rem 2rem;
|
||||
}
|
||||
.sk-partitions-table td {
|
||||
white-space: nowrap;
|
||||
border-style: none;
|
||||
vertical-align: top;
|
||||
}
|
||||
.sk-partitions-table th {
|
||||
font-weight: normal;
|
||||
padding: 0.2em;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sk-partitions-table tbody td:first-child::after {
|
||||
content: leader(". ");
|
||||
}
|
||||
|
||||
.sk-partitions-table tbody td:nth-child(4) {
|
||||
text-align: right;
|
||||
}
|
||||
.sk-partitions-table tbody td:nth-child(5) {
|
||||
text-align: right;
|
||||
}
|
||||
.sk-partitions-table tbody td:nth-child(6) {
|
||||
text-align: center;
|
||||
}
|
||||
.sk-partitions-table tbody td:nth-child(8) {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table-condensed .progress {
|
||||
margin-bottom: 0 !important;
|
||||
width: 400px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.table-borderless > tbody > tr > td,
|
||||
.table-borderless > thead > tr > td,
|
||||
.table-borderless {
|
||||
border-bottom: 0;
|
||||
border-top: 0;
|
||||
padding: 4px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
border-bottom: 0;
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
padding: 4px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 0;
|
||||
white-space: nowrap;
|
||||
table-layout: auto;
|
||||
}
|
||||
|
||||
.table {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.justify {
|
||||
text-align: justify;
|
||||
text-justify: inter-word;
|
||||
}
|
||||
|
||||
.keymap {
|
||||
max-height: 36.2em;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.hr_no_margin {
|
||||
margin-top: 0.2em;
|
||||
margin-bottom: 0.2em;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
overflow: hidden;
|
||||
}
|
||||
.radio-mouse {
|
||||
padding-right: 1em;
|
||||
}
|
||||
.radio-mouse label {
|
||||
float: left;
|
||||
clear: none;
|
||||
display: block;
|
||||
padding: 0px 1em 0px 8px;
|
||||
}
|
||||
input[type=radio] .radio-mouse,
|
||||
input.radio .radio-mouse {
|
||||
float: left;
|
||||
clear: none;
|
||||
margin: 2px 0 0 2px;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/css/style.css
|
||||
253
webserver/css/style.css
Normal file
253
webserver/css/style.css
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-family: Tahoma, Arial, sans-serif;
|
||||
color: #06D85F;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
*/
|
||||
.box {
|
||||
width: 40%;
|
||||
margin: 0 auto;
|
||||
background: rgba(255,255,255,0.2);
|
||||
padding: 35px;
|
||||
border: 2px solid #fff;
|
||||
border-radius: 20px/50px;
|
||||
background-clip: padding-box;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
//transition: opacity 500ms;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
.overlay:target {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.popup {
|
||||
margin: 70px auto;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
width: 30%;
|
||||
position: relative;
|
||||
transition: all 5s ease-in-out;
|
||||
}
|
||||
|
||||
.popup h2 {
|
||||
margin-top: 0;
|
||||
color: #333;
|
||||
font-family: Tahoma, Arial, sans-serif;
|
||||
}
|
||||
.popup .close {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 30px;
|
||||
transition: all 200ms;
|
||||
font-size: 30px;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
}
|
||||
.popup .close:hover {
|
||||
color: #06D85F;
|
||||
}
|
||||
.popup .content {
|
||||
max-height: 30%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 700px){
|
||||
.box{
|
||||
width: 90%;
|
||||
}
|
||||
.popup{
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
html {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.8rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
*/
|
||||
|
||||
.topnav {
|
||||
overflow: hidden;
|
||||
background-color: #0A1128;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 5%;
|
||||
}
|
||||
|
||||
.card-grid {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-gap: 2rem;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: lightyellow;
|
||||
box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
color: #034078
|
||||
}
|
||||
/*
|
||||
input[type=submit] {
|
||||
border: none;
|
||||
color: #FEFCFB;
|
||||
background-color: #034078;
|
||||
padding: 15px 15px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 18px;
|
||||
width: 30%;
|
||||
margin-right: 10px;
|
||||
border-radius: 4px;
|
||||
transition-duration: 0.4s;
|
||||
}
|
||||
|
||||
input[type=submit]:hover {
|
||||
background-color: #1282A2;
|
||||
}
|
||||
|
||||
input[type=text], input[type=number], select {
|
||||
width: 40%;
|
||||
padding: 8px 10px;
|
||||
margin: 12px;
|
||||
display: inline-block;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input[type=url], input[type=number], select {
|
||||
width: 50%;
|
||||
padding: 12px 20px;
|
||||
margin: 18px;
|
||||
display: inline-block;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: 1rem;
|
||||
}
|
||||
*/
|
||||
.value{
|
||||
font-size: 1rem;
|
||||
color: #1282A2;
|
||||
}
|
||||
.state {
|
||||
font-size: 1rem;
|
||||
color: #1282A2;
|
||||
}
|
||||
|
||||
.button {
|
||||
font-size: 2em;
|
||||
padding: 3px;
|
||||
color: #dbd75e;
|
||||
border: 1px solid #000;
|
||||
border-radius: 5px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 1s ease-out;
|
||||
|
||||
background: #1b06d8;
|
||||
}
|
||||
.button:hover {
|
||||
background: #06D85F;
|
||||
}
|
||||
|
||||
/*
|
||||
button {
|
||||
//border: none;
|
||||
// color: #FEFCFB;
|
||||
// padding: 15px 32px;
|
||||
// text-align: center;
|
||||
// font-size: 30px;
|
||||
// width: 100px;
|
||||
// border-radius: 4px;
|
||||
transition-duration: 0.4s;
|
||||
}
|
||||
.button-on {
|
||||
// background-color: #034078;
|
||||
}
|
||||
.button-on:hover {
|
||||
// background-color: #1282A2;
|
||||
}
|
||||
.button-off {
|
||||
// background-color: #858585;
|
||||
}
|
||||
.button-off:hover {
|
||||
// background-color: #252524;
|
||||
}
|
||||
*/
|
||||
#sk-modules-table {
|
||||
border: solid thin;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
#sk-modules-table caption {
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
#sk-modules-table th,
|
||||
#sk-modules-table td {
|
||||
border: solid thin;
|
||||
padding: 0.5rem 2rem;
|
||||
}
|
||||
#sk-modules-table td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
#sk-modules-table th {
|
||||
font-weight: normal;
|
||||
}
|
||||
#sk-modules-table td {
|
||||
border-style: none solid;
|
||||
vertical-align: top;
|
||||
}
|
||||
#sk-modules-table th {
|
||||
padding: 0.2em;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#sk-modules-table tbody td:first-child::after {
|
||||
content: leader(". ");
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/css/styles.css
|
||||
1
webserver/css/styles.css
Normal file
1
webserver/css/styles.css
Normal file
@@ -0,0 +1 @@
|
||||
body { padding-bottom: 70px; }
|
||||
@@ -1 +0,0 @@
|
||||
../../sharpkey/webserver/favicon.ico
|
||||
|
Before Width: | Height: | Size: 36 B After Width: | Height: | Size: 4.2 KiB |
BIN
webserver/favicon.ico
Normal file
BIN
webserver/favicon.ico
Normal file
Binary file not shown.
|
Before Width: | Height: | Size: 36 B After Width: | Height: | Size: 4.2 KiB |
@@ -1 +0,0 @@
|
||||
../../../../sharpkey/webserver/font-awesome/css
|
||||
1
webserver/font-awesome/css/font-awesome.css
vendored
1
webserver/font-awesome/css/font-awesome.css
vendored
@@ -1 +0,0 @@
|
||||
../../../../sharpkey/webserver/font-awesome/css/font-awesome.css
|
||||
2337
webserver/font-awesome/css/font-awesome.css
vendored
Normal file
2337
webserver/font-awesome/css/font-awesome.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
../../../../sharpkey/webserver/font-awesome/css/font-awesome.min.css
|
||||
4
webserver/font-awesome/css/font-awesome.min.css
vendored
Normal file
4
webserver/font-awesome/css/font-awesome.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
../../../../sharpkey/webserver/font-awesome/css/font-awesome.min.css.orig
|
||||
4
webserver/font-awesome/css/font-awesome.min.css.orig
Normal file
4
webserver/font-awesome/css/font-awesome.min.css.orig
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/font-awesome
|
||||
@@ -1 +0,0 @@
|
||||
../../../../sharpkey/webserver/font-awesome/fonts/fontawesome-webfont.woff
|
||||
BIN
webserver/font-awesome/fonts/fontawesome-webfont.woff
Normal file
BIN
webserver/font-awesome/fonts/fontawesome-webfont.woff
Normal file
Binary file not shown.
@@ -1 +0,0 @@
|
||||
../../../../sharpkey/webserver/font-awesome/fonts
|
||||
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/images
|
||||
@@ -1 +0,0 @@
|
||||
../../sharpkey/webserver/index.html
|
||||
166
webserver/index.html
Normal file
166
webserver/index.html
Normal file
@@ -0,0 +1,166 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
||||
<title>Dashboard - SharpKey Admin</title>
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Add custom CSS here -->
|
||||
<link href="css/sb-admin.css" rel="stylesheet">
|
||||
<link href="css/sharpkey.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="font-awesome/css/font-awesome.min.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="wrapper">
|
||||
|
||||
<!-- Sidebar -->
|
||||
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
|
||||
<!-- Brand and toggle get grouped for better mobile display -->
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="index.html">SharpKey Interface</a>
|
||||
</div>
|
||||
|
||||
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||
<div class="collapse navbar-collapse navbar-ex1-collapse">
|
||||
<ul class="nav navbar-nav side-nav">
|
||||
<li class="active"><a href="index.html"><i class="fa fa-dashboard"></i> Status</a></li>
|
||||
<li id="keyMapAvailable"><a href="keymap.html"><i class="fa fa-keyboard-o"></i> %SK_CURRENTIF%Keymap</a></li>
|
||||
<li id="mouseCfgAvailable"><a href="mouse.html"><i class="fa fa-mouse-pointer"></i> Mouse Config</a></li>
|
||||
<li><a href="ota.html"><i class="fa fa-file"></i> OTA Update</a></li>
|
||||
<li><a href="wifimanager.html"><i class="fa fa-wifi"></i> WiFi Manager</a></li>
|
||||
<li><a href="reboot"><i class="fa fa-power-off"></i> Reboot</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav navbar-nav navbar-right navbar-user">
|
||||
<li class="dropdown user-dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-gear"></i> Settings <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#"><i class="fa fa-gear"></i> Settings</a></li>
|
||||
<li class="divider"></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div><!-- /.navbar-collapse -->
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h1>Status </h1>
|
||||
<ol class="breadcrumb">
|
||||
<li class="active"><i class="fa fa-dashboard"></i> Status</li>
|
||||
</ol>
|
||||
<div class="alert alert-success alert-dismissable justify">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<p>Welcome to the SharpKey embedded browser based configuration App which enables keyboard map changes, mouse configuration, <u>O</u>ver <u>T</u>he <u>A</u>ir firmware updates and more.<br>No help text is available as NVRAM is at a premium, so please read the documentation if you need help.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.row -->
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><i class="fa fa-wifi"></i> WiFi Configuration</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="table-responsive">
|
||||
<div id="client-wifi-config-area">
|
||||
<div id="wifiCfg%SK_WIFIMODEAP%">
|
||||
<table class="table table-borderless table-sm">
|
||||
<tbody>
|
||||
<tr style="display: compact;">
|
||||
<td>SSID:</td><td><span style="color: blue;">%SK_APSSID%</span></td>
|
||||
</tr>
|
||||
<tr style="display: compact;">
|
||||
<td>Password:</td><td><span style="color: blue;">%SK_APPWD%</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IP (AP):</td><td><span style="color: blue;">%SK_CURRENTIP%</span></td>
|
||||
<td>NETMASK:</td><td><span style="color: blue;">%SK_CURRENTNM%</span></td>
|
||||
<td>GATEWAY:</td><td><span style="color: blue;">%SK_CURRENTGW%</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="wifiCfg0%SK_WIFIMODECLIENT%">
|
||||
<table class="table table-borderless table-sm">
|
||||
<tbody>
|
||||
<tr style="display: compact;">
|
||||
<td>SSID:</td><td><span style="color: blue;">%SK_CLIENTSSID%</span></td>
|
||||
</tr>
|
||||
<tr style="display: compact;" id="wifiCfg1%SK_CLIENTDHCPON%">
|
||||
<td>DHCP:</td><td><span style="color: blue;">Enabled</span></td>
|
||||
</tr>
|
||||
<tr id="wifiCfg3%SK_CLIENTDHCPON%">
|
||||
<td>IP (assigned):</td><td><span style="color: blue;">%SK_CURRENTIP%</span></td>
|
||||
<td>NETMASK:</td><td><span style="color: blue;">%SK_CURRENTNM%</span></td>
|
||||
<td>GATEWAY:</td><td><span style="color: blue;">%SK_CURRENTGW%</span></td>
|
||||
</tr>
|
||||
<tr id="wifiCfg3%SK_CLIENTDHCPOFF%">
|
||||
<td>IP (fixed):</td><td><span style="color: blue;">%SK_CURRENTIP%</span></td>
|
||||
<td>NETMASK:</td><td><span style="color: blue;">%SK_CURRENTNM%</span></td>
|
||||
<td>GATEWAY:</td><td><span style="color: blue;">%SK_CURRENTGW%</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.row -->
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><i class="fa fa-file"></i> Version Information</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="table-responsive">
|
||||
<div id="firmware-revision-area">
|
||||
%SK_PRODNAME% v%SK_PRODVERSION% © P.D. Smart, 2018-22<br><br>
|
||||
<b>Modules</b><br>
|
||||
%SK_MODULES%
|
||||
<b>File Pack</b><br>
|
||||
%SK_FILEPACK%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.row -->
|
||||
|
||||
|
||||
</div><!-- /#page-wrapper -->
|
||||
|
||||
</div><!-- /#wrapper -->
|
||||
|
||||
<!-- JavaScript -->
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<script src="js/bootstrap.min.js"></script>
|
||||
<script>
|
||||
// Store the name of the active and secondary interfaces.
|
||||
const activeInterface = "%SK_CURRENTIF%";
|
||||
const secondaryInterface = "%SK_SECONDIF%"
|
||||
</script>
|
||||
<script src="js/index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
1
webserver/js/140medley.min.js
vendored
1
webserver/js/140medley.min.js
vendored
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/js/140medley.min.js
|
||||
2
webserver/js/140medley.min.js
vendored
Normal file
2
webserver/js/140medley.min.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
var t=function(a,b){return function(c,d){return a.replace(/#{([^}]*)}/g,function(a,f){return Function("x","with(x)return "+f).call(c,d||b||{})})}},s=function(a,b){return b?{get:function(c){return a[c]&&b.parse(a[c])},set:function(c,d){a[c]=b.stringify(d)}}:{}}(this.localStorage||{},JSON),p=function(a,b,c,d){c=c||document;d=c[b="on"+b];a=c[b]=function(e){d=d&&d(e=e||c.event);return(a=a&&b(e))?b:d};c=this},m=function(a,b,c){b=document;c=b.createElement("p");c.innerHTML=a;for(a=b.createDocumentFragment();b=
|
||||
c.firstChild;)a.appendChild(b);return a},$=function(a,b){a=a.match(/^(\W)?(.*)/);return(b||document)["getElement"+(a[1]?a[1]=="#"?"ById":"sByClassName":"sByTagName")](a[2])},j=function(a){for(a=0;a<4;a++)try{return a?new ActiveXObject([,"Msxml2","Msxml3","Microsoft"][a]+".XMLHTTP"):new XMLHttpRequest}catch(b){}};
|
||||
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/js/bootstrap.min.js.gz
|
||||
BIN
webserver/js/bootstrap.min.js.gz
Normal file
BIN
webserver/js/bootstrap.min.js.gz
Normal file
Binary file not shown.
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/js/index.js
|
||||
66
webserver/js/index.js
Normal file
66
webserver/js/index.js
Normal file
@@ -0,0 +1,66 @@
|
||||
function showIPConfig()
|
||||
{
|
||||
if(document.getElementById("wifiCfg0checked"))
|
||||
{
|
||||
document.getElementById("wifiCfg0checked").style.display = 'compact';
|
||||
document.getElementById("wifiCfg").style.display = 'none';
|
||||
|
||||
if(document.getElementById("wifiCfg3checked"))
|
||||
{
|
||||
document.getElementById("wifiCfg3checked").style.display = 'compact';
|
||||
document.getElementById("wifiCfg3").style.display = 'none';
|
||||
} else
|
||||
{
|
||||
document.getElementById("wifiCfg3checked").style.display = 'none';
|
||||
document.getElementById("wifiCfg3").style.display = 'compact';
|
||||
}
|
||||
|
||||
if(document.getElementById("wifiCfg1checked"))
|
||||
{
|
||||
document.getElementById("wifiCfg1checked").style.display = 'compact';
|
||||
} else
|
||||
{
|
||||
document.getElementById("wifiCfg1").style.display = 'none';
|
||||
}
|
||||
} else
|
||||
{
|
||||
document.getElementById("wifiCfg0").style.display = 'none';
|
||||
document.getElementById("wifiCfgchecked").style.display = 'compact';
|
||||
}
|
||||
}
|
||||
|
||||
// Method to enable the correct side-bar menu for the underlying host interface.
|
||||
function enableIfConfig()
|
||||
{
|
||||
// Disable keymap if no host is connected to the SharpKey. KeyInterface is the base class which exists when
|
||||
// no host was detected to invoke a host specific sub-class.
|
||||
if(activeInterface === "KeyInterface ")
|
||||
{
|
||||
document.getElementById("keyMapAvailable").style.display = 'none';
|
||||
}
|
||||
// Mouse interface active?
|
||||
else if(activeInterface === "Mouse ")
|
||||
{
|
||||
document.getElementById("keyMapAvailable").style.display = 'none';
|
||||
document.getElementById("mouseCfgAvailable").style.display = 'compact';
|
||||
} else
|
||||
{
|
||||
document.getElementById("keyMapAvailable").style.display = 'compact';
|
||||
|
||||
// Secondary interface available?
|
||||
if(secondaryInterface == "Mouse ")
|
||||
{
|
||||
document.getElementById("mouseCfgAvailable").style.display = 'compact';
|
||||
} else
|
||||
{
|
||||
document.getElementById("mouseCfgAvailable").style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// On document load, setup the items viewable on the page according to set values.
|
||||
document.addEventListener("DOMContentLoaded", function setPageDefaults()
|
||||
{
|
||||
showIPConfig();
|
||||
enableIfConfig();
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/js/jquery.edittable.js
|
||||
340
webserver/js/jquery.edittable.js
Normal file
340
webserver/js/jquery.edittable.js
Normal file
@@ -0,0 +1,340 @@
|
||||
/*! editTable v0.2.0 by Alessandro Benoit */
|
||||
(function ($, window, i) {
|
||||
|
||||
'use strict';
|
||||
|
||||
$.fn.editTable = function (options) {
|
||||
|
||||
// Settings
|
||||
var s = $.extend({
|
||||
data: [['']],
|
||||
tableClass: 'inputtable',
|
||||
jsonData: false,
|
||||
headerCols: false,
|
||||
maxRows: 999,
|
||||
first_row: true,
|
||||
row_template: false,
|
||||
field_templates: false,
|
||||
validate_field: function (col_id, value, col_type, $element) {
|
||||
return true;
|
||||
}
|
||||
}, options),
|
||||
$el = $(this),
|
||||
defaultTableContent = '<thead><tr></tr></thead><tbody></tbody>',
|
||||
$table = $('<table/>', {
|
||||
class: s.tableClass + ((s.first_row) ? ' wh' : ''),
|
||||
html: defaultTableContent
|
||||
}),
|
||||
addRowCallBack = null,
|
||||
defaultth = '<th><a class="addcol fa fa-plus" style="color: green" href="#"></a> <a class="delcol fa fa-minus" style="color: red" href="#"></a></th>',
|
||||
colnumber,
|
||||
rownumber,
|
||||
reset,
|
||||
is_validated = true;
|
||||
|
||||
// Increment for IDs
|
||||
i = i + 1;
|
||||
|
||||
// Build cell
|
||||
function buildCell(content, type) {
|
||||
content = (content === 0) ? "0" : (content || '');
|
||||
// Custom type
|
||||
if (type && 'text' !== type){
|
||||
var field = s.field_templates[type];
|
||||
return '<td>' + field.setValue(field.html, content)[0].outerHTML + '</td>';
|
||||
}
|
||||
// Default
|
||||
return '<td><input type="text" value="' + content.toString().replace(/"/g, """) + '" /></td>';
|
||||
}
|
||||
|
||||
// Build row
|
||||
function buildRow(data, len) {
|
||||
|
||||
var rowcontent = '', b;
|
||||
|
||||
data = data || '';
|
||||
|
||||
if (!s.row_template) {
|
||||
// Without row template
|
||||
for (b = 0; b < (len || data.length); b += 1) {
|
||||
rowcontent += buildCell(data[b]);
|
||||
}
|
||||
} else {
|
||||
// With row template
|
||||
for (b = 0; b < s.row_template.length; b += 1) {
|
||||
// For each field in the row
|
||||
rowcontent += buildCell(data[b], s.row_template[b]);
|
||||
}
|
||||
}
|
||||
|
||||
return $('<tr/>', {
|
||||
html: rowcontent + '<td> <a class="addrow fa fa-plus" style="color: green" href="#"></a> / <a class="delrow fa fa-minus" style="color: red" href="#"></a></td>'
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Check button status (enable/disabled)
|
||||
function checkButtons() {
|
||||
if (colnumber < 2) {
|
||||
$table.find('.delcol').addClass('disabled');
|
||||
}
|
||||
if (rownumber < 2) {
|
||||
$table.find('.delrow').addClass('disabled');
|
||||
}
|
||||
if (s.maxRows && rownumber === s.maxRows) {
|
||||
$table.find('.addrow').addClass('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
// Extension method to change or set the table header. This method should be called before fillTableData.
|
||||
function setTableHeader(header) {
|
||||
s.headerCols = header;
|
||||
}
|
||||
|
||||
// Extension method to change or set the table column types. This method should be called before fillTableData.
|
||||
function setTableType(types) {
|
||||
s.row_template = types;
|
||||
}
|
||||
|
||||
// Fill table with data
|
||||
function fillTableData(data) {
|
||||
|
||||
var a, crow = Math.min(s.maxRows, data.length);
|
||||
|
||||
// Clear table
|
||||
$table.html(defaultTableContent);
|
||||
|
||||
// If headers or row_template are set
|
||||
if (s.headerCols || s.row_template) {
|
||||
|
||||
// Fixed columns
|
||||
var col = s.headerCols || s.row_template;
|
||||
|
||||
// Table headers
|
||||
for (a = 0; a < col.length; a += 1) {
|
||||
var col_title = s.headerCols[a] || '';
|
||||
$table.find('thead tr').append('<th>' + col_title + '</th>');
|
||||
}
|
||||
$table.find('thead tr').append('<th><span class="fa fa-table"></span></th>');
|
||||
|
||||
// Table content
|
||||
for (a = 0; a < crow; a += 1) {
|
||||
// For each row in data
|
||||
buildRow(data[a], col.length).appendTo($table.find('tbody'));
|
||||
}
|
||||
|
||||
} else if ( data[0] ) {
|
||||
|
||||
// Variable columns
|
||||
for (a = 0; a < data[0].length; a += 1) {
|
||||
$table.find('thead tr').append(defaultth);
|
||||
}
|
||||
|
||||
for (a = 0; a < crow; a += 1) {
|
||||
buildRow(data[a]).appendTo($table.find('tbody'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Append missing th
|
||||
//$table.find('thead tr').append('<th></th>');
|
||||
|
||||
// Count rows and columns
|
||||
colnumber = $table.find('thead th').length - 1;
|
||||
rownumber = $table.find('tbody tr').length;
|
||||
|
||||
checkButtons();
|
||||
}
|
||||
|
||||
// Export data
|
||||
function exportData() {
|
||||
var row = 0, data = [], value;
|
||||
|
||||
is_validated = true;
|
||||
|
||||
$table.find('tbody tr').each(function () {
|
||||
|
||||
row += 1;
|
||||
data[row] = [];
|
||||
|
||||
$(this).find('td:not(:last-child)').each(function (i, v) {
|
||||
if ( s.row_template && 'text' !== s.row_template[i] ){
|
||||
var field = s.field_templates[s.row_template[i]],
|
||||
el = $(this).find($(field.html).prop('tagName'));
|
||||
|
||||
value = field.getValue(el);
|
||||
if ( !s.validate_field(i, value, s.row_template[i], el) ){
|
||||
is_validated = false;
|
||||
}
|
||||
data[row].push(value);
|
||||
} else {
|
||||
value = $(this).find('input[type="text"]').val();
|
||||
if ( !s.validate_field(i, value, 'text', v) ){
|
||||
is_validated = false;
|
||||
}
|
||||
data[row].push(value);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// Remove undefined
|
||||
data.splice(0, 1);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// Fill the table with data from textarea or given properties
|
||||
if ($el.is('textarea')) {
|
||||
|
||||
try {
|
||||
reset = JSON.parse($el.val());
|
||||
} catch (e) {
|
||||
reset = s.data;
|
||||
}
|
||||
|
||||
$el.after($table);
|
||||
|
||||
// If inside a form set the textarea content on submit
|
||||
if ($table.parents('form').length > 0) {
|
||||
$table.parents('form').submit(function () {
|
||||
$el.val(JSON.stringify(exportData()));
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
reset = (JSON.parse(s.jsonData) || s.data);
|
||||
$el.append($table);
|
||||
}
|
||||
|
||||
fillTableData(reset);
|
||||
|
||||
// Add column
|
||||
$table.on('click', '.addcol', function () {
|
||||
|
||||
var colid = parseInt($(this).closest('tr').children().index($(this).parent('th')), 10);
|
||||
|
||||
colnumber += 1;
|
||||
|
||||
$table.find('thead tr').find('th:eq(' + colid + ')').after(defaultth);
|
||||
|
||||
$table.find('tbody tr').each(function () {
|
||||
$(this).find('td:eq(' + colid + ')').after(buildCell());
|
||||
});
|
||||
|
||||
$table.find('.delcol').removeClass('disabled');
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Remove column
|
||||
$table.on('click', '.delcol', function () {
|
||||
|
||||
if ($(this).hasClass('disabled')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var colid = parseInt($(this).closest('tr').children().index($(this).parent('th')), 10);
|
||||
|
||||
colnumber -= 1;
|
||||
|
||||
checkButtons();
|
||||
|
||||
$(this).parent('th').remove();
|
||||
|
||||
$table.find('tbody tr').each(function () {
|
||||
$(this).find('td:eq(' + colid + ')').remove();
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Add row
|
||||
$table.on('click', '.addrow', function () {
|
||||
|
||||
if ($(this).hasClass('disabled')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
rownumber += 1;
|
||||
|
||||
$(this).closest('tr').after(buildRow(0, colnumber));
|
||||
|
||||
$table.find('.delrow').removeClass('disabled');
|
||||
|
||||
checkButtons();
|
||||
|
||||
// Addition, raise callback after row has been added.
|
||||
if(addRowCallBack)
|
||||
{
|
||||
addRowCallBack(rownumber, $(this).closest('tr').next('tr'), $table);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Delete row
|
||||
$table.on('click', '.delrow', function () {
|
||||
|
||||
if ($(this).hasClass('disabled')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
rownumber -= 1;
|
||||
|
||||
checkButtons();
|
||||
|
||||
$(this).closest('tr').remove();
|
||||
|
||||
$table.find('.addrow').removeClass('disabled');
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Select all content on click
|
||||
$table.on('click', 'input', function () {
|
||||
$(this).select();
|
||||
});
|
||||
|
||||
// Return functions
|
||||
return {
|
||||
// Get an array of data
|
||||
getData: function () {
|
||||
return exportData();
|
||||
},
|
||||
// Get the JSON rappresentation of data
|
||||
getJsonData: function () {
|
||||
return JSON.stringify(exportData());
|
||||
},
|
||||
// Load an array of data
|
||||
loadData: function (data) {
|
||||
reset = data;
|
||||
fillTableData(data);
|
||||
},
|
||||
// Load a JSON rappresentation of data
|
||||
loadJsonData: function (data) {
|
||||
reset = JSON.parse(data);
|
||||
fillTableData(JSON.parse(data));
|
||||
},
|
||||
// Extension method to change or set the table header. This method should be called before fillTableData.
|
||||
setHeaders: function (headers) {
|
||||
setTableHeader(headers);
|
||||
},
|
||||
// Extension method to change or set the table column types. This method should be called before fillTableData.
|
||||
setTypes: function (types) {
|
||||
setTableType(types);
|
||||
},
|
||||
// Reset data to the first instance
|
||||
reset: function () {
|
||||
fillTableData(reset);
|
||||
},
|
||||
isValidated: function () {
|
||||
return is_validated;
|
||||
},
|
||||
addRowCallBack: function (func) {
|
||||
addRowCallBack = func;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
})(jQuery, this, 0);
|
||||
1
webserver/js/jquery.edittable.min.js
vendored
1
webserver/js/jquery.edittable.min.js
vendored
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/js/jquery.edittable.min.js
|
||||
2
webserver/js/jquery.edittable.min.js
vendored
Normal file
2
webserver/js/jquery.edittable.min.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/*! editTable v0.2.0 by Alessandro Benoit */
|
||||
(function(e,t,n){"use strict";e.fn.editTable=function(t){function p(e,t){e=e===0?"0":e||"";if(t&&"text"!==t){var n=r.field_templates[t];return"<td>"+n.setValue(n.html,e)[0].outerHTML+"</td>"}return'<td><input type="text" value="'+e.toString().replace(/"/g,""")+'" /></td>'}function d(t,n){var i="",s;t=t||"";if(!r.row_template){for(s=0;s<(n||t.length);s+=1){i+=p(t[s])}}else{for(s=0;s<r.row_template.length;s+=1){i+=p(t[s],r.row_template[s])}}return e("<tr/>",{html:i+'<td><a class="addrow icon-button" href="#">+</a> <a class="delrow icon-button" href="#">-</a></td>'})}function v(){if(f<2){u.find(".delcol").addClass("disabled")}if(l<2){u.find(".delrow").addClass("disabled")}if(r.maxRows&&l===r.maxRows){u.find(".addrow").addClass("disabled")}}function m(e){var t,n=Math.min(r.maxRows,e.length);u.html(o);if(r.headerCols||r.row_template){var i=r.headerCols||r.row_template;for(t=0;t<i.length;t+=1){var s=r.headerCols[t]||"";u.find("thead tr").append("<th>"+s+"</th>")}for(t=0;t<n;t+=1){d(e[t],i.length).appendTo(u.find("tbody"))}}else if(e[0]){for(t=0;t<e[0].length;t+=1){u.find("thead tr").append(a)}for(t=0;t<n;t+=1){d(e[t]).appendTo(u.find("tbody"))}}u.find("thead tr").append("<th></th>");f=u.find("thead th").length-1;l=u.find("tbody tr").length;v()}function g(){var t=0,n=[],i;h=true;u.find("tbody tr").each(function(){t+=1;n[t]=[];e(this).find("td:not(:last-child)").each(function(s,o){if(r.row_template&&"text"!==r.row_template[s]){var u=r.field_templates[r.row_template[s]],a=e(this).find(e(u.html).prop("tagName"));i=u.getValue(a);if(!r.validate_field(s,i,r.row_template[s],a)){h=false}n[t].push(i)}else{i=e(this).find('input[type="text"]').val();if(!r.validate_field(s,i,"text",o)){h=false}n[t].push(i)}})});n.splice(0,1);return n}var r=e.extend({data:[[""]],tableClass:"inputtable",jsonData:false,headerCols:false,maxRows:999,first_row:true,row_template:false,field_templates:false,validate_field:function(e,t,n,r){return true}},t),s=e(this),o="<thead><tr></tr></thead><tbody></tbody>",u=e("<table/>",{"class":r.tableClass+(r.first_row?" wh":""),html:o}),a='<th><a class="addcol icon-button" href="#">+</a> <a class="delcol icon-button" href="#">-</a></th>',f,l,c,h=true;n=n+1;if(s.is("textarea")){try{c=JSON.parse(s.val())}catch(y){c=r.data}s.after(u);if(u.parents("form").length>0){u.parents("form").submit(function(){s.val(JSON.stringify(g()))})}}else{c=JSON.parse(r.jsonData)||r.data;s.append(u)}m(c);u.on("click",".addcol",function(){var t=parseInt(e(this).closest("tr").children().index(e(this).parent("th")),10);f+=1;u.find("thead tr").find("th:eq("+t+")").after(a);u.find("tbody tr").each(function(){e(this).find("td:eq("+t+")").after(p())});u.find(".delcol").removeClass("disabled");return false});u.on("click",".delcol",function(){if(e(this).hasClass("disabled")){return false}var t=parseInt(e(this).closest("tr").children().index(e(this).parent("th")),10);f-=1;v();e(this).parent("th").remove();u.find("tbody tr").each(function(){e(this).find("td:eq("+t+")").remove()});return false});u.on("click",".addrow",function(){if(e(this).hasClass("disabled")){return false}l+=1;e(this).closest("tr").after(d(0,f));u.find(".delrow").removeClass("disabled");v();return false});u.on("click",".delrow",function(){if(e(this).hasClass("disabled")){return false}l-=1;v();e(this).closest("tr").remove();u.find(".addrow").removeClass("disabled");return false});u.on("click","input",function(){e(this).select()});return{getData:function(){return g()},getJsonData:function(){return JSON.stringify(g())},loadData:function(e){m(e)},loadJsonData:function(e){m(JSON.parse(e))},reset:function(){m(c)},isValidated:function(){return h}}}})(jQuery,this,0)
|
||||
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/js/jquery.min.js.gz
|
||||
BIN
webserver/js/jquery.min.js.gz
Normal file
BIN
webserver/js/jquery.min.js.gz
Normal file
Binary file not shown.
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/js
|
||||
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/js/keymap.js
|
||||
653
webserver/js/keymap.js
Normal file
653
webserver/js/keymap.js
Normal file
@@ -0,0 +1,653 @@
|
||||
// Method to display a message in a message field. The existing html is saved and replaced
|
||||
// with the new html. After a timeout period the original html is restored.
|
||||
//
|
||||
$origMessage = null;
|
||||
$origId = null;
|
||||
$msgTimerId = null;
|
||||
function showMessage(timeout, id, message)
|
||||
{
|
||||
// Is this a new message whilst one is active?
|
||||
if($origMessage !== null)
|
||||
{
|
||||
// Cancel timer and restore original message.
|
||||
clearTimeout($msgTimerId);
|
||||
$('#' + $origId).html($origMessage);
|
||||
}
|
||||
|
||||
// Store original message and Id so that on timer expiry it can be replaced..
|
||||
$origMessage = $('#' + id).html();
|
||||
$origId = id;
|
||||
|
||||
// Change HTML and set timer to restore it.
|
||||
$('#' + id).html(message);
|
||||
$msgTimerId = setTimeout(function(msgFieldId)
|
||||
{
|
||||
$('#' + msgFieldId).html($origMessage);
|
||||
$origMessage = null;
|
||||
}, timeout, id);
|
||||
}
|
||||
|
||||
// Ajax download function, from user:leo on stackexchange, customised for this application.
|
||||
//
|
||||
function downloadFile()
|
||||
{
|
||||
var req = new XMLHttpRequest();
|
||||
var downloadUrl = "/keymap";
|
||||
req.open("GET", downloadUrl, true);
|
||||
req.responseType = "blob";
|
||||
|
||||
req.onload = function (event)
|
||||
{
|
||||
var blob = req.response;
|
||||
var fileName = null;
|
||||
var contentType = req.getResponseHeader("content-type");
|
||||
|
||||
// IE/EDGE seems not returning some response header
|
||||
if (req.getResponseHeader("content-disposition"))
|
||||
{
|
||||
var contentDisposition = req.getResponseHeader("content-disposition");
|
||||
fileName = contentDisposition.substring(contentDisposition.indexOf("=")+1);
|
||||
} else
|
||||
{
|
||||
fileName = "unnamed." + contentType.substring(contentType.indexOf("/")+1);
|
||||
}
|
||||
|
||||
if (window.navigator.msSaveOrOpenBlob)
|
||||
{
|
||||
// Internet Explorer
|
||||
window.navigator.msSaveOrOpenBlob(new Blob([blob], {type: contentType}), fileName);
|
||||
} else
|
||||
{
|
||||
var el = document.getElementById("keymapDownloadLink");
|
||||
el.href = window.URL.createObjectURL(blob);
|
||||
el.download = fileName;
|
||||
el.click();
|
||||
}
|
||||
};
|
||||
showMessage(1000, 'keymapMsg', "<p style=\"color:orange;\">Downloading keymap file, please wait...</p>");
|
||||
req.send();
|
||||
}
|
||||
|
||||
// Listener for the Download button click. Once clicked commence download via ajax function.
|
||||
//
|
||||
document.getElementById('keymapDownload').onclick = function keymapFileDownload(e)
|
||||
{
|
||||
downloadFile();
|
||||
}
|
||||
|
||||
|
||||
// Listener for an event change on the INPUT file tag 'keymapUpload'. Once pressed and a filename available, this method
|
||||
// commences transmission via a POST method to the SharpKey server.
|
||||
document.getElementById('keymapUpload').onchange = function keymapFileUpload(e)
|
||||
{
|
||||
var fileInput = document.getElementById("keymapUpload").files;
|
||||
|
||||
// Reset the last status. Used to track state change.
|
||||
lastStatus = 0;
|
||||
|
||||
// Client side checks, no point initiating an upload if the file isnt valid!
|
||||
if (fileInput.length == 0)
|
||||
{
|
||||
alert("No file selected!");
|
||||
|
||||
// Sane size check, a keymap entry is 6-12 bytes long, so a 1000 rows should be the max size.
|
||||
} else if (fileInput[0].size > 12*1024)
|
||||
{
|
||||
alert("File size must be less than 12KB!");
|
||||
} else
|
||||
{
|
||||
var xhttp;
|
||||
if(window.XMLHttpRequest)
|
||||
{
|
||||
xhttp = new XMLHttpRequest();
|
||||
} else
|
||||
{
|
||||
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
|
||||
}
|
||||
|
||||
// Install listeners to handle state change.
|
||||
xhttp.onreadystatechange = function()
|
||||
{
|
||||
if (xhttp.readyState == 4)
|
||||
{
|
||||
lastStatus = xhttp.status;
|
||||
if (xhttp.status == 200)
|
||||
{
|
||||
showMessage(5000, 'keymapMsg', "<p style=\"color:green;\">Upload complete. Please press <b>Reboot</b> to activate new key map.</p>");
|
||||
} else if (xhttp.status == 500)
|
||||
{
|
||||
showMessage(5000, 'keymapMsg', "<p style=\"color:red;\">Error: " + xhttp.responseText + " - Press Reboot</p>");
|
||||
}
|
||||
else if (xhttp.status == 0)
|
||||
{
|
||||
showMessage(5000, 'keymapMsg', "<p style=\"color:red;\">Error: Server closed the connection abrubtly, status unknown! - Please retry or press Reboot</p>");
|
||||
} else
|
||||
{
|
||||
showMessage(5000, 'keymapMsg', "<p style=\"color:red;\">Error: " + xhttp.responseText + " - Press Reboot</p>");
|
||||
}
|
||||
}
|
||||
};
|
||||
showMessage(10000, 'keymapMsg', "<p style=\"color:orange;\">Uploading keymap file, please wait...</p>");
|
||||
xhttp.open("POST", "/keymap", true);
|
||||
xhttp.send(fileInput[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Method to enable the correct side-bar menu for the underlying host interface.
|
||||
function enableIfConfig()
|
||||
{
|
||||
// Disable keymap if no host is connected to the SharpKey. KeyInterface is the base class which exists when
|
||||
// no host was detected to invoke a host specific sub-class.
|
||||
if(activeInterface === "KeyInterface ")
|
||||
{
|
||||
document.getElementById("keyMapAvailable").style.display = 'none';
|
||||
}
|
||||
// Mouse interface active?
|
||||
else if(activeInterface === "Mouse ")
|
||||
{
|
||||
document.getElementById("keyMapAvailable").style.display = 'none';
|
||||
document.getElementById("mouseCfgAvailable").style.display = 'compact';
|
||||
} else
|
||||
{
|
||||
document.getElementById("keyMapAvailable").style.display = 'compact';
|
||||
|
||||
// Secondary interface available?
|
||||
if(secondaryInterface == "Mouse ")
|
||||
{
|
||||
document.getElementById("mouseCfgAvailable").style.display = 'compact';
|
||||
} else
|
||||
{
|
||||
document.getElementById("mouseCfgAvailable").style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method to validate a hex input field. Basically call hexConvert and it will return a
|
||||
// valid number, 0 if invalid or 255 if out of range.
|
||||
function validateHexInput(event)
|
||||
{
|
||||
event.target.value=hexConvert(event.target.value, false);
|
||||
return true;
|
||||
};
|
||||
|
||||
// Use variables to remember last popover and value as the popover mechanism, probably due to race states, isnt uniform.
|
||||
// Also use the shown/hidden events and toggle as hide doesnt trigger reliably.
|
||||
var $currentPopover = null;
|
||||
var $currentPopoverVal = -1;
|
||||
var $currentClass = null;
|
||||
var $currentSelectCount = 0;
|
||||
var $currentTimerId = null;
|
||||
var $currentDataSetModified = false;
|
||||
|
||||
function updateButtons()
|
||||
{
|
||||
if($currentSelectCount == 2)
|
||||
{
|
||||
$('#keymapSwap').removeAttr('disabled');
|
||||
} else
|
||||
{
|
||||
$('#keymapSwap').attr('disabled', 'disabled');
|
||||
}
|
||||
if($currentSelectCount > 0)
|
||||
{
|
||||
$('#keymapDelete').removeAttr('disabled');
|
||||
} else
|
||||
{
|
||||
$('#keymapDelete').attr('disabled', 'disabled');
|
||||
}
|
||||
if($currentDataSetModified == true)
|
||||
{
|
||||
$('#keymapSave').removeAttr('disabled');
|
||||
$('#keymapReload').removeAttr('disabled');
|
||||
} else
|
||||
{
|
||||
$('#keymapSave').attr('disabled', 'disabled');
|
||||
$('#keymapReload').attr('disabled', 'disabled');
|
||||
}
|
||||
}
|
||||
|
||||
function addPopover(field, tableRow)
|
||||
{
|
||||
|
||||
// Popover auto-hide timer alarm.
|
||||
const popoverAlarm = {
|
||||
|
||||
setup: function(timerId, timeout) {
|
||||
if (timerId != null) {
|
||||
timerId = this.cancel(timerId);
|
||||
}
|
||||
|
||||
timerId = setTimeout(function() {
|
||||
|
||||
if($currentPopover) {
|
||||
$currentPopover.popover('toggle');
|
||||
}
|
||||
}, timeout);
|
||||
|
||||
return(timerId);
|
||||
},
|
||||
|
||||
cancel: function(timerId) {
|
||||
clearTimeout(timerId);
|
||||
timerId = null;
|
||||
return(timerId);
|
||||
},
|
||||
};
|
||||
|
||||
// Setup popover menus on each custom field to aid with conversion of a feature into a hex number.
|
||||
$(field).popover({
|
||||
html: true,
|
||||
title: function () {
|
||||
|
||||
return $('#popover-' + field.className).find('.head').html();
|
||||
},
|
||||
content: function () {
|
||||
return $('#popover-' + field.className).find('.content').html();
|
||||
}
|
||||
})
|
||||
.on("show.bs.popover", function(e)
|
||||
{
|
||||
var className = this.getAttribute('class');
|
||||
var $target = $(e.target);
|
||||
|
||||
// Detect a class change, user has gone from one column to another, need to close out current
|
||||
// popover ready for initialisation of new.
|
||||
if($currentClass != null && $currentClass != className)
|
||||
{
|
||||
if($currentPopover)
|
||||
{
|
||||
if($currentTimerId != 0)
|
||||
{
|
||||
$currentTimerId = popoverAlarm.cancel($currentTimerId);
|
||||
}
|
||||
|
||||
if ($currentPopover && ($currentPopover.get(0) != $target.get(0))) {
|
||||
$currentPopover.popover('toggle');
|
||||
}
|
||||
$currentPopover = null;
|
||||
}
|
||||
}
|
||||
$currentClass = className;
|
||||
|
||||
if($currentPopover)
|
||||
{
|
||||
$currentPopoverVal = 0;
|
||||
$('#popover-' + field.className).find('input').each( function( index ) {
|
||||
|
||||
if($(this).attr("checked") === "checked")
|
||||
{
|
||||
$currentPopoverVal += parseInt(this.getAttribute('data-value'), 10);
|
||||
}
|
||||
});
|
||||
} else
|
||||
{
|
||||
$currentPopoverVal = -1;
|
||||
}
|
||||
|
||||
// Go through all the checkboxes and setup the initial value.
|
||||
$('#popover-' + field.className).find('input').each( function( index ) {
|
||||
if((e.target.value & this.getAttribute('data-value')) == this.getAttribute('data-value'))
|
||||
{
|
||||
$(this).attr('checked', true);
|
||||
} else
|
||||
{
|
||||
$(this).attr('checked', false);
|
||||
}
|
||||
});
|
||||
})
|
||||
.on("shown.bs.popover", function(e)
|
||||
{
|
||||
var $target = $(e.target);
|
||||
|
||||
if ($currentPopover && ($currentPopover.get(0) != $target.get(0))) {
|
||||
$currentPopover.popover('toggle');
|
||||
}
|
||||
$currentPopover = $target;
|
||||
|
||||
// Setup an alarm to remove a visible popover if not clicked closed.
|
||||
$currentTimerId = popoverAlarm.setup($currentTimerId, 5000);
|
||||
})
|
||||
.on("hidden.bs.popover", function(e)
|
||||
{
|
||||
var $target = $(e.target);
|
||||
|
||||
if ($currentPopover && ($currentPopover.get(0) == $target.get(0))) {
|
||||
if($currentPopoverVal == -1)
|
||||
{
|
||||
$currentPopoverVal = 0;
|
||||
$('#popover-' + field.className).find('input').each( function( index ) {
|
||||
|
||||
if($(this).attr("checked") === "checked")
|
||||
{
|
||||
$currentPopoverVal += parseInt(this.getAttribute('data-value'), 10);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if(e.target.value != hexConvert($currentPopoverVal, false))
|
||||
{
|
||||
e.target.value = hexConvert($currentPopoverVal, false);
|
||||
$currentDataSetModified = true;
|
||||
updateButtons();
|
||||
}
|
||||
$currentPopover = null;
|
||||
// Cancel the alarm on the hidden popover.
|
||||
$currentTimerId = popoverAlarm.cancel($currentTimerId);
|
||||
}
|
||||
$currentPopoverVal = -1;
|
||||
})
|
||||
|
||||
.on("hide.bs.popover", function(e)
|
||||
{
|
||||
// $currentTimerId = popoverAlarm.cancel($currentTimerId);
|
||||
})
|
||||
.on("click", function(e)
|
||||
{
|
||||
console.log('click: ' + e.target.value);
|
||||
})
|
||||
.on("change", function(e)
|
||||
{
|
||||
if(e.target.className === 'checkbox')
|
||||
{
|
||||
if($(e.target).attr('checked') == 'checked')
|
||||
{
|
||||
$currentSelectCount += 1;
|
||||
//$(e.target).attr("checked", true);
|
||||
}
|
||||
else if($(e.target).attr('checked') != 'checked')
|
||||
{
|
||||
$currentSelectCount -= 1;
|
||||
//$(e.target).attr("checked", false);
|
||||
}
|
||||
} else
|
||||
{
|
||||
if(e.target.value != hexConvert($currentPopoverVal, false))
|
||||
{
|
||||
e.target.value = hexConvert(e.target.value, false);
|
||||
$currentDataSetModified = true;
|
||||
}
|
||||
}
|
||||
updateButtons();
|
||||
})
|
||||
|
||||
.blur(function () {
|
||||
});
|
||||
// End popover definition
|
||||
|
||||
// Only attach popover handlers to classes in the first row, subsequent rows will use the same popover.
|
||||
//
|
||||
if(tableRow == 1)
|
||||
{
|
||||
var search = '#popover-' + field.className;
|
||||
$(search + ' input').each(function () {
|
||||
|
||||
$('body').on('click', '#' + this.id, function(e) {
|
||||
|
||||
$currentTimerId = popoverAlarm.setup($currentTimerId, 5000);
|
||||
|
||||
$(search + ' input').each(function() {
|
||||
if(this.id === e.target.id)
|
||||
{
|
||||
$(this).attr("checked", e.target.checked ? true : false);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Method to convert a string/numeric into a hex number and range check it to be within and 8bit unsigned range.
|
||||
function hexConvert(inVal, invert)
|
||||
{
|
||||
if(isNaN(inVal) == true && isNaN('0x' + inVal) == false)
|
||||
{
|
||||
inVal = parseInt(inVal, 16);
|
||||
}
|
||||
else if(isNaN(inVal) == false && typeof inVal == 'string' && inVal.length > 0)
|
||||
{
|
||||
if(inVal.toUpperCase().indexOf("0X") == 0)
|
||||
{
|
||||
inVal = parseInt(inVal, 16);
|
||||
} else
|
||||
{
|
||||
inVal = parseInt(inVal, 10);
|
||||
}
|
||||
}
|
||||
else if(isNaN(inVal) == true || inVal.length == 0)
|
||||
{
|
||||
inVal = 0;
|
||||
}
|
||||
if(inVal < 0)
|
||||
{
|
||||
inVal = 0;
|
||||
}
|
||||
else if(inVal > 255)
|
||||
{
|
||||
inVal = 255;
|
||||
}
|
||||
if(invert == true)
|
||||
{
|
||||
inVal = (~inVal) & 0xFF;
|
||||
}
|
||||
return('0x' + (inVal).toString(16).toUpperCase());
|
||||
}
|
||||
|
||||
// Method to swap 2 tables rows. This is necessary as the keymap table has top down priority in mapping.
|
||||
function swapKeyMapData()
|
||||
{
|
||||
// Locals.
|
||||
var firstRow = null;
|
||||
var secondRow = null;
|
||||
|
||||
// Get row numbers of the rows to be swapped.
|
||||
var rowcnt = 1;
|
||||
$('.inputtable tr .checkbox').each(function() {
|
||||
if(firstRow == null && this.checked == true)
|
||||
{
|
||||
firstRow = rowcnt;
|
||||
} else
|
||||
if(firstRow != null && secondRow == null && this.checked == true)
|
||||
{
|
||||
secondRow = rowcnt;
|
||||
}
|
||||
rowcnt++;
|
||||
});
|
||||
|
||||
// If rows were matched (should be due to count) then perform swap.
|
||||
if(firstRow != null && secondRow != null)
|
||||
{
|
||||
$('.inputtable > tbody > tr:nth-child(' + firstRow + ')').replaceWith($('.inputtable > tbody > tr:nth-child(' + secondRow + ')').after($('.inputtable > tbody > tr:nth-child(' + firstRow + ')').clone(true)));
|
||||
}
|
||||
|
||||
// Update the modified state and update button state.
|
||||
$currentDataSetModified = true;
|
||||
updateButtons();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method to delete 1 or multiple rows. An individual row can be deleted by the +/- buttons but multiple requires selection and then running through the table, deleting the selected rows.
|
||||
function deleteKeyMapData()
|
||||
{
|
||||
// Locals.
|
||||
var toDeleteRows = [];
|
||||
|
||||
// Get row numbers of the rows to be swapped.
|
||||
var rowcnt = 1;
|
||||
$('.inputtable tr .checkbox').each(function() {
|
||||
if(this.checked == true)
|
||||
{
|
||||
toDeleteRows.push(rowcnt);
|
||||
}
|
||||
rowcnt++;
|
||||
});
|
||||
|
||||
// Iterate through the array of rows to delete and actually perform the deletion.
|
||||
// We work in reverse order, ie. deleting the last row first due to the index changing if we delete first to last.
|
||||
for(var idx = toDeleteRows.length; idx > 0; idx--)
|
||||
{
|
||||
$('.inputtable > tbody > tr:nth-child(' + toDeleteRows[idx-1] + ')').remove();
|
||||
}
|
||||
|
||||
// Reset the select counter and update button state.
|
||||
$currentSelectCount = 0;
|
||||
$currentDataSetModified = true;
|
||||
updateButtons();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method to save the current keymap table data to the interface.
|
||||
function saveKeyMapData()
|
||||
{
|
||||
var data = keymapTable.getData();
|
||||
|
||||
// Reset the last status. Used to track state change.
|
||||
lastStatus = 0;
|
||||
|
||||
// Client side checks, no point initiating an upload if the file isnt valid!
|
||||
if(data.length == 0)
|
||||
{
|
||||
alert("No data to save!!");
|
||||
|
||||
} else
|
||||
{
|
||||
var xhttp;
|
||||
if(window.XMLHttpRequest)
|
||||
{
|
||||
xhttp = new XMLHttpRequest();
|
||||
} else
|
||||
{
|
||||
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
|
||||
}
|
||||
|
||||
// Install listeners to handle state change.
|
||||
xhttp.onreadystatechange = function()
|
||||
{
|
||||
if (xhttp.readyState == 4)
|
||||
{
|
||||
lastStatus = xhttp.status;
|
||||
if (xhttp.status == 200)
|
||||
{
|
||||
showMessage(5000, 'keymapEditorMsg', "<p style=\"color:green;\">Upload complete. Please press <b>Reboot</b> to activate new key map.</p>");
|
||||
|
||||
// Reset the data modified flag and update button state.
|
||||
$currentDataSetModified = false;
|
||||
updateButtons();
|
||||
} else if (xhttp.status == 500)
|
||||
{
|
||||
showMessage(5000, 'keymapEditorMsg', "<p style=\"color:red;\">Error: " + xhttp.responseText + " - Press Reboot</p>");
|
||||
}
|
||||
else if (xhttp.status == 0)
|
||||
{
|
||||
showMessage(5000, 'keymapEditorMsg', "<p style=\"color:red;\">Error: Server closed the connection abrubtly, status unknown! - Please retry Save</p>");
|
||||
} else
|
||||
{
|
||||
showMessage(5000, 'keymapEditorMsg', "<p style=\"color:red;\">Error: " + xhttp.responseText + " - Press Reboot</p>");
|
||||
}
|
||||
}
|
||||
};
|
||||
showMessage(5000, 'keymapEditorMsg', "<p style=\"color:orange;\">Uploading keymap data, please wait...</p>");
|
||||
xhttp.open("POST", "/keymap/table", true);
|
||||
xhttp.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method to reload the initial table data set.
|
||||
function reloadKeyMapData()
|
||||
{
|
||||
// Request reload of the initial data.
|
||||
keymapTable.reset();
|
||||
|
||||
// Re-install the listeners as the old dataset was deleted.
|
||||
setupTableListeners();
|
||||
|
||||
// Complete message.
|
||||
showMessage(5000, 'keymapEditorMsg', "<p style=\"color:green;\">Reload complete, data reset to initial values.</p>");
|
||||
|
||||
// Reset the select counter and update button state.
|
||||
$currentSelectCount = 0;
|
||||
$currentDataSetModified = false;
|
||||
updateButtons();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Callback function triggered after a row has been added to the edittable. Use the event to add popover listeners to the new fields.
|
||||
//
|
||||
function tableRowAdded(row, rowHandle, tableHandle)
|
||||
{
|
||||
$(rowHandle).children().children().each(function() {
|
||||
if(this.className !== "")
|
||||
{
|
||||
addPopover(this, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Method to install listeners on the table data for popover aids.
|
||||
//
|
||||
function setupTableListeners()
|
||||
{
|
||||
// Setup a popover on each input field where a modal exists.
|
||||
$('.inputtable >tbody > tr').each(function (tridx) {
|
||||
|
||||
// We seperate out the Row from the input as we want to watch every input but only setup 1 popover per input type.
|
||||
$(this).find('input').each(function (inpidx) {
|
||||
if(this.className !== "")
|
||||
{
|
||||
addPopover(this, tridx);
|
||||
}
|
||||
});
|
||||
});
|
||||
//
|
||||
// Setup a callback on new rows added to table.
|
||||
keymapTable.addRowCallBack(tableRowAdded);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
// Load up the table headers, types and data. This is done with JQuery fetch as it is easier. The SharpKey is synchronous
|
||||
// but fetch order can vary so we nest the requests to ensure all headers and types are in place before the data.
|
||||
fetch('/data/keymap/table/headers')
|
||||
.then((response) => {
|
||||
return(response.json());
|
||||
})
|
||||
.then((headers) => {
|
||||
keymapTable.setHeaders(headers);
|
||||
|
||||
fetch('/data/keymap/table/types')
|
||||
.then((response) => {
|
||||
return(response.json());
|
||||
})
|
||||
.then((types) => {
|
||||
keymapTable.setTypes(types);
|
||||
|
||||
fetch('/data/keymap/table/data')
|
||||
.then((response) => {
|
||||
return(response.json());
|
||||
})
|
||||
.then((data) => {
|
||||
keymapTable.loadData(data);
|
||||
setupTableListeners();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Disable buttons, enabled when user action requires them.
|
||||
$('#keymapSwap').attr('disabled', 'disabled');
|
||||
$('#keymapDelete').attr('disabled', 'disabled');
|
||||
$('#keymapSave').attr('disabled', 'disabled');
|
||||
$('#keymapReload').attr('disabled', 'disabled');
|
||||
|
||||
// Add listeners to the buttons.
|
||||
$('#keymapSwap').on('click', function() { swapKeyMapData(); });
|
||||
$('#keymapDelete').on('click', function() { deleteKeyMapData(); });
|
||||
$('#keymapSave').on('click', function() { saveKeyMapData(); });
|
||||
$('#keymapReload').on('click', function() { reloadKeyMapData(); });
|
||||
|
||||
// Setup the menu options according to underlying interface.
|
||||
enableIfConfig();
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/js/mouse.js
|
||||
141
webserver/js/mouse.js
Normal file
141
webserver/js/mouse.js
Normal file
@@ -0,0 +1,141 @@
|
||||
// Method to display a message in a message field. The existing html is saved and replaced
|
||||
// with the new html. After a timeout period the original html is restored.
|
||||
//
|
||||
$origMessage = null;
|
||||
$origId = null;
|
||||
$msgTimerId = null;
|
||||
function showMessage(timeout, id, message)
|
||||
{
|
||||
// Is this a new message whilst one is active?
|
||||
if($origMessage !== null)
|
||||
{
|
||||
// Cancel timer and restore original message.
|
||||
clearTimeout($msgTimerId);
|
||||
$('#' + $origId).html($origMessage);
|
||||
}
|
||||
|
||||
// Store original message and Id so that on timer expiry it can be replaced..
|
||||
$origMessage = $('#' + id).html();
|
||||
$origId = id;
|
||||
|
||||
// Change HTML and set timer to restore it.
|
||||
$('#' + id).html(message);
|
||||
$msgTimerId = setTimeout(function(msgFieldId)
|
||||
{
|
||||
$('#' + msgFieldId).html($origMessage);
|
||||
$origMessage = null;
|
||||
}, timeout, id);
|
||||
}
|
||||
|
||||
// Method to enable the correct side-bar menu for the underlying host interface.
|
||||
function enableIfConfig()
|
||||
{
|
||||
// Disable keymap if no host is connected to the SharpKey. KeyInterface is the base class which exists when
|
||||
// no host was detected to invoke a host specific sub-class.
|
||||
if(activeInterface === "KeyInterface ")
|
||||
{
|
||||
document.getElementById("keyMapAvailable").style.display = 'none';
|
||||
}
|
||||
// Mouse interface active?
|
||||
else if(activeInterface === "Mouse ")
|
||||
{
|
||||
document.getElementById("keyMapAvailable").style.display = 'none';
|
||||
document.getElementById("mouseCfgAvailable").style.display = 'compact';
|
||||
} else
|
||||
{
|
||||
document.getElementById("keyMapAvailable").style.display = 'compact';
|
||||
|
||||
// Secondary interface available?
|
||||
if(secondaryInterface == "Mouse ")
|
||||
{
|
||||
document.getElementById("mouseCfgAvailable").style.display = 'compact';
|
||||
} else
|
||||
{
|
||||
document.getElementById("mouseCfgAvailable").style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method to convert a string/numeric into a hex number and range check it to be within and 8bit unsigned range.
|
||||
function hexConvert(inVal, invert)
|
||||
{
|
||||
if(isNaN(inVal) == true && isNaN('0x' + inVal) == false)
|
||||
{
|
||||
inVal = parseInt(inVal, 16);
|
||||
}
|
||||
else if(isNaN(inVal) == false && typeof inVal == 'string' && inVal.length > 0)
|
||||
{
|
||||
if(inVal.toUpperCase().indexOf("0X") == 0)
|
||||
{
|
||||
inVal = parseInt(inVal, 16);
|
||||
} else
|
||||
{
|
||||
inVal = parseInt(inVal, 10);
|
||||
}
|
||||
}
|
||||
else if(isNaN(inVal) == true || inVal.length == 0)
|
||||
{
|
||||
inVal = 0;
|
||||
}
|
||||
if(inVal < 0)
|
||||
{
|
||||
inVal = 0;
|
||||
}
|
||||
else if(inVal > 255)
|
||||
{
|
||||
inVal = 255;
|
||||
}
|
||||
if(invert == true)
|
||||
{
|
||||
inVal = (~inVal) & 0xFF;
|
||||
}
|
||||
return('0x' + (inVal).toString(16).toUpperCase());
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
// Setup the menu options according to underlying interface.
|
||||
enableIfConfig();
|
||||
|
||||
// AJAX code to post in a controlled manner so that we can receive back an error/success message to the commit.
|
||||
$("#mouseHostCfgSave").submit( function(e)
|
||||
{
|
||||
var form = $(this);
|
||||
var actionUrl = form.attr('action');
|
||||
|
||||
// Prevent default submit action, we want to manually submit and be able to receive a response for errors/success.
|
||||
e.preventDefault();
|
||||
|
||||
$.ajax(
|
||||
{
|
||||
type: "POST",
|
||||
url: actionUrl,
|
||||
data: form.serialize(), // serializes the form's elements.
|
||||
success: function(data)
|
||||
{
|
||||
// Show the message then revert back to original text.
|
||||
showMessage(10000, "mouseHostCfgMsg", data);
|
||||
}
|
||||
});
|
||||
});
|
||||
$("#mousePS2CfgSave").submit( function(e)
|
||||
{
|
||||
var form = $(this);
|
||||
var actionUrl = form.attr('action');
|
||||
|
||||
// Prevent default submit action, we want to manually submit and be able to receive a response for errors/success.
|
||||
e.preventDefault();
|
||||
|
||||
$.ajax(
|
||||
{
|
||||
type: "POST",
|
||||
url: actionUrl,
|
||||
data: form.serialize(), // serializes the form's elements.
|
||||
success: function(data)
|
||||
{
|
||||
// Show the message then revert back to original text.
|
||||
showMessage(10000, "mousePS2CfgMsg", data);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/js/ota.js
|
||||
300
webserver/js/ota.js
Normal file
300
webserver/js/ota.js
Normal file
@@ -0,0 +1,300 @@
|
||||
var lastStatus = 0;
|
||||
|
||||
// Method to display a message in a message field. The existing html is saved and replaced
|
||||
// with the new html. After a timeout period the original html is restored.
|
||||
//
|
||||
$origMessage = null;
|
||||
$origId = null;
|
||||
$msgTimerId = null;
|
||||
function showMessage(timeout, id, message)
|
||||
{
|
||||
// Is this a new message whilst one is active?
|
||||
if($origMessage !== null)
|
||||
{
|
||||
// Cancel timer and restore original message.
|
||||
clearTimeout($msgTimerId);
|
||||
$('#' + $origId).html($origMessage);
|
||||
}
|
||||
|
||||
// Store original message and Id so that on timer expiry it can be replaced..
|
||||
$origMessage = $('#' + id).html();
|
||||
$origId = id;
|
||||
|
||||
// Change HTML and set timer to restore it.
|
||||
$('#' + id).html(message);
|
||||
$msgTimerId = setTimeout(function(msgFieldId)
|
||||
{
|
||||
$('#' + msgFieldId).html($origMessage);
|
||||
$origMessage = null;
|
||||
}, timeout, id);
|
||||
}
|
||||
|
||||
// Clear the file input array.
|
||||
function clearFileInput(ctrl)
|
||||
{
|
||||
try {
|
||||
ctrl.value = null;
|
||||
} catch(ex) { }
|
||||
if (ctrl.value) {
|
||||
ctrl.parentNode.replaceChild(ctrl.cloneNode(true), ctrl);
|
||||
}
|
||||
}
|
||||
|
||||
// Firmware handlers.
|
||||
document.getElementById('firmwareUpload').onchange = function getFirmwareFileName(e)
|
||||
{
|
||||
var default_path = document.getElementById("firmwareUpload").files[0].name;
|
||||
|
||||
// Put the name of the file into the table cell.
|
||||
document.getElementById('firmwareName').innerHTML = "<b>=></b>" + default_path;
|
||||
document.getElementById('firmwareUpgrade').value = document.getElementById('firmwareUpload').files[0].name;
|
||||
|
||||
// Disable select and enable upgrade/cancel.
|
||||
document.getElementById('firmwareUploadLabel').style.display = 'none';
|
||||
document.getElementById('firmwareUpgrade').disabled = false;
|
||||
document.getElementById('firmwareUpgrade').style.display = 'block';
|
||||
document.getElementById('firmwareCancel').disabled = false;
|
||||
document.getElementById('firmwareCancel').style.display = 'block';
|
||||
document.getElementById('firmwareMsg').innerHTML = "Press <b>Upgrade</b> to upload and flash the firmware into the SharpKey or <b>Cancel</b> to cancel and re-select file.";
|
||||
}
|
||||
document.getElementById('firmwareCancel').onclick = function cancelFirmwareUpload(e)
|
||||
{
|
||||
var default_path = document.getElementById("firmwareUpload").files[0].name;
|
||||
|
||||
// Reset the selected filename.
|
||||
document.getElementById('firmwareName').innerHTML = "";
|
||||
document.getElementById('firmwareUpgrade').value = [];
|
||||
clearFileInput(document.getElementById('firmwareUpload'));
|
||||
|
||||
// Enable select and disable upgrade/cancel.
|
||||
document.getElementById('firmwareUploadLabel').style.display = 'block';
|
||||
document.getElementById('firmwareUpgrade').disabled = true;
|
||||
document.getElementById('firmwareUpgrade').style.display = 'none';
|
||||
document.getElementById('firmwareCancel').disabled = true;
|
||||
document.getElementById('firmwareCancel').style.display = 'none';
|
||||
document.getElementById('firmwareMsg').innerHTML = "Select a firmware image file with which to upgrade the SharpKey Operating System.";
|
||||
}
|
||||
|
||||
// Firmware upgrade handler.
|
||||
function firmwareUpdateProgress(e)
|
||||
{
|
||||
if (e.lengthComputable)
|
||||
{
|
||||
var percentage = Math.round((e.loaded/e.total)*100);
|
||||
document.getElementById('firmwareProgressBar').style.width = percentage + '%';
|
||||
}
|
||||
else
|
||||
{
|
||||
document.getElementById('firmwareProgressBar').innerHTML = "Unable to compute progress information since the total size is unknown";
|
||||
}
|
||||
}
|
||||
document.getElementById('firmwareUpgrade').onclick = function firmwareUpload()
|
||||
{
|
||||
var fileInput = document.getElementById("firmwareUpload").files;
|
||||
|
||||
// Reset the last status. Used to track state change.
|
||||
lastStatus = 0;
|
||||
|
||||
// Client side checks, no point initiating a firmware update if the file isnt valid!
|
||||
if (fileInput.length == 0)
|
||||
{
|
||||
alert("No file selected!");
|
||||
} else if (fileInput[0].size > 1664*1024)
|
||||
{
|
||||
alert("File size must be less than 1.6MB!");
|
||||
} else
|
||||
{
|
||||
document.getElementById("firmwareUpload").disabled = true;
|
||||
document.getElementById("firmwareUploadLabel").style.display = 'none';
|
||||
document.getElementById("firmwareUpgrade").disabled = true;
|
||||
document.getElementById("firmwareUpgrade").style.display = 'none';
|
||||
document.getElementById("firmwareCancel").style.display = 'none';
|
||||
document.getElementById("firmwareProgress").style.display = 'block';
|
||||
|
||||
var xhttp;
|
||||
if(window.XMLHttpRequest)
|
||||
{
|
||||
xhttp = new XMLHttpRequest();
|
||||
} else
|
||||
{
|
||||
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
|
||||
}
|
||||
|
||||
// Install listeners to
|
||||
xhttp.upload.addEventListener("progress", firmwareUpdateProgress, false);
|
||||
xhttp.onreadystatechange = function()
|
||||
{
|
||||
if (xhttp.readyState == 4)
|
||||
{
|
||||
lastStatus = xhttp.status;
|
||||
if (xhttp.status == 200)
|
||||
{
|
||||
document.getElementById('firmwareMsg').innerHTML = "<p style=\"color:green;\">Transfer complete, firmware successfully flashed onto the SharpKey. Please press <b>Reboot</b> to activate.</p>";
|
||||
document.getElementById("firmwareProgress").style.display = 'none';
|
||||
document.getElementById('firmwareName').style.display = 'none';
|
||||
} else if (xhttp.status == 500)
|
||||
{
|
||||
document.getElementById('firmwareMsg').innerHTML = "<p style=\"color:red;\">Error: " + xhttp.responseText + " - Press Reboot</p>";
|
||||
}
|
||||
else if (xhttp.status == 0)
|
||||
{
|
||||
document.getElementById('firmwareMsg').innerHTML = "<p style=\"color:red;\">Error: Server closed the connection abrubtly, flash status unknown! - Press Reboot</p>";
|
||||
} else
|
||||
{
|
||||
document.getElementById('firmwareMsg').innerHTML = "<p style=\"color:red;\">Error: " + xhttp.responseText + " - Press Reboot</p>";
|
||||
}
|
||||
}
|
||||
};
|
||||
document.getElementById('firmwareMsg').innerHTML = "<p style=\"color:orange;\">Uploading and flashing the new firmware, please wait...</p>";
|
||||
xhttp.open("POST", "/ota/firmware", true);
|
||||
xhttp.send(fileInput[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Filepack handlers.
|
||||
document.getElementById('filepackUpload').onchange = function getFilepackFileName(e)
|
||||
{
|
||||
var default_path = document.getElementById("filepackUpload").files[0].name;
|
||||
|
||||
// Put the name of the file into the table cell.
|
||||
document.getElementById('filepackName').innerHTML = "<b>=></b>" + default_path;
|
||||
document.getElementById('filepackUpgrade').value = document.getElementById('filepackUpload').files[0].name;
|
||||
|
||||
// Disable select and enable upgrade/cancel.
|
||||
document.getElementById('filepackUploadLabel').style.display = 'none';
|
||||
document.getElementById('filepackUpgrade').disabled = false;
|
||||
document.getElementById('filepackUpgrade').style.display = 'block';
|
||||
document.getElementById('filepackCancel').disabled = false;
|
||||
document.getElementById('filepackCancel').style.display = 'block';
|
||||
document.getElementById('filepackWarning').style.display = 'block';
|
||||
document.getElementById('filepackMsg').innerHTML = "Press <b>Upgrade</b> to upload and flash the filepack onto the SharpKey filesystem or <b>Cancel</b> to cancel and re-select file.";
|
||||
}
|
||||
document.getElementById('filepackCancel').onclick = function cancelFilepackUpload(e)
|
||||
{
|
||||
var default_path = document.getElementById("filepackUpload").files[0].name;
|
||||
|
||||
// Reset the selected filename.
|
||||
document.getElementById('filepackName').innerHTML = "";
|
||||
clearFileInput(document.getElementById('filepackUpload'));
|
||||
|
||||
// Enable select and disable upgrade/cancel.
|
||||
document.getElementById('filepackUploadLabel').style.display = 'block';
|
||||
document.getElementById('filepackUpgrade').disabled = true;
|
||||
document.getElementById('filepackUpgrade').style.display = 'none';
|
||||
document.getElementById('filepackCancel').disabled = true;
|
||||
document.getElementById('filepackCancel').style.display = 'none';
|
||||
document.getElementById('filepackWarning').style.display = 'none';
|
||||
document.getElementById('filepackMsg').innerHTML = "Select a filepack image file with which to upgrade the SharpKey filesystem.";
|
||||
}
|
||||
|
||||
// Filepack upgrade handler.
|
||||
function filepackUpdateProgress(e)
|
||||
{
|
||||
if (e.lengthComputable)
|
||||
{
|
||||
var percentage = Math.round((e.loaded/e.total)*100);
|
||||
document.getElementById('filepackProgressBar').style.width = percentage + '%';
|
||||
}
|
||||
else
|
||||
{
|
||||
document.getElementById('filepackProgressBar').innerHTML = "Unable to compute progress information since the total size is unknown";
|
||||
}
|
||||
}
|
||||
document.getElementById('filepackUpgrade').onclick = function filepackUpload()
|
||||
{
|
||||
var fileInput = document.getElementById("filepackUpload").files;
|
||||
|
||||
// Reset the last status. Used to track state change.
|
||||
lastStatus = 0;
|
||||
|
||||
// Client side checks, no point initiating a filepack update if the file isnt valid!
|
||||
if (fileInput.length == 0)
|
||||
{
|
||||
alert("No file selected!");
|
||||
} else if (fileInput[0].size > 640*1024)
|
||||
{
|
||||
alert("File size must be less than 640K!");
|
||||
} else
|
||||
{
|
||||
document.getElementById("filepackUpload").disabled = true;
|
||||
document.getElementById("filepackUploadLabel").style.display = 'none';
|
||||
document.getElementById("filepackUpgrade").disabled = true;
|
||||
document.getElementById("filepackUpgrade").style.display = 'none';
|
||||
document.getElementById("filepackCancel").style.display = 'none';
|
||||
document.getElementById("filepackProgress").style.display = 'block';
|
||||
|
||||
var xhttp;
|
||||
if(window.XMLHttpRequest)
|
||||
{
|
||||
xhttp = new XMLHttpRequest();
|
||||
} else
|
||||
{
|
||||
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
|
||||
}
|
||||
|
||||
// Install listeners to
|
||||
xhttp.upload.addEventListener("progress", filepackUpdateProgress, false);
|
||||
xhttp.onreadystatechange = function()
|
||||
{
|
||||
if (xhttp.readyState == 4)
|
||||
{
|
||||
lastStatus = xhttp.status;
|
||||
if (xhttp.status == 200)
|
||||
{
|
||||
document.getElementById('filepackMsg').innerHTML = "<p style=\"color:green;\">Transfer complete, filepack successfully flashed onto SharpKey filesystem. Please press <b>Reboot</b> to activate.</p>";
|
||||
document.getElementById("filepackProgress").style.display = 'none';
|
||||
document.getElementById('filepackName').style.display = 'none';
|
||||
} else if (xhttp.status == 500)
|
||||
{
|
||||
document.getElementById('filepackMsg').innerHTML = "<p style=\"color:red;\">Error: " + xhttp.responseText + " - Press Reboot</p>";
|
||||
}
|
||||
else if (xhttp.status == 0)
|
||||
{
|
||||
document.getElementById('filepackMsg').innerHTML = "<p style=\"color:red;\">Error: Server closed the connection abrubtly, flash status unknown! - Press Reboot</p>";
|
||||
} else
|
||||
{
|
||||
document.getElementById('filepackMsg').innerHTML = "<p style=\"color:red;\">Error: " + xhttp.responseText + " - Press Reboot</p>";
|
||||
}
|
||||
}
|
||||
};
|
||||
document.getElementById('filepackMsg').innerHTML = "<p style=\"color:orange;\">Uploading and flashing the new filepack, please wait...</p>";
|
||||
xhttp.open("POST", "/ota/filepack", true);
|
||||
xhttp.send(fileInput[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Method to enable the correct side-bar menu for the underlying host interface.
|
||||
function enableIfConfig()
|
||||
{
|
||||
// Disable keymap if no host is connected to the SharpKey. KeyInterface is the base class which exists when
|
||||
// no host was detected to invoke a host specific sub-class.
|
||||
if(activeInterface === "KeyInterface ")
|
||||
{
|
||||
document.getElementById("keyMapAvailable").style.display = 'none';
|
||||
}
|
||||
// Mouse interface active?
|
||||
else if(activeInterface === "Mouse ")
|
||||
{
|
||||
document.getElementById("keyMapAvailable").style.display = 'none';
|
||||
document.getElementById("mouseCfgAvailable").style.display = 'compact';
|
||||
} else
|
||||
{
|
||||
document.getElementById("keyMapAvailable").style.display = 'compact';
|
||||
|
||||
// Secondary interface available?
|
||||
if(secondaryInterface == "Mouse ")
|
||||
{
|
||||
document.getElementById("mouseCfgAvailable").style.display = 'compact';
|
||||
} else
|
||||
{
|
||||
document.getElementById("mouseCfgAvailable").style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// On document load, setup the items viewable on the page according to set values.
|
||||
document.addEventListener("DOMContentLoaded", function setPageDefaults()
|
||||
{
|
||||
enableIfConfig();
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
../../../sharpkey/webserver/js/wifimanager.js
|
||||
178
webserver/js/wifimanager.js
Normal file
178
webserver/js/wifimanager.js
Normal file
@@ -0,0 +1,178 @@
|
||||
// Method to display a message in a message field. The existing html is saved and replaced
|
||||
// with the new html. After a timeout period the original html is restored.
|
||||
//
|
||||
$origMessage = null;
|
||||
$origId = null;
|
||||
$msgTimerId = null;
|
||||
function showMessage(timeout, id, message)
|
||||
{
|
||||
// Is this a new message whilst one is active?
|
||||
if($origMessage !== null)
|
||||
{
|
||||
// Cancel timer and restore original message.
|
||||
clearTimeout($msgTimerId);
|
||||
$('#' + $origId).html($origMessage);
|
||||
}
|
||||
|
||||
// Store original message and Id so that on timer expiry it can be replaced..
|
||||
$origMessage = $('#' + id).html();
|
||||
$origId = id;
|
||||
|
||||
// Change HTML and set timer to restore it.
|
||||
$('#' + id).html(message);
|
||||
$msgTimerId = setTimeout(function(msgFieldId)
|
||||
{
|
||||
$('#' + msgFieldId).html($origMessage);
|
||||
$origMessage = null;
|
||||
}, timeout, id);
|
||||
}
|
||||
|
||||
|
||||
function showWiFiAPInput()
|
||||
{
|
||||
document.getElementById('inputWiFiClient').style.display = 'none';
|
||||
document.getElementById('inputWiFiAP').style.display = 'block';
|
||||
document.getElementById("errorMsgAP").innerHTML = "";
|
||||
}
|
||||
|
||||
function showWiFiClientInput()
|
||||
{
|
||||
document.getElementById('inputWiFiClient').style.display = 'block';
|
||||
document.getElementById('inputWiFiAP').style.display = 'none';
|
||||
document.getElementById("errorMsgClient").innerHTML = "";
|
||||
}
|
||||
|
||||
function hideFixedIPInput()
|
||||
{
|
||||
document.getElementById('rowClientIP').style.display = 'none';
|
||||
document.getElementById('rowClientNETMASK').style.display = 'none';
|
||||
document.getElementById('rowClientGATEWAY').style.display = 'none';
|
||||
document.getElementById('dhcpInput').style.display = 'block';
|
||||
document.getElementById("errorMsgClient").innerHTML = "";
|
||||
}
|
||||
|
||||
function showFixedIPInput()
|
||||
{
|
||||
document.getElementById('rowClientIP').style.display = 'table-row';
|
||||
document.getElementById('rowClientNETMASK').style.display = 'table-row';
|
||||
document.getElementById('rowClientGATEWAY').style.display = 'table-row';
|
||||
document.getElementById('dhcpInput').style.display = 'none';
|
||||
document.getElementById("errorMsgClient").innerHTML = "";
|
||||
}
|
||||
|
||||
function showIPConfig()
|
||||
{
|
||||
if(document.getElementById("wifiCfg0checked"))
|
||||
{
|
||||
document.getElementById("wifiCfg0checked").style.display = 'compact';
|
||||
document.getElementById("wifiCfg").style.display = 'none';
|
||||
|
||||
if(document.getElementById("wifiCfg3checked"))
|
||||
{
|
||||
document.getElementById("wifiCfg3checked").style.display = 'compact';
|
||||
document.getElementById("wifiCfg3").style.display = 'none';
|
||||
} else
|
||||
{
|
||||
document.getElementById("wifiCfg3checked").style.display = 'none';
|
||||
document.getElementById("wifiCfg3").style.display = 'compact';
|
||||
}
|
||||
|
||||
if(document.getElementById("wifiCfg1checked"))
|
||||
{
|
||||
document.getElementById("wifiCfg1checked").style.display = 'compact';
|
||||
} else
|
||||
{
|
||||
document.getElementById("wifiCfg1").style.display = 'none';
|
||||
}
|
||||
} else
|
||||
{
|
||||
document.getElementById("wifiCfg0").style.display = 'none';
|
||||
document.getElementById("wifiCfgchecked").style.display = 'compact';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Method to enable the correct side-bar menu for the underlying host interface.
|
||||
function enableIfConfig()
|
||||
{
|
||||
// Disable keymap if no host is connected to the SharpKey. KeyInterface is the base class which exists when
|
||||
// no host was detected to invoke a host specific sub-class.
|
||||
if(activeInterface === "KeyInterface ")
|
||||
{
|
||||
document.getElementById("keyMapAvailable").style.display = 'none';
|
||||
}
|
||||
// Mouse interface active?
|
||||
else if(activeInterface === "Mouse ")
|
||||
{
|
||||
document.getElementById("keyMapAvailable").style.display = 'none';
|
||||
document.getElementById("mouseCfgAvailable").style.display = 'compact';
|
||||
} else
|
||||
{
|
||||
document.getElementById("keyMapAvailable").style.display = 'compact';
|
||||
|
||||
// Secondary interface available?
|
||||
if(secondaryInterface == "Mouse ")
|
||||
{
|
||||
document.getElementById("mouseCfgAvailable").style.display = 'compact';
|
||||
} else
|
||||
{
|
||||
document.getElementById("mouseCfgAvailable").style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// On document load, setup the items viewable on the page according to set values.
|
||||
document.addEventListener("DOMContentLoaded", function setPageDefaults()
|
||||
{
|
||||
document.getElementById('wifiModeAccessPoint').onclick = showWiFiAPInput;
|
||||
document.getElementById('wifiModeClient').onclick = showWiFiClientInput;
|
||||
document.getElementById('dhcpModeEnabled').onclick = hideFixedIPInput;
|
||||
document.getElementById('dhcpModeDisabled').onclick = showFixedIPInput;
|
||||
|
||||
// Setup AP/Client display.
|
||||
if(document.getElementById('wifiModeClient').checked)
|
||||
{
|
||||
showWiFiClientInput();
|
||||
} else
|
||||
{
|
||||
showWiFiAPInput();
|
||||
}
|
||||
if(document.getElementById('dhcpModeEnabled').checked)
|
||||
{
|
||||
hideFixedIPInput();
|
||||
} else
|
||||
{
|
||||
showFixedIPInput();
|
||||
}
|
||||
|
||||
// AJAX code to post in a controlled manner so that we can receive back an error/success message to the commit.
|
||||
$("#wifiman").submit( function(e)
|
||||
{
|
||||
var form = $(this);
|
||||
var actionUrl = form.attr('action');
|
||||
|
||||
// Prevent default submit action, we want to manually submit and be able to receive a response for errors/success.
|
||||
e.preventDefault();
|
||||
|
||||
// Clear message window before making a POST, allows for a new message if one is provided or blank if it isnt.
|
||||
document.getElementById("errorMsgClient").innerHTML = "";
|
||||
document.getElementById("errorMsgAP").innerHTML = "";
|
||||
|
||||
$.ajax(
|
||||
{
|
||||
type: "POST",
|
||||
url: actionUrl,
|
||||
data: form.serialize(), // serializes the form's elements.
|
||||
success: function(data)
|
||||
{
|
||||
// JQuery not rendering HTML correcly so revert to DOM.
|
||||
document.getElementById("errorMsgClient").innerHTML = data;
|
||||
document.getElementById("errorMsgAP").innerHTML = data;
|
||||
//form.find('#errorMsg').html(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
showIPConfig();
|
||||
enableIfConfig();
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
../../sharpkey/webserver/keymap.html
|
||||
239
webserver/keymap.html
Normal file
239
webserver/keymap.html
Normal file
@@ -0,0 +1,239 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
||||
<title>Dashboard - SharpKey Admin</title>
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Add custom CSS here -->
|
||||
<link href="css/sb-admin.css" rel="stylesheet">
|
||||
<link href="css/sharpkey.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="font-awesome/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/css/jquery.edittable.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/css/style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="wrapper">
|
||||
|
||||
<!-- Sidebar -->
|
||||
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
|
||||
<!-- Brand and toggle get grouped for better mobile display -->
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="index.html">SharpKey Interface</a>
|
||||
</div>
|
||||
|
||||
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||
<div class="collapse navbar-collapse navbar-ex1-collapse">
|
||||
<ul class="nav navbar-nav side-nav">
|
||||
<li><a href="index.html"><i class="fa fa-dashboard"></i> Status</a></li>
|
||||
<li class="active" id="keyMapAvailable"><a href="keymap.html"><i class="fa fa-keyboard-o"></i> %SK_CURRENTIF%Keymap</a></li>
|
||||
<li id="mouseCfgAvailable"><a href="mouse.html"><i class="fa fa-mouse-pointer"></i> Mouse Config</a></li>
|
||||
<li><a href="ota.html"><i class="fa fa-file"></i> OTA Update</a></li>
|
||||
<li><a href="wifimanager.html"><i class="fa fa-wifi"></i> WiFi Manager</a></li>
|
||||
<li><a href="reboot"><i class="fa fa-power-off"></i> Reboot</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav navbar-nav navbar-right navbar-user">
|
||||
<li class="dropdown user-dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-gear"></i> Settings <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#"><i class="fa fa-gear"></i> Settings</a></li>
|
||||
<li class="divider"></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div><!-- /.navbar-collapse -->
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h1>%SK_CURRENTIF%KeyMap </h1>
|
||||
<ol class="breadcrumb">
|
||||
<li class="active"><i class="fa fa-dashboard"></i> Status->%SK_CURRENTIF%KeyMap</li>
|
||||
</ol>
|
||||
<div class="alert alert-success alert-dismissable">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<p>Configure the keyboard mapping of the interface from PS/2 to %SK_CURRENTIF% via the table below. The priority is top down and a copy can be saved or uploaded to/from your disk for backup or offline editting.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.row -->
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><i class="fa fa-table"></i> KeyMap Editor</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="table-responsive" id="keymap-edit-area">
|
||||
%SK_KEYMAPPOPOVER%
|
||||
|
||||
<div class="table-responsive keymap" id="edittable-area">
|
||||
<div id="keymapTable"></div>
|
||||
</div>
|
||||
<p></p>
|
||||
<p style="white-space: pre-wrap;" id="keymapEditorMsg">Directly edit the table, click <span class="fa fa-plus" style="color: green"></span> to add a row, <span class="fa fa-minus" style="color: red"></span> to delete, select (<i class="fa fa-check"></i>) 2 rows to Swap or select multiple rows for multi-row Delete.<br>Press Save to commit changes or Reload to discard changes and reload active i/f map.</p>
|
||||
<div>
|
||||
<table class="table-condensed">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<button type="button" class="wm-button" name="keymapSwap" id="keymapSwap" value="">Swap</button>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="wm-button" name="keymapDelete" id="keymapDelete" value="">Delete</button>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="wm-button" name="keymapSave" id="keymapSave" value="">Save</button>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="wm-button" name="keymapReload" id="keymapReload" value="">Reload</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.row -->
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><i class="fa fa-table"></i> Direct KeyMap Access</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="table-responsive" id="esp32-partitions-area">
|
||||
<form action="/keymap/upload" method="POST" id="fwupgrade">
|
||||
<p style="white-space: pre-wrap;">The KeyMap for the %SK_CURRENTIF% interface can be directly manipulated via the buttons below. You can download the file for backup or to modify locally then upload to restore or implement changes.</p>
|
||||
<p id="keymapMsg">Select Upload file (to SharpKey) or Download (to your PC) via the buttons below.</p>
|
||||
<table class="table-condensed">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="keymapUpload" class="keymap-file-upload" id="keymapUploadLabel">Upload<input type="file" id="keymapUpload"/>
|
||||
</label>
|
||||
</td>
|
||||
<td>
|
||||
<a id="keymapDownloadLink" style="display: none"></a>
|
||||
<button type="button" class="wm-button" name="keymapDownload" id="keymapDownload" value="">Download</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.row -->
|
||||
</div><!-- /#page-wrapper -->
|
||||
|
||||
<!-- JavaScript -->
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<script src="js/bootstrap.min.js"></script>
|
||||
<script src="js/jquery.edittable.js"></script>
|
||||
<script>
|
||||
// Initialize keymap editting table. The table is defined in the document because the custom types need to be added in by the server dynamically. There is no method to
|
||||
// add custom fields in edittable and JSON is not recommended (and doesnt support it in the API) for serialization of functions hence this solution.
|
||||
var keymapTable = $("#keymapTable").editTable({
|
||||
field_templates: {
|
||||
'checkbox' : {
|
||||
html: '<input type="checkbox" class="checkbox"/>',
|
||||
getValue: function (input) {
|
||||
return $(input).is(':checked');
|
||||
},
|
||||
setValue: function (input, value) {
|
||||
if ( value ){
|
||||
return $(input).attr('checked', true);
|
||||
}
|
||||
return $(input).removeAttr('checked');
|
||||
}
|
||||
},
|
||||
'textarea' : {
|
||||
html: '<textarea/>',
|
||||
getValue: function (input) {
|
||||
return $(input).val();
|
||||
},
|
||||
setValue: function (input, value) {
|
||||
return $(input).text(value);
|
||||
}
|
||||
},
|
||||
'hex' : {
|
||||
html: '<input type="text" class="hexInput" value="" data-placement="right"/>',
|
||||
getValue: function (input) {
|
||||
return $(input).val();
|
||||
},
|
||||
setValue: function (input, inVal) {
|
||||
return $(input).attr("value", hexConvert(inVal, false));
|
||||
}
|
||||
},
|
||||
'list' : {
|
||||
html: '<select><option value="">None</option><option>All</option></select>',
|
||||
getValue: function (input) {
|
||||
return $(input).val();
|
||||
},
|
||||
setValue: function (input, value) {
|
||||
var select = $(input);
|
||||
select.find('option').filter(function() {
|
||||
return $(this).val() == value;
|
||||
}).attr('selected', true);
|
||||
return select;
|
||||
}
|
||||
},
|
||||
%SK_KEYMAPJSFIELDS%
|
||||
},
|
||||
row_template: false,
|
||||
headerCols: false,
|
||||
first_row: false,
|
||||
data:[ ],
|
||||
|
||||
// Checkbox validation
|
||||
validate_field: function (col_id, value, col_type, $element) {
|
||||
if ( col_type === 'checkbox' ) {
|
||||
$element.parent('td').animate({'background-color':'#fff'});
|
||||
if ( value === false ){
|
||||
$element.parent('td').animate({'background-color':'#DB4A39'});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ( col_type === 'HEX' ) {
|
||||
let regEx = "^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$";
|
||||
let isHex = regEx.match(value);
|
||||
console.log(isHex);
|
||||
console.log(value);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
tableClass: 'inputtable custom'
|
||||
});
|
||||
|
||||
// Store the name of the active and secondary interfaces.
|
||||
const activeInterface = "%SK_CURRENTIF%";
|
||||
const secondaryInterface = "%SK_SECONDIF%"
|
||||
</script>
|
||||
<script src="js/keymap.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
../../sharpkey/webserver/mouse.html
|
||||
167
webserver/mouse.html
Normal file
167
webserver/mouse.html
Normal file
@@ -0,0 +1,167 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
||||
<title>Dashboard - SharpKey Admin</title>
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Add custom CSS here -->
|
||||
<link href="css/sb-admin.css" rel="stylesheet">
|
||||
<link href="css/sharpkey.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="font-awesome/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/css/jquery.edittable.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/css/style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="wrapper">
|
||||
|
||||
<!-- Sidebar -->
|
||||
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
|
||||
<!-- Brand and toggle get grouped for better mobile display -->
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="index.html">SharpKey Interface</a>
|
||||
</div>
|
||||
|
||||
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||
<div class="collapse navbar-collapse navbar-ex1-collapse">
|
||||
<ul class="nav navbar-nav side-nav">
|
||||
<li><a href="index.html"><i class="fa fa-dashboard"></i> Status</a></li>
|
||||
<li id="keyMapAvailable"><a href="keymap.html"><i class="fa fa-keyboard-o"></i> %SK_CURRENTIF%Keymap</a></li>
|
||||
<li class="active" id="mouseCfgAvailable"><a href="mouse.html"><i class="fa fa-mouse-pointer"></i> Mouse Config</a></li>
|
||||
<li><a href="ota.html"><i class="fa fa-file"></i> OTA Update</a></li>
|
||||
<li><a href="wifimanager.html"><i class="fa fa-wifi"></i> WiFi Manager</a></li>
|
||||
<li><a href="reboot"><i class="fa fa-power-off"></i> Reboot</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav navbar-nav navbar-right navbar-user">
|
||||
<li class="dropdown user-dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-gear"></i> Settings <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#"><i class="fa fa-gear"></i> Settings</a></li>
|
||||
<li class="divider"></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div><!-- /.navbar-collapse -->
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h1>Mouse Config </h1>
|
||||
<ol class="breadcrumb">
|
||||
<li class="active"><i class="fa fa-dashboard"></i> Status->Mouse Config</li>
|
||||
</ol>
|
||||
<div class="alert alert-success alert-dismissable">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<p>Configure the Mouse settings in the panel below. Some of the mouse settings can be configured by the mouse wheel, please refer to the documentation for details.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.row -->
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><i class="fa fa-server"></i> Mouse Host Configuration</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form action="/data/mouse" method="POST" id="mouseHostCfgSave">
|
||||
<p><b>Host Scaling</b></p>
|
||||
<div>
|
||||
%SK_MOUSEHOSTSCALING%
|
||||
</div>
|
||||
<hr class="hr_no_margin">
|
||||
<div>
|
||||
<p style="white-space: pre-wrap;" id="mouseHostCfgMsg">Setup the host side mouse interface parameters. Commit changes by pressing <i>Save</i>.</p>
|
||||
</div>
|
||||
<hr class="hr_no_margin">
|
||||
<div>
|
||||
<table class="table-condensed">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<button type="submit" class="wm-button" name="mouseHostSave" id="mouseHostSave" form="mouseHostCfgSave" value="">Save</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.row -->
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><i class="fa fa-mouse-pointer"></i> Mouse Configuration</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form action="/data/mouse" method="POST" id="mousePS2CfgSave">
|
||||
<p><b>Mouse Scaling</b></p>
|
||||
<div>
|
||||
%SK_MOUSEPS2SCALING%
|
||||
</div>
|
||||
<p><b>Mouse Resolution</b></p>
|
||||
<div>
|
||||
%SK_MOUSEPS2RESOLUTION%
|
||||
<div>
|
||||
<p><b>Mouse Sampling Rate</b></p>
|
||||
<div>
|
||||
%SK_MOUSEPS2SAMPLERATE%
|
||||
</div>
|
||||
<hr class="hr_no_margin">
|
||||
<div>
|
||||
<p style="white-space: pre-wrap;" id="mousePS2CfgMsg">Setup the mouse parameters which are PS/2 settings, Bluetooth mice will use these parameters via internal mapping.<br>Commit changes by pressing <i>Save</i>.</p>
|
||||
</div>
|
||||
<hr class="hr_no_margin">
|
||||
<div>
|
||||
<table class="table-condensed">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<button type="submit" class="wm-button" name="mousePS2Save" id="mousePS2Save" form="mousePS2CfgSave" value="">Save</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.row -->
|
||||
|
||||
</div><!-- /#page-wrapper -->
|
||||
|
||||
<!-- JavaScript -->
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<script src="js/bootstrap.min.js"></script>
|
||||
<script>
|
||||
// Store the name of the active and secondary interfaces.
|
||||
const activeInterface = "%SK_CURRENTIF%";
|
||||
const secondaryInterface = "%SK_SECONDIF%"
|
||||
</script>
|
||||
<script src="js/mouse.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
../../sharpkey/webserver/ota.html
|
||||
238
webserver/ota.html
Normal file
238
webserver/ota.html
Normal file
@@ -0,0 +1,238 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
||||
<title>Dashboard - SharpKey Admin</title>
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Add custom CSS here -->
|
||||
<link href="css/sb-admin.css" rel="stylesheet">
|
||||
<link href="css/sharpkey.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="font-awesome/css/font-awesome.min.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="wrapper">
|
||||
|
||||
<!-- Sidebar -->
|
||||
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
|
||||
<!-- Brand and toggle get grouped for better mobile display -->
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="index.html">SharpKey Interface</a>
|
||||
</div>
|
||||
|
||||
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||
<div class="collapse navbar-collapse navbar-ex1-collapse">
|
||||
<ul class="nav navbar-nav side-nav">
|
||||
<li><a href="index.html"><i class="fa fa-dashboard"></i> Status</a></li>
|
||||
<li id="keyMapAvailable"><a href="keymap.html"><i class="fa fa-keyboard-o"></i> %SK_CURRENTIF%Keymap</a></li>
|
||||
<li id="mouseCfgAvailable"><a href="mouse.html"><i class="fa fa-mouse-pointer"></i> Mouse Config</a></li>
|
||||
<li class="active"><a href="ota.html"><i class="fa fa-table"></i> OTA Update</a></li>
|
||||
<li><a href="wifimanager.html"><i class="fa fa-wifi"></i> WiFi Manager</a></li>
|
||||
<li><a href="reboot"><i class="fa fa-power-off"></i> Reboot</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav navbar-nav navbar-right navbar-user">
|
||||
<li class="dropdown user-dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-gear"></i> Settings <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#"><i class="fa fa-gear"></i> Settings</a></li>
|
||||
<li class="divider"></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div><!-- /.navbar-collapse -->
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h1><b>O</b>ver <b>T</b>he <b>A</b>ir Update </h1>
|
||||
<ol class="breadcrumb">
|
||||
<li class="active"><i class="fa fa-dashboard"></i> Status->OTA Update </li>
|
||||
</ol>
|
||||
<div class="alert alert-success alert-dismissable justify">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<p>Welcome to the SharpKey OTA Update page.</p><p>This page allows you to check current firmware/filepack version information and upgrade them as necessary. Please read the documentation if you need help.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.row -->
|
||||
|
||||
<div class="row">
|
||||
</div><!-- /.row -->
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><i class="fa fa-file"></i> Version Information</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="table-responsive">
|
||||
<div id="firmware-revision-area">
|
||||
%SK_PRODNAME% v%SK_PRODVERSION% © P.D. Smart, 2018-22<br><br>
|
||||
<b>Modules</b><br>
|
||||
%SK_MODULES%
|
||||
<b>File Pack</b><br>
|
||||
%SK_FILEPACK%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.row -->
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><i class="fa fa-table"></i> ESP32 Partitions</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="table-responsive">
|
||||
<div id="esp32-partitions-area">
|
||||
<table class="table table-borderless table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><b>Partition Name</b></th>
|
||||
<th><b>Type</b></th>
|
||||
<th><b>Sub-Type</b></th>
|
||||
<th><b>Address</b></th>
|
||||
<th><b>Size</b></th>
|
||||
<th><b>Version</b></th>
|
||||
<th><b>Timestamp</b></th>
|
||||
<th><b>Active (Running)</b></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
%SK_PARTITIONS%
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.row -->
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><i class="fa fa-file"></i> Firmware Upload</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="table-responsive" id="upload-firmware-area">
|
||||
<form action="/data/wifi" method="POST" id="fwupgrade">
|
||||
<p><i>Firmware</i> is a binary file containing the latest operating system for the SharpKey interface.</p>
|
||||
<hr class="hr_no_margin">
|
||||
<p id="firmwareMsg">Select a firmware image file with which to upgrade the SharpKey Operating System.</p>
|
||||
<hr class="hr_no_margin">
|
||||
<table class="table-condensed">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="firmwareUpload" class="firmware-file-upload" id="firmwareUploadLabel">Select file<input type="file" id="firmwareUpload"/>
|
||||
</label>
|
||||
</td>
|
||||
<td>
|
||||
<div id="firmwareName"></div>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="wm-button" name="firmwareUpgrade" id="firmwareUpgrade" value="" style="display: none;" disabled>Upgrade</button>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="wm-button" name="firmwareCancel" id="firmwareCancel" style="display: none;" disabled>Cancel</button>
|
||||
</td>
|
||||
<td>
|
||||
<div class="progress progress-striped" id="firmwareProgress">
|
||||
<div class="progress-bar progress-bar-striped active" id="firmwareProgressBar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%">
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.row -->
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><i class="fa fa-file"></i> File Pack Upload</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="table-responsive" id="upload-firmware-area">
|
||||
<form action="/data/wifi" method="POST" id="fpupgrade">
|
||||
<p style="white-space: pre-wrap;">A filepack is a SharpKey filesystem stored in a binary image file which contains static and template files for the webserver interface.</p>
|
||||
<p id="filepackWarning" style="display: none; white-space: pre-wrap;"><b>WARNING!</b> There is no rollback for this update unlike the firmware upgrade, if the process is interrupted and the filesystem corrupted you will need to attach a serial cable to the SharpKey programming interface and upload the image via the command:
|
||||
<br><span style="margin-left: 1em; display: block;"><i>esptool.py --chip esp32 --baud 921600 --before default_reset --after hard_reset write_flash --flash_freq 40m --flash_size detect 0x310000 <filepack></i></span></p>
|
||||
<hr class="hr_no_margin">
|
||||
<p id="filepackMsg">Select a filepack image file with which to upgrade the SharpKey filesystem.</p>
|
||||
<hr class="hr_no_margin">
|
||||
<table class="table-condensed">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="filepackUpload" class="firmware-file-upload" id="filepackUploadLabel">Select file<input type="file" id="filepackUpload"/>
|
||||
</label>
|
||||
</td>
|
||||
<td>
|
||||
<div id="filepackName"></div>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="wm-button" name="filepackUpgrade" id="filepackUpgrade" value="" style="display: none;" disabled>Upgrade</button>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="wm-button" name="filepackCancel" id="filepackCancel" style="display: none;" disabled>Cancel</button>
|
||||
</td>
|
||||
<td>
|
||||
<div class="progress progress-striped" id="filepackProgress">
|
||||
<div class="progress-bar progress-bar-striped active" id="filepackProgressBar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%">
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.row -->
|
||||
|
||||
</div><!-- /#page-wrapper -->
|
||||
|
||||
</div><!-- /#wrapper -->
|
||||
|
||||
<!-- JavaScript -->
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<script src="js/bootstrap.min.js"></script>
|
||||
<script>
|
||||
// Store the name of the active and secondary interfaces.
|
||||
const activeInterface = "%SK_CURRENTIF%";
|
||||
const secondaryInterface = "%SK_SECONDIF%"
|
||||
</script>
|
||||
<script src="js/ota.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
../../sharpkey/webserver/ptable.bin
|
||||
1
webserver/ptable.bin
Normal file
1
webserver/ptable.bin
Normal file
@@ -0,0 +1 @@
|
||||
hello philip˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙and another test here˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙
|
||||
@@ -1 +0,0 @@
|
||||
../../sharpkey/webserver/version.txt
|
||||
1
webserver/version.txt
Normal file
1
webserver/version.txt
Normal file
@@ -0,0 +1 @@
|
||||
1.02
|
||||
@@ -1 +0,0 @@
|
||||
../../sharpkey/webserver/wifimanager.html
|
||||
312
webserver/wifimanager.html
Normal file
312
webserver/wifimanager.html
Normal file
@@ -0,0 +1,312 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
||||
<title>Dashboard - SharpKey Admin</title>
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Add custom CSS here -->
|
||||
<link href="css/sb-admin.css" rel="stylesheet">
|
||||
<link href="css/sharpkey.css" rel="stylesheet">
|
||||
<link href="font-awesome/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" href="/css/style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="wrapper">
|
||||
|
||||
<!-- Sidebar -->
|
||||
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
|
||||
<!-- Brand and toggle get grouped for better mobile display -->
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="index.html">SharpKey Interface</a>
|
||||
</div>
|
||||
|
||||
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||
<div class="collapse navbar-collapse navbar-ex1-collapse">
|
||||
<ul class="nav navbar-nav side-nav">
|
||||
<li><a href="index.html"><i class="fa fa-dashboard"></i> Status</a></li>
|
||||
<li id="keyMapAvailable"><a href="keymap.html"><i class="fa fa-keyboard-o"></i> %SK_CURRENTIF%Keymap</a></li>
|
||||
<li id="mouseCfgAvailable"><a href="mouse.html"><i class="fa fa-mouse-pointer"></i> Mouse Config</a></li>
|
||||
<li><a href="ota.html"><i class="fa fa-table"></i> OTA Update</a></li>
|
||||
<li class="active"><a href="wifimanager.html"><i class="fa fa-wifi"></i> WiFi Manager</a></li>
|
||||
<li><a href="reboot"><i class="fa fa-power-off"></i> Reboot</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav navbar-nav navbar-right navbar-user">
|
||||
<li class="dropdown user-dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-gear"></i> Settings <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#"><i class="fa fa-gear"></i> Settings</a></li>
|
||||
<li class="divider"></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div><!-- /.navbar-collapse -->
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h1>WiFi Manager </h1>
|
||||
<ol class="breadcrumb">
|
||||
<li class="active"><i class="fa fa-dashboard"></i> Status->WiFi Manager</li>
|
||||
</ol>
|
||||
<div class="alert alert-success alert-dismissable justify">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<p>Welcome to the SharpKey WiFi Manager page.</p><p>This page allows you to configure the SharpKey WiFi Access Point (you connect to the SharpKey) or
|
||||
Client (SharpKey connects to your network) mode.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.row -->
|
||||
|
||||
<div class="row">
|
||||
</div><!-- /.row -->
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><i class="fa fa-wifi"></i> WiFi Configuration</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="table-responsive">
|
||||
<div id="client-wifi-config-area">
|
||||
|
||||
<div id="wifiCfg%SK_WIFIMODEAP%">
|
||||
<table class="table table-borderless table-sm">
|
||||
<tbody>
|
||||
<tr style="display: compact;">
|
||||
<td>SSID:</td><td><span style="color: blue;">%SK_APSSID%</span></td>
|
||||
</tr>
|
||||
<tr style="display: compact;">
|
||||
<td>Password:</td><td><span style="color: blue;">%SK_APPWD%</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>IP (AP):</td><td><span style="color: blue;">%SK_CURRENTIP%</span></td>
|
||||
<td>NETMASK:</td><td><span style="color: blue;">%SK_CURRENTNM%</span></td>
|
||||
<td>GATEWAY:</td><td><span style="color: blue;">%SK_CURRENTGW%</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="wifiCfg0%SK_WIFIMODECLIENT%">
|
||||
<table class="table table-borderless table-sm">
|
||||
<tbody>
|
||||
<tr style="display: compact;">
|
||||
<td>SSID:</td><td><span style="color: blue;">%SK_CLIENTSSID%</span></td>
|
||||
</tr>
|
||||
<tr style="display: compact;" id="wifiCfg1%SK_CLIENTDHCPON%">
|
||||
<td>DHCP:</td><td><span style="color: blue;">Enabled</span></td>
|
||||
</tr>
|
||||
<tr id="wifiCfg3%SK_CLIENTDHCPON%">
|
||||
<td>IP (assigned):</td><td><span style="color: blue;">%SK_CURRENTIP%</span></td>
|
||||
<td>NETMASK:</td><td><span style="color: blue;">%SK_CURRENTNM%</span></td>
|
||||
<td>GATEWAY:</td><td><span style="color: blue;">%SK_CURRENTGW%</span></td>
|
||||
</tr>
|
||||
<tr id="wifiCfg3%SK_CLIENTDHCPOFF%">
|
||||
<td>IP (fixed):</td><td><span style="color: blue;">%SK_CURRENTIP%</span></td>
|
||||
<td>NETMASK:</td><td><span style="color: blue;">%SK_CURRENTNM%</span></td>
|
||||
<td>GATEWAY:</td><td><span style="color: blue;">%SK_CURRENTGW%</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.row -->
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><i class="fa fa-table"></i> Configure WiFi</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="table-responsive" id="wifi-configuration-area">
|
||||
<form action="/data/wifi" method="POST" id="wifiman">
|
||||
<div>
|
||||
<table class="sk-client-wifi-config-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>WiFi Mode:</b></td>
|
||||
<td>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="wifiMode" id="wifiModeAccessPoint" value="ap" %SK_WIFIMODEAP%>
|
||||
<label class="form-check-label" for="wifiModeAccessPoint">Access Point</label>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="wifiMode" id="wifiModeClient" value="client" %SK_WIFIMODECLIENT%>
|
||||
<label class="form-check-label" for="wifiModeClient">Client</label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Client Mode -->
|
||||
<div id="inputWiFiClient">
|
||||
<div>
|
||||
<table class="sk-client-wifi-config-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><label for="clientSSID">SSID:</label></td>
|
||||
<td><input type="text" id="clientSSID" name="clientSSID" value="%SK_CLIENTSSID%" autocapitalize="none"></td>
|
||||
<td>Name of Wifi router to use.<td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="clientPWD">Password:</label></td>
|
||||
<td><input type="text" id="clientPWD" name="clientPWD" value="%SK_CLIENTPWD%" autocapitalize="none"></td>
|
||||
<td>Password of WiFi router.</td>
|
||||
</tr>
|
||||
<br>
|
||||
<tr>
|
||||
<td><b>DHCP Mode:</b></td>
|
||||
<td>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="dhcpMode" id="dhcpModeEnabled" value="on" %SK_CLIENTDHCPON%>
|
||||
<label class="form-check-label" for="dhcpModeEnabled">
|
||||
Enabled
|
||||
</label>
|
||||
</div>
|
||||
</td>
|
||||
<td style="width: 1px;">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="dhcpMode" id="dhcpModeDisabled" value="off" %SK_CLIENTDHCPOFF%>
|
||||
<label class="form-check-label" for="dhcpModeDisabled">
|
||||
Disabled
|
||||
</label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="rowClientIP">
|
||||
<td><label for="clientIP">IP Address</label></td>
|
||||
<td><input id="clientIP" name="clientIP" inputmode="decimal" placeholder="_._._._" value="%SK_CLIENTIP%"></td>
|
||||
<td>Static IP address to assign to SharpKey</td>
|
||||
</tr>
|
||||
<tr id="rowClientNETMASK">
|
||||
<td><label for "clientNETMASK">Netmask</label></td>
|
||||
<td><input id="clientNETMASK" name="clientNETMASK" inputmode="decimal" placeholder="_._._._" value="%SK_CLIENTNM%"/></td>
|
||||
<td>Netmask for Static IP</td>
|
||||
</tr>
|
||||
<tr id="rowClientGATEWAY">
|
||||
<td><label for="clientGATEWAY">Gateway</label></td>
|
||||
<td><input id="clientGATEWAY" name="clientGATEWAY" inputmode="decimal" placeholder="_._._._" value="%SK_CLIENTGW%"/></td>
|
||||
<td>Gateway or router IP Address (if needed).</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="justify" id="dhcpInput">
|
||||
<p style="white-space: initial;">DHCP Mode enabled.<br>The SharpKey will use the credentials specified above (SSID/Password) to connect to the router and obtain
|
||||
an IP address, Netmask and Gateway. Use your router admin page to determine the IP address allocated and connect to the SharpKey via http://<assigned IP></p>
|
||||
</div>
|
||||
<div>
|
||||
<div id="errorMsgClient">
|
||||
%SK_ERRMSG%
|
||||
</div>
|
||||
<hr class="hr_no_margin">
|
||||
<div class="buttons" style="float: left; padding-left: 0px">
|
||||
<table class="sk-client-wifi-config-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><button type="submit" class="wm-button" name="save" id="save" form="wifiman">Save</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Access Point Mode -->
|
||||
<div id="inputWiFiAP">
|
||||
<div>
|
||||
<br>
|
||||
<table class="sk-client-wifi-config-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><label for="apSSID">SSID</label></td>
|
||||
<td><input type="text" id="apSSID" name="apSSID" value="%SK_APSSID%" autocapitalize="none"></td>
|
||||
<td>Name of this WiFi Access Point.<td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="apPWD">Password</label></td>
|
||||
<td><input type="text" id="apPWD" name="apPWD" value="%SK_APPWD%" autocapitalize="none"></td>
|
||||
<td>Password required by this AP to authenticate clients.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="apIP">IP Address</label></td>
|
||||
<td><input id="apIP" name="apIP" inputmode="decimal" placeholder="_._._._" value="%SK_APIP%"></td>
|
||||
<td>IP address assigned to this Access Point. ie. 192.168.4.1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for "apNETMASK">Netmask</label></td>
|
||||
<td><input id="apNETMASK" name="apNETMASK" inputmode="decimal" placeholder="_._._._" value="%SK_APNM%"/></td>
|
||||
<td>Netmask of this AP network.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="apGATEWAY">Gateway</label></td>
|
||||
<td><input id="apGATEWAY" name="apGATEWAY" inputmode="decimal" placeholder="_._._._" value="%SK_APGW%"/></td>
|
||||
<td>Gateway this AP will assign to a client. Normally same as IP.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div>
|
||||
<div id="errorMsgAP">
|
||||
%SK_ERRMSG%
|
||||
</div>
|
||||
<hr class="hr_no_margin">
|
||||
<div class="buttons" style="float: left; padding-left: 0px">
|
||||
<table class="sk-client-wifi-config-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><button type="submit" class="wm-button" name="save" id="save" form="wifiman">Save</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.row -->
|
||||
|
||||
</div><!-- /#page-wrapper -->
|
||||
|
||||
</div><!-- /#wrapper -->
|
||||
|
||||
<!-- JavaScript -->
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<script src="js/bootstrap.min.js"></script>
|
||||
<script>
|
||||
// Store the name of the active and secondary interfaces.
|
||||
const activeInterface = "%SK_CURRENTIF%";
|
||||
const secondaryInterface = "%SK_SECONDIF%"
|
||||
</script>
|
||||
<script src="js/wifimanager.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user