first commit
This commit is contained in:
116
html/admin.html
Executable file
116
html/admin.html
Executable file
@@ -0,0 +1,116 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Responsive Sidebar Template</title>
|
||||
<link rel="stylesheet" href="assets/css/bootstrap.min.css">
|
||||
<style>
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
#sidebar a.nav-link {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
#sidebar a.nav-link:hover {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
#sidebar a.nav-link svg {
|
||||
margin-right: 8px; /* Add spacing between icons and text */
|
||||
}
|
||||
#sidebar {
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
.offcanvas-backdrop {
|
||||
z-index: 1040;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Topbar -->
|
||||
<span id="topbar"></span>
|
||||
|
||||
<!-- Sidebar Offcanvas for Mobile -->
|
||||
<div class="offcanvas offcanvas-start text-white bg-dark" tabindex="-1" id="sidebarOffcanvas" aria-labelledby="sidebarOffcanvasLabel">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title" id="sidebarOffcanvasLabel">NebuleAir</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body" id="sidebar_mobile">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid mt-5">
|
||||
<div class="row">
|
||||
<aside class="col-md-2 col-lg-1 d-none d-md-block vh-100 position-fixed bg-dark text-white" id="sidebar">
|
||||
</aside>
|
||||
<!-- Main content -->
|
||||
<main class="col-md-10 ms-sm-auto col-lg-11 offset-md-2 offset-lg-1 px-md-4">
|
||||
<h1 class="mt-4">Admin</h1>
|
||||
|
||||
<div class="col-lg-6 col-12">
|
||||
<form>
|
||||
|
||||
<div class="form-check form-switch mb-2">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="flexSwitchCheckDefault">
|
||||
<label class="form-check-label" for="flexSwitchCheckDefault">Loop Logs</label>
|
||||
</div>
|
||||
<div class="form-check form-switch mb-2">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="flexSwitchCheckDefault">
|
||||
<label class="form-check-label" for="flexSwitchCheckDefault">Boot Logs</label>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" value="" id="flexCheckDefault">
|
||||
<label class="form-check-label" for="flexCheckDefault">
|
||||
Sonde temp/hum (BME280)
|
||||
</label>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="exampleInputEmail1" class="form-label">ssh tunnel port</label>
|
||||
<input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="exampleInputPassword1" class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="exampleInputPassword1">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JAVASCRIPT -->
|
||||
|
||||
<!-- Link Ajax locally -->
|
||||
<script src="assets/jquery/jquery-3.7.1.min.js"></script>
|
||||
<!-- Link Bootstrap JS and Popper.js locally -->
|
||||
<script src="assets/js/bootstrap.bundle.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const elementsToLoad = [
|
||||
{ id: 'topbar', file: 'topbar.html' },
|
||||
{ id: 'sidebar', file: 'sidebar.html' },
|
||||
{ id: 'sidebar_mobile', file: 'sidebar.html' }
|
||||
];
|
||||
|
||||
elementsToLoad.forEach(({ id, file }) => {
|
||||
fetch(file)
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.innerHTML = data;
|
||||
}
|
||||
})
|
||||
.catch(error => console.error(`Error loading ${file}:`, error));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
4085
html/assets/css/bootstrap-grid.css
vendored
Executable file
4085
html/assets/css/bootstrap-grid.css
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1
html/assets/css/bootstrap-grid.css.map
Executable file
1
html/assets/css/bootstrap-grid.css.map
Executable file
File diff suppressed because one or more lines are too long
6
html/assets/css/bootstrap-grid.min.css
vendored
Executable file
6
html/assets/css/bootstrap-grid.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
1
html/assets/css/bootstrap-grid.min.css.map
Executable file
1
html/assets/css/bootstrap-grid.min.css.map
Executable file
File diff suppressed because one or more lines are too long
4084
html/assets/css/bootstrap-grid.rtl.css
vendored
Executable file
4084
html/assets/css/bootstrap-grid.rtl.css
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1
html/assets/css/bootstrap-grid.rtl.css.map
Executable file
1
html/assets/css/bootstrap-grid.rtl.css.map
Executable file
File diff suppressed because one or more lines are too long
6
html/assets/css/bootstrap-grid.rtl.min.css
vendored
Executable file
6
html/assets/css/bootstrap-grid.rtl.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
1
html/assets/css/bootstrap-grid.rtl.min.css.map
Executable file
1
html/assets/css/bootstrap-grid.rtl.min.css.map
Executable file
File diff suppressed because one or more lines are too long
597
html/assets/css/bootstrap-reboot.css
vendored
Executable file
597
html/assets/css/bootstrap-reboot.css
vendored
Executable file
@@ -0,0 +1,597 @@
|
||||
/*!
|
||||
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2024 The Bootstrap Authors
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
:root,
|
||||
[data-bs-theme=light] {
|
||||
--bs-blue: #0d6efd;
|
||||
--bs-indigo: #6610f2;
|
||||
--bs-purple: #6f42c1;
|
||||
--bs-pink: #d63384;
|
||||
--bs-red: #dc3545;
|
||||
--bs-orange: #fd7e14;
|
||||
--bs-yellow: #ffc107;
|
||||
--bs-green: #198754;
|
||||
--bs-teal: #20c997;
|
||||
--bs-cyan: #0dcaf0;
|
||||
--bs-black: #000;
|
||||
--bs-white: #fff;
|
||||
--bs-gray: #6c757d;
|
||||
--bs-gray-dark: #343a40;
|
||||
--bs-gray-100: #f8f9fa;
|
||||
--bs-gray-200: #e9ecef;
|
||||
--bs-gray-300: #dee2e6;
|
||||
--bs-gray-400: #ced4da;
|
||||
--bs-gray-500: #adb5bd;
|
||||
--bs-gray-600: #6c757d;
|
||||
--bs-gray-700: #495057;
|
||||
--bs-gray-800: #343a40;
|
||||
--bs-gray-900: #212529;
|
||||
--bs-primary: #0d6efd;
|
||||
--bs-secondary: #6c757d;
|
||||
--bs-success: #198754;
|
||||
--bs-info: #0dcaf0;
|
||||
--bs-warning: #ffc107;
|
||||
--bs-danger: #dc3545;
|
||||
--bs-light: #f8f9fa;
|
||||
--bs-dark: #212529;
|
||||
--bs-primary-rgb: 13, 110, 253;
|
||||
--bs-secondary-rgb: 108, 117, 125;
|
||||
--bs-success-rgb: 25, 135, 84;
|
||||
--bs-info-rgb: 13, 202, 240;
|
||||
--bs-warning-rgb: 255, 193, 7;
|
||||
--bs-danger-rgb: 220, 53, 69;
|
||||
--bs-light-rgb: 248, 249, 250;
|
||||
--bs-dark-rgb: 33, 37, 41;
|
||||
--bs-primary-text-emphasis: #052c65;
|
||||
--bs-secondary-text-emphasis: #2b2f32;
|
||||
--bs-success-text-emphasis: #0a3622;
|
||||
--bs-info-text-emphasis: #055160;
|
||||
--bs-warning-text-emphasis: #664d03;
|
||||
--bs-danger-text-emphasis: #58151c;
|
||||
--bs-light-text-emphasis: #495057;
|
||||
--bs-dark-text-emphasis: #495057;
|
||||
--bs-primary-bg-subtle: #cfe2ff;
|
||||
--bs-secondary-bg-subtle: #e2e3e5;
|
||||
--bs-success-bg-subtle: #d1e7dd;
|
||||
--bs-info-bg-subtle: #cff4fc;
|
||||
--bs-warning-bg-subtle: #fff3cd;
|
||||
--bs-danger-bg-subtle: #f8d7da;
|
||||
--bs-light-bg-subtle: #fcfcfd;
|
||||
--bs-dark-bg-subtle: #ced4da;
|
||||
--bs-primary-border-subtle: #9ec5fe;
|
||||
--bs-secondary-border-subtle: #c4c8cb;
|
||||
--bs-success-border-subtle: #a3cfbb;
|
||||
--bs-info-border-subtle: #9eeaf9;
|
||||
--bs-warning-border-subtle: #ffe69c;
|
||||
--bs-danger-border-subtle: #f1aeb5;
|
||||
--bs-light-border-subtle: #e9ecef;
|
||||
--bs-dark-border-subtle: #adb5bd;
|
||||
--bs-white-rgb: 255, 255, 255;
|
||||
--bs-black-rgb: 0, 0, 0;
|
||||
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||
--bs-body-font-size: 1rem;
|
||||
--bs-body-font-weight: 400;
|
||||
--bs-body-line-height: 1.5;
|
||||
--bs-body-color: #212529;
|
||||
--bs-body-color-rgb: 33, 37, 41;
|
||||
--bs-body-bg: #fff;
|
||||
--bs-body-bg-rgb: 255, 255, 255;
|
||||
--bs-emphasis-color: #000;
|
||||
--bs-emphasis-color-rgb: 0, 0, 0;
|
||||
--bs-secondary-color: rgba(33, 37, 41, 0.75);
|
||||
--bs-secondary-color-rgb: 33, 37, 41;
|
||||
--bs-secondary-bg: #e9ecef;
|
||||
--bs-secondary-bg-rgb: 233, 236, 239;
|
||||
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
|
||||
--bs-tertiary-color-rgb: 33, 37, 41;
|
||||
--bs-tertiary-bg: #f8f9fa;
|
||||
--bs-tertiary-bg-rgb: 248, 249, 250;
|
||||
--bs-heading-color: inherit;
|
||||
--bs-link-color: #0d6efd;
|
||||
--bs-link-color-rgb: 13, 110, 253;
|
||||
--bs-link-decoration: underline;
|
||||
--bs-link-hover-color: #0a58ca;
|
||||
--bs-link-hover-color-rgb: 10, 88, 202;
|
||||
--bs-code-color: #d63384;
|
||||
--bs-highlight-color: #212529;
|
||||
--bs-highlight-bg: #fff3cd;
|
||||
--bs-border-width: 1px;
|
||||
--bs-border-style: solid;
|
||||
--bs-border-color: #dee2e6;
|
||||
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
|
||||
--bs-border-radius: 0.375rem;
|
||||
--bs-border-radius-sm: 0.25rem;
|
||||
--bs-border-radius-lg: 0.5rem;
|
||||
--bs-border-radius-xl: 1rem;
|
||||
--bs-border-radius-xxl: 2rem;
|
||||
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
|
||||
--bs-border-radius-pill: 50rem;
|
||||
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
|
||||
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
--bs-focus-ring-width: 0.25rem;
|
||||
--bs-focus-ring-opacity: 0.25;
|
||||
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
|
||||
--bs-form-valid-color: #198754;
|
||||
--bs-form-valid-border-color: #198754;
|
||||
--bs-form-invalid-color: #dc3545;
|
||||
--bs-form-invalid-border-color: #dc3545;
|
||||
}
|
||||
|
||||
[data-bs-theme=dark] {
|
||||
color-scheme: dark;
|
||||
--bs-body-color: #dee2e6;
|
||||
--bs-body-color-rgb: 222, 226, 230;
|
||||
--bs-body-bg: #212529;
|
||||
--bs-body-bg-rgb: 33, 37, 41;
|
||||
--bs-emphasis-color: #fff;
|
||||
--bs-emphasis-color-rgb: 255, 255, 255;
|
||||
--bs-secondary-color: rgba(222, 226, 230, 0.75);
|
||||
--bs-secondary-color-rgb: 222, 226, 230;
|
||||
--bs-secondary-bg: #343a40;
|
||||
--bs-secondary-bg-rgb: 52, 58, 64;
|
||||
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
|
||||
--bs-tertiary-color-rgb: 222, 226, 230;
|
||||
--bs-tertiary-bg: #2b3035;
|
||||
--bs-tertiary-bg-rgb: 43, 48, 53;
|
||||
--bs-primary-text-emphasis: #6ea8fe;
|
||||
--bs-secondary-text-emphasis: #a7acb1;
|
||||
--bs-success-text-emphasis: #75b798;
|
||||
--bs-info-text-emphasis: #6edff6;
|
||||
--bs-warning-text-emphasis: #ffda6a;
|
||||
--bs-danger-text-emphasis: #ea868f;
|
||||
--bs-light-text-emphasis: #f8f9fa;
|
||||
--bs-dark-text-emphasis: #dee2e6;
|
||||
--bs-primary-bg-subtle: #031633;
|
||||
--bs-secondary-bg-subtle: #161719;
|
||||
--bs-success-bg-subtle: #051b11;
|
||||
--bs-info-bg-subtle: #032830;
|
||||
--bs-warning-bg-subtle: #332701;
|
||||
--bs-danger-bg-subtle: #2c0b0e;
|
||||
--bs-light-bg-subtle: #343a40;
|
||||
--bs-dark-bg-subtle: #1a1d20;
|
||||
--bs-primary-border-subtle: #084298;
|
||||
--bs-secondary-border-subtle: #41464b;
|
||||
--bs-success-border-subtle: #0f5132;
|
||||
--bs-info-border-subtle: #087990;
|
||||
--bs-warning-border-subtle: #997404;
|
||||
--bs-danger-border-subtle: #842029;
|
||||
--bs-light-border-subtle: #495057;
|
||||
--bs-dark-border-subtle: #343a40;
|
||||
--bs-heading-color: inherit;
|
||||
--bs-link-color: #6ea8fe;
|
||||
--bs-link-hover-color: #8bb9fe;
|
||||
--bs-link-color-rgb: 110, 168, 254;
|
||||
--bs-link-hover-color-rgb: 139, 185, 254;
|
||||
--bs-code-color: #e685b5;
|
||||
--bs-highlight-color: #dee2e6;
|
||||
--bs-highlight-bg: #664d03;
|
||||
--bs-border-color: #495057;
|
||||
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
|
||||
--bs-form-valid-color: #75b798;
|
||||
--bs-form-valid-border-color: #75b798;
|
||||
--bs-form-invalid-color: #ea868f;
|
||||
--bs-form-invalid-border-color: #ea868f;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
:root {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: var(--bs-body-font-family);
|
||||
font-size: var(--bs-body-font-size);
|
||||
font-weight: var(--bs-body-font-weight);
|
||||
line-height: var(--bs-body-line-height);
|
||||
color: var(--bs-body-color);
|
||||
text-align: var(--bs-body-text-align);
|
||||
background-color: var(--bs-body-bg);
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 1rem 0;
|
||||
color: inherit;
|
||||
border: 0;
|
||||
border-top: var(--bs-border-width) solid;
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
h6, h5, h4, h3, h2, h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
color: var(--bs-heading-color);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: calc(1.375rem + 1.5vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: calc(1.325rem + 0.9vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: calc(1.3rem + 0.6vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h3 {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h4 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title] {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
-webkit-text-decoration-skip-ink: none;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: 0.5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
mark {
|
||||
padding: 0.1875em;
|
||||
color: var(--bs-highlight-color);
|
||||
background-color: var(--bs-highlight-bg);
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 0.75em;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:hover {
|
||||
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
|
||||
}
|
||||
|
||||
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: var(--bs-font-monospace);
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
pre code {
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 0.875em;
|
||||
color: var(--bs-code-color);
|
||||
word-wrap: break-word;
|
||||
}
|
||||
a > code {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
kbd {
|
||||
padding: 0.1875rem 0.375rem;
|
||||
font-size: 0.875em;
|
||||
color: var(--bs-body-bg);
|
||||
background-color: var(--bs-body-color);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
kbd kbd {
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img,
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
color: var(--bs-secondary-color);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
text-align: -webkit-match-parent;
|
||||
}
|
||||
|
||||
thead,
|
||||
tbody,
|
||||
tfoot,
|
||||
tr,
|
||||
td,
|
||||
th {
|
||||
border-color: inherit;
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus:not(:focus-visible) {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
[role=button] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
select {
|
||||
word-wrap: normal;
|
||||
}
|
||||
select:disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
button,
|
||||
[type=button],
|
||||
[type=reset],
|
||||
[type=submit] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
button:not(:disabled),
|
||||
[type=button]:not(:disabled),
|
||||
[type=reset]:not(:disabled),
|
||||
[type=submit]:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
float: left;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
line-height: inherit;
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
legend {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
legend + * {
|
||||
clear: left;
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit-fields-wrapper,
|
||||
::-webkit-datetime-edit-text,
|
||||
::-webkit-datetime-edit-minute,
|
||||
::-webkit-datetime-edit-hour-field,
|
||||
::-webkit-datetime-edit-day-field,
|
||||
::-webkit-datetime-edit-month-field,
|
||||
::-webkit-datetime-edit-year-field {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-inner-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type=search] {
|
||||
-webkit-appearance: textfield;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
/* rtl:raw:
|
||||
[type="tel"],
|
||||
[type="url"],
|
||||
[type="email"],
|
||||
[type="number"] {
|
||||
direction: ltr;
|
||||
}
|
||||
*/
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
::file-selector-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
||||
1
html/assets/css/bootstrap-reboot.css.map
Executable file
1
html/assets/css/bootstrap-reboot.css.map
Executable file
File diff suppressed because one or more lines are too long
6
html/assets/css/bootstrap-reboot.min.css
vendored
Executable file
6
html/assets/css/bootstrap-reboot.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
1
html/assets/css/bootstrap-reboot.min.css.map
Executable file
1
html/assets/css/bootstrap-reboot.min.css.map
Executable file
File diff suppressed because one or more lines are too long
594
html/assets/css/bootstrap-reboot.rtl.css
vendored
Executable file
594
html/assets/css/bootstrap-reboot.rtl.css
vendored
Executable file
@@ -0,0 +1,594 @@
|
||||
/*!
|
||||
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2024 The Bootstrap Authors
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
:root,
|
||||
[data-bs-theme=light] {
|
||||
--bs-blue: #0d6efd;
|
||||
--bs-indigo: #6610f2;
|
||||
--bs-purple: #6f42c1;
|
||||
--bs-pink: #d63384;
|
||||
--bs-red: #dc3545;
|
||||
--bs-orange: #fd7e14;
|
||||
--bs-yellow: #ffc107;
|
||||
--bs-green: #198754;
|
||||
--bs-teal: #20c997;
|
||||
--bs-cyan: #0dcaf0;
|
||||
--bs-black: #000;
|
||||
--bs-white: #fff;
|
||||
--bs-gray: #6c757d;
|
||||
--bs-gray-dark: #343a40;
|
||||
--bs-gray-100: #f8f9fa;
|
||||
--bs-gray-200: #e9ecef;
|
||||
--bs-gray-300: #dee2e6;
|
||||
--bs-gray-400: #ced4da;
|
||||
--bs-gray-500: #adb5bd;
|
||||
--bs-gray-600: #6c757d;
|
||||
--bs-gray-700: #495057;
|
||||
--bs-gray-800: #343a40;
|
||||
--bs-gray-900: #212529;
|
||||
--bs-primary: #0d6efd;
|
||||
--bs-secondary: #6c757d;
|
||||
--bs-success: #198754;
|
||||
--bs-info: #0dcaf0;
|
||||
--bs-warning: #ffc107;
|
||||
--bs-danger: #dc3545;
|
||||
--bs-light: #f8f9fa;
|
||||
--bs-dark: #212529;
|
||||
--bs-primary-rgb: 13, 110, 253;
|
||||
--bs-secondary-rgb: 108, 117, 125;
|
||||
--bs-success-rgb: 25, 135, 84;
|
||||
--bs-info-rgb: 13, 202, 240;
|
||||
--bs-warning-rgb: 255, 193, 7;
|
||||
--bs-danger-rgb: 220, 53, 69;
|
||||
--bs-light-rgb: 248, 249, 250;
|
||||
--bs-dark-rgb: 33, 37, 41;
|
||||
--bs-primary-text-emphasis: #052c65;
|
||||
--bs-secondary-text-emphasis: #2b2f32;
|
||||
--bs-success-text-emphasis: #0a3622;
|
||||
--bs-info-text-emphasis: #055160;
|
||||
--bs-warning-text-emphasis: #664d03;
|
||||
--bs-danger-text-emphasis: #58151c;
|
||||
--bs-light-text-emphasis: #495057;
|
||||
--bs-dark-text-emphasis: #495057;
|
||||
--bs-primary-bg-subtle: #cfe2ff;
|
||||
--bs-secondary-bg-subtle: #e2e3e5;
|
||||
--bs-success-bg-subtle: #d1e7dd;
|
||||
--bs-info-bg-subtle: #cff4fc;
|
||||
--bs-warning-bg-subtle: #fff3cd;
|
||||
--bs-danger-bg-subtle: #f8d7da;
|
||||
--bs-light-bg-subtle: #fcfcfd;
|
||||
--bs-dark-bg-subtle: #ced4da;
|
||||
--bs-primary-border-subtle: #9ec5fe;
|
||||
--bs-secondary-border-subtle: #c4c8cb;
|
||||
--bs-success-border-subtle: #a3cfbb;
|
||||
--bs-info-border-subtle: #9eeaf9;
|
||||
--bs-warning-border-subtle: #ffe69c;
|
||||
--bs-danger-border-subtle: #f1aeb5;
|
||||
--bs-light-border-subtle: #e9ecef;
|
||||
--bs-dark-border-subtle: #adb5bd;
|
||||
--bs-white-rgb: 255, 255, 255;
|
||||
--bs-black-rgb: 0, 0, 0;
|
||||
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||
--bs-body-font-size: 1rem;
|
||||
--bs-body-font-weight: 400;
|
||||
--bs-body-line-height: 1.5;
|
||||
--bs-body-color: #212529;
|
||||
--bs-body-color-rgb: 33, 37, 41;
|
||||
--bs-body-bg: #fff;
|
||||
--bs-body-bg-rgb: 255, 255, 255;
|
||||
--bs-emphasis-color: #000;
|
||||
--bs-emphasis-color-rgb: 0, 0, 0;
|
||||
--bs-secondary-color: rgba(33, 37, 41, 0.75);
|
||||
--bs-secondary-color-rgb: 33, 37, 41;
|
||||
--bs-secondary-bg: #e9ecef;
|
||||
--bs-secondary-bg-rgb: 233, 236, 239;
|
||||
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
|
||||
--bs-tertiary-color-rgb: 33, 37, 41;
|
||||
--bs-tertiary-bg: #f8f9fa;
|
||||
--bs-tertiary-bg-rgb: 248, 249, 250;
|
||||
--bs-heading-color: inherit;
|
||||
--bs-link-color: #0d6efd;
|
||||
--bs-link-color-rgb: 13, 110, 253;
|
||||
--bs-link-decoration: underline;
|
||||
--bs-link-hover-color: #0a58ca;
|
||||
--bs-link-hover-color-rgb: 10, 88, 202;
|
||||
--bs-code-color: #d63384;
|
||||
--bs-highlight-color: #212529;
|
||||
--bs-highlight-bg: #fff3cd;
|
||||
--bs-border-width: 1px;
|
||||
--bs-border-style: solid;
|
||||
--bs-border-color: #dee2e6;
|
||||
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
|
||||
--bs-border-radius: 0.375rem;
|
||||
--bs-border-radius-sm: 0.25rem;
|
||||
--bs-border-radius-lg: 0.5rem;
|
||||
--bs-border-radius-xl: 1rem;
|
||||
--bs-border-radius-xxl: 2rem;
|
||||
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
|
||||
--bs-border-radius-pill: 50rem;
|
||||
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
|
||||
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
--bs-focus-ring-width: 0.25rem;
|
||||
--bs-focus-ring-opacity: 0.25;
|
||||
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
|
||||
--bs-form-valid-color: #198754;
|
||||
--bs-form-valid-border-color: #198754;
|
||||
--bs-form-invalid-color: #dc3545;
|
||||
--bs-form-invalid-border-color: #dc3545;
|
||||
}
|
||||
|
||||
[data-bs-theme=dark] {
|
||||
color-scheme: dark;
|
||||
--bs-body-color: #dee2e6;
|
||||
--bs-body-color-rgb: 222, 226, 230;
|
||||
--bs-body-bg: #212529;
|
||||
--bs-body-bg-rgb: 33, 37, 41;
|
||||
--bs-emphasis-color: #fff;
|
||||
--bs-emphasis-color-rgb: 255, 255, 255;
|
||||
--bs-secondary-color: rgba(222, 226, 230, 0.75);
|
||||
--bs-secondary-color-rgb: 222, 226, 230;
|
||||
--bs-secondary-bg: #343a40;
|
||||
--bs-secondary-bg-rgb: 52, 58, 64;
|
||||
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
|
||||
--bs-tertiary-color-rgb: 222, 226, 230;
|
||||
--bs-tertiary-bg: #2b3035;
|
||||
--bs-tertiary-bg-rgb: 43, 48, 53;
|
||||
--bs-primary-text-emphasis: #6ea8fe;
|
||||
--bs-secondary-text-emphasis: #a7acb1;
|
||||
--bs-success-text-emphasis: #75b798;
|
||||
--bs-info-text-emphasis: #6edff6;
|
||||
--bs-warning-text-emphasis: #ffda6a;
|
||||
--bs-danger-text-emphasis: #ea868f;
|
||||
--bs-light-text-emphasis: #f8f9fa;
|
||||
--bs-dark-text-emphasis: #dee2e6;
|
||||
--bs-primary-bg-subtle: #031633;
|
||||
--bs-secondary-bg-subtle: #161719;
|
||||
--bs-success-bg-subtle: #051b11;
|
||||
--bs-info-bg-subtle: #032830;
|
||||
--bs-warning-bg-subtle: #332701;
|
||||
--bs-danger-bg-subtle: #2c0b0e;
|
||||
--bs-light-bg-subtle: #343a40;
|
||||
--bs-dark-bg-subtle: #1a1d20;
|
||||
--bs-primary-border-subtle: #084298;
|
||||
--bs-secondary-border-subtle: #41464b;
|
||||
--bs-success-border-subtle: #0f5132;
|
||||
--bs-info-border-subtle: #087990;
|
||||
--bs-warning-border-subtle: #997404;
|
||||
--bs-danger-border-subtle: #842029;
|
||||
--bs-light-border-subtle: #495057;
|
||||
--bs-dark-border-subtle: #343a40;
|
||||
--bs-heading-color: inherit;
|
||||
--bs-link-color: #6ea8fe;
|
||||
--bs-link-hover-color: #8bb9fe;
|
||||
--bs-link-color-rgb: 110, 168, 254;
|
||||
--bs-link-hover-color-rgb: 139, 185, 254;
|
||||
--bs-code-color: #e685b5;
|
||||
--bs-highlight-color: #dee2e6;
|
||||
--bs-highlight-bg: #664d03;
|
||||
--bs-border-color: #495057;
|
||||
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
|
||||
--bs-form-valid-color: #75b798;
|
||||
--bs-form-valid-border-color: #75b798;
|
||||
--bs-form-invalid-color: #ea868f;
|
||||
--bs-form-invalid-border-color: #ea868f;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
:root {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: var(--bs-body-font-family);
|
||||
font-size: var(--bs-body-font-size);
|
||||
font-weight: var(--bs-body-font-weight);
|
||||
line-height: var(--bs-body-line-height);
|
||||
color: var(--bs-body-color);
|
||||
text-align: var(--bs-body-text-align);
|
||||
background-color: var(--bs-body-bg);
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 1rem 0;
|
||||
color: inherit;
|
||||
border: 0;
|
||||
border-top: var(--bs-border-width) solid;
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
h6, h5, h4, h3, h2, h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
color: var(--bs-heading-color);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: calc(1.375rem + 1.5vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: calc(1.325rem + 0.9vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: calc(1.3rem + 0.6vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h3 {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h4 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title] {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
-webkit-text-decoration-skip-ink: none;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: 0.5rem;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
mark {
|
||||
padding: 0.1875em;
|
||||
color: var(--bs-highlight-color);
|
||||
background-color: var(--bs-highlight-bg);
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 0.75em;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:hover {
|
||||
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
|
||||
}
|
||||
|
||||
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: var(--bs-font-monospace);
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
pre code {
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 0.875em;
|
||||
color: var(--bs-code-color);
|
||||
word-wrap: break-word;
|
||||
}
|
||||
a > code {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
kbd {
|
||||
padding: 0.1875rem 0.375rem;
|
||||
font-size: 0.875em;
|
||||
color: var(--bs-body-bg);
|
||||
background-color: var(--bs-body-color);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
kbd kbd {
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img,
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
color: var(--bs-secondary-color);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
text-align: -webkit-match-parent;
|
||||
}
|
||||
|
||||
thead,
|
||||
tbody,
|
||||
tfoot,
|
||||
tr,
|
||||
td,
|
||||
th {
|
||||
border-color: inherit;
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus:not(:focus-visible) {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
[role=button] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
select {
|
||||
word-wrap: normal;
|
||||
}
|
||||
select:disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
button,
|
||||
[type=button],
|
||||
[type=reset],
|
||||
[type=submit] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
button:not(:disabled),
|
||||
[type=button]:not(:disabled),
|
||||
[type=reset]:not(:disabled),
|
||||
[type=submit]:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
float: right;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
line-height: inherit;
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
legend {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
legend + * {
|
||||
clear: right;
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit-fields-wrapper,
|
||||
::-webkit-datetime-edit-text,
|
||||
::-webkit-datetime-edit-minute,
|
||||
::-webkit-datetime-edit-hour-field,
|
||||
::-webkit-datetime-edit-day-field,
|
||||
::-webkit-datetime-edit-month-field,
|
||||
::-webkit-datetime-edit-year-field {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-inner-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type=search] {
|
||||
-webkit-appearance: textfield;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
[type="tel"],
|
||||
[type="url"],
|
||||
[type="email"],
|
||||
[type="number"] {
|
||||
direction: ltr;
|
||||
}
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
::file-selector-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */
|
||||
1
html/assets/css/bootstrap-reboot.rtl.css.map
Executable file
1
html/assets/css/bootstrap-reboot.rtl.css.map
Executable file
File diff suppressed because one or more lines are too long
6
html/assets/css/bootstrap-reboot.rtl.min.css
vendored
Executable file
6
html/assets/css/bootstrap-reboot.rtl.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
1
html/assets/css/bootstrap-reboot.rtl.min.css.map
Executable file
1
html/assets/css/bootstrap-reboot.rtl.min.css.map
Executable file
File diff suppressed because one or more lines are too long
5402
html/assets/css/bootstrap-utilities.css
vendored
Executable file
5402
html/assets/css/bootstrap-utilities.css
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1
html/assets/css/bootstrap-utilities.css.map
Executable file
1
html/assets/css/bootstrap-utilities.css.map
Executable file
File diff suppressed because one or more lines are too long
6
html/assets/css/bootstrap-utilities.min.css
vendored
Executable file
6
html/assets/css/bootstrap-utilities.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
1
html/assets/css/bootstrap-utilities.min.css.map
Executable file
1
html/assets/css/bootstrap-utilities.min.css.map
Executable file
File diff suppressed because one or more lines are too long
5393
html/assets/css/bootstrap-utilities.rtl.css
vendored
Executable file
5393
html/assets/css/bootstrap-utilities.rtl.css
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1
html/assets/css/bootstrap-utilities.rtl.css.map
Executable file
1
html/assets/css/bootstrap-utilities.rtl.css.map
Executable file
File diff suppressed because one or more lines are too long
6
html/assets/css/bootstrap-utilities.rtl.min.css
vendored
Executable file
6
html/assets/css/bootstrap-utilities.rtl.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
1
html/assets/css/bootstrap-utilities.rtl.min.css.map
Executable file
1
html/assets/css/bootstrap-utilities.rtl.min.css.map
Executable file
File diff suppressed because one or more lines are too long
12057
html/assets/css/bootstrap.css
vendored
Executable file
12057
html/assets/css/bootstrap.css
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1
html/assets/css/bootstrap.css.map
Executable file
1
html/assets/css/bootstrap.css.map
Executable file
File diff suppressed because one or more lines are too long
6
html/assets/css/bootstrap.min.css
vendored
Executable file
6
html/assets/css/bootstrap.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
1
html/assets/css/bootstrap.min.css.map
Executable file
1
html/assets/css/bootstrap.min.css.map
Executable file
File diff suppressed because one or more lines are too long
12030
html/assets/css/bootstrap.rtl.css
vendored
Executable file
12030
html/assets/css/bootstrap.rtl.css
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1
html/assets/css/bootstrap.rtl.css.map
Executable file
1
html/assets/css/bootstrap.rtl.css.map
Executable file
File diff suppressed because one or more lines are too long
6
html/assets/css/bootstrap.rtl.min.css
vendored
Executable file
6
html/assets/css/bootstrap.rtl.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
1
html/assets/css/bootstrap.rtl.min.css.map
Executable file
1
html/assets/css/bootstrap.rtl.min.css.map
Executable file
File diff suppressed because one or more lines are too long
4
html/assets/icons/arrow-clockwise.svg
Executable file
4
html/assets/icons/arrow-clockwise.svg
Executable file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z"/>
|
||||
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 349 B |
32
html/assets/icons/bootstrap-icons.svg
Executable file
32
html/assets/icons/bootstrap-icons.svg
Executable file
@@ -0,0 +1,32 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||
<symbol id="house" viewBox="0 0 16 16">
|
||||
<path d="M8 3.293l6 6V14.5a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1v-2.5a1 1 0 0 0-2 0V14.5a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V9.293l6-6zM7.293 1a1 1 0 0 1 1.414 0l7 7a1 1 0 0 1-1.414 1.414L8 2.414 1.707 8.707a1 1 0 1 1-1.414-1.414l7-7z"/>
|
||||
</symbol>
|
||||
<symbol id="info-circle" viewBox="0 0 16 16">
|
||||
<path d="M8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1zm0 13A6 6 0 1 1 8 2a6 6 0 0 1 0 12z"/>
|
||||
<path d="M8.93 6.588a.5.5 0 0 0-.427-.255H8.5a.5.5 0 0 0-.5.5v3.99a.5.5 0 0 0 .5.5h.003a.5.5 0 0 0 .491-.41l.008-.09v-3.99a.5.5 0 0 0-.072-.246z"/>
|
||||
<circle cx="8" cy="4.5" r="1"/>
|
||||
</symbol>
|
||||
<symbol id="gear" viewBox="0 0 16 16">
|
||||
<path d="M8 1.5A2.5 2.5 0 1 1 5.5 4a2.5 2.5 0 0 1 2.5-2.5zM6.5 2a1.5 1.5 0 1 0 3 0 1.5 1.5 0 0 0-3 0z"/>
|
||||
</symbol>
|
||||
<symbol id="telephone" viewBox="0 0 16 16">
|
||||
<path d="M3.654 1.328a.678.678 0 0 1 .493-.289c.295 0 .496.155.593.454l.028.115c.017.075.25 1.098.342 1.448.09.342.03.575-.103.725l-.05.065c-.084.11-.178.22-.276.326-.97.994-1.115 1.174-.614 2.171.475.963 2.042 3.032 3.224 3.564 1.224.554 1.572.456 2.18-.193.396-.414.495-.576.9-.522.305.042 1.122.387 1.447.524.465.197.582.34.541.676-.01.098-.035.268-.055.347-.158.614-.44 1.207-.788 1.504a2.727 2.727 0 0 1-1.336.538c-.27.037-.538.073-.852.073-.883 0-1.68-.29-2.343-.655a15.235 15.235 0 0 1-3.287-2.52A15.133 15.133 0 0 1 1.433 6.47a15.578 15.578 0 0 1-.654-2.343c-.032-.308.001-.582.073-.853a2.725 2.725 0 0 1 .538-1.336C1.39 1.946 1.982 1.664 2.596 1.506a15.57 15.57 0 0 1 .348-.055c.34-.041.48-.141.676-.54.138-.325.483-1.142.522-1.447.054-.406-.108-.504-.523-.9z"/>
|
||||
</symbol>
|
||||
<symbol id="thermometer-half" viewBox="0 0 16 16">
|
||||
<path d="M8 2a2 2 0 1 0-4 0v9.585A1.5 1.5 0 1 0 6.5 15v-3.5h1V15a1.5 1.5 0 1 0 2.5-1.415V2zm-3 .5a1 1 0 1 1 2 0v9h-2V2.5z"/>
|
||||
</symbol>
|
||||
<symbol id="router" viewBox="0 0 16 16">
|
||||
<path d="M7.5 5A1.5 1.5 0 0 1 9 3.5h3A1.5 1.5 0 0 1 13.5 5h-6zm2-2h2a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1zm3 4h-2a.5.5 0 0 0 0 1h2a.5.5 0 0 0 0-1z"/>
|
||||
</symbol>
|
||||
<symbol id="wifi" viewBox="0 0 16 16">
|
||||
<path d="M8 12.5a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0zm1.937-1.43A5.987 5.987 0 0 1 13.5 8a.5.5 0 0 1 1 0 6.987 6.987 0 0 0-4.563 2.57.5.5 0 0 1-.75 0z"/>
|
||||
</symbol>
|
||||
<symbol id="journal" viewBox="0 0 16 16">
|
||||
<path d="M3 0h10a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1zM2 1v14a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V1a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2z"/>
|
||||
</symbol>
|
||||
<symbol id="person" viewBox="0 0 16 16">
|
||||
<path d="M10 5a3 3 0 1 1-6 0 3 3 0 0 1 6 0zm4 8c0 1-2 2-5 2s-5-1-5-2 2-3 5-3 5 2 5 3z"/>
|
||||
</symbol>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
4
html/assets/icons/bootstrap-icons2.svg
Executable file
4
html/assets/icons/bootstrap-icons2.svg
Executable file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-wifi" viewBox="0 0 16 16">
|
||||
<path d="M15.384 6.115a.485.485 0 0 0-.047-.736A12.44 12.44 0 0 0 8 3C5.259 3 2.723 3.882.663 5.379a.485.485 0 0 0-.048.736.52.52 0 0 0 .668.05A11.45 11.45 0 0 1 8 4c2.507 0 4.827.802 6.716 2.164.205.148.49.13.668-.049"/>
|
||||
<path d="M13.229 8.271a.482.482 0 0 0-.063-.745A9.46 9.46 0 0 0 8 6c-1.905 0-3.68.56-5.166 1.526a.48.48 0 0 0-.063.745.525.525 0 0 0 .652.065A8.46 8.46 0 0 1 8 7a8.46 8.46 0 0 1 4.576 1.336c.206.132.48.108.653-.065m-2.183 2.183c.226-.226.185-.605-.1-.75A6.5 6.5 0 0 0 8 9c-1.06 0-2.062.254-2.946.704-.285.145-.326.524-.1.75l.015.015c.16.16.407.19.611.09A5.5 5.5 0 0 1 8 10c.868 0 1.69.201 2.42.56.203.1.45.07.61-.091zM9.06 12.44c.196-.196.198-.52-.04-.66A2 2 0 0 0 8 11.5a2 2 0 0 0-1.02.28c-.238.14-.236.464-.04.66l.706.706a.5.5 0 0 0 .707 0l.707-.707z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 911 B |
BIN
html/assets/img/LogoNebuleAir.png
Executable file
BIN
html/assets/img/LogoNebuleAir.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
2
html/assets/jquery/jquery-3.7.1.min.js
vendored
Executable file
2
html/assets/jquery/jquery-3.7.1.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
6314
html/assets/js/bootstrap.bundle.js
vendored
Executable file
6314
html/assets/js/bootstrap.bundle.js
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1
html/assets/js/bootstrap.bundle.js.map
Executable file
1
html/assets/js/bootstrap.bundle.js.map
Executable file
File diff suppressed because one or more lines are too long
7
html/assets/js/bootstrap.bundle.min.js
vendored
Executable file
7
html/assets/js/bootstrap.bundle.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
1
html/assets/js/bootstrap.bundle.min.js.map
Executable file
1
html/assets/js/bootstrap.bundle.min.js.map
Executable file
File diff suppressed because one or more lines are too long
4447
html/assets/js/bootstrap.esm.js
vendored
Executable file
4447
html/assets/js/bootstrap.esm.js
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1
html/assets/js/bootstrap.esm.js.map
Executable file
1
html/assets/js/bootstrap.esm.js.map
Executable file
File diff suppressed because one or more lines are too long
7
html/assets/js/bootstrap.esm.min.js
vendored
Executable file
7
html/assets/js/bootstrap.esm.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
1
html/assets/js/bootstrap.esm.min.js.map
Executable file
1
html/assets/js/bootstrap.esm.min.js.map
Executable file
File diff suppressed because one or more lines are too long
4494
html/assets/js/bootstrap.js
vendored
Executable file
4494
html/assets/js/bootstrap.js
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1
html/assets/js/bootstrap.js.map
Executable file
1
html/assets/js/bootstrap.js.map
Executable file
File diff suppressed because one or more lines are too long
7
html/assets/js/bootstrap.min.js
vendored
Executable file
7
html/assets/js/bootstrap.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
1
html/assets/js/bootstrap.min.js.map
Executable file
1
html/assets/js/bootstrap.min.js.map
Executable file
File diff suppressed because one or more lines are too long
89
html/index.html
Executable file
89
html/index.html
Executable file
@@ -0,0 +1,89 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Responsive Sidebar Template</title>
|
||||
<link rel="stylesheet" href="assets/css/bootstrap.min.css">
|
||||
<style>
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
#sidebar a.nav-link {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
#sidebar a.nav-link:hover {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
#sidebar a.nav-link svg {
|
||||
margin-right: 8px; /* Add spacing between icons and text */
|
||||
}
|
||||
#sidebar {
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
.offcanvas-backdrop {
|
||||
z-index: 1040;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Topbar -->
|
||||
<span id="topbar"></span>
|
||||
|
||||
<!-- Sidebar Offcanvas for Mobile -->
|
||||
<div class="offcanvas offcanvas-start text-white bg-dark" tabindex="-1" id="sidebarOffcanvas" aria-labelledby="sidebarOffcanvasLabel">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title" id="sidebarOffcanvasLabel">NebuleAir</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body" id="sidebar_mobile">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid mt-5">
|
||||
<div class="row">
|
||||
<aside class="col-md-2 col-lg-1 d-none d-md-block vh-100 position-fixed bg-dark text-white" id="sidebar">
|
||||
</aside>
|
||||
<!-- Main content -->
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 offset-md-3 offset-lg-2 px-md-4">
|
||||
<h1 class="mt-4">Votre capteur</h1>
|
||||
<p>This is the main content area. You can add any content you want here.</p>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam.</p>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JAVASCRIPT -->
|
||||
|
||||
<!-- Link Ajax locally -->
|
||||
<script src="assets/jquery/jquery-3.7.1.min.js"></script>
|
||||
<!-- Link Bootstrap JS and Popper.js locally -->
|
||||
<script src="assets/js/bootstrap.bundle.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const elementsToLoad = [
|
||||
{ id: 'topbar', file: 'topbar.html' },
|
||||
{ id: 'sidebar', file: 'sidebar.html' },
|
||||
{ id: 'sidebar_mobile', file: 'sidebar.html' }
|
||||
];
|
||||
|
||||
elementsToLoad.forEach(({ id, file }) => {
|
||||
fetch(file)
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.innerHTML = data;
|
||||
}
|
||||
})
|
||||
.catch(error => console.error(`Error loading ${file}:`, error));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
278
html/launcher.php
Executable file
278
html/launcher.php
Executable file
@@ -0,0 +1,278 @@
|
||||
<?php
|
||||
$type=$_GET['type'];
|
||||
|
||||
if ($type == "RTC_time") {
|
||||
$time = shell_exec("date '+%d/%m/%Y %H:%M:%S'");
|
||||
echo $time;
|
||||
}
|
||||
|
||||
if ($type == "git_pull") {
|
||||
$command = 'sudo git pull';
|
||||
$output = shell_exec($command);
|
||||
echo $output;
|
||||
}
|
||||
|
||||
if ($type == "sshTunnel") {
|
||||
$ssh_port=$_GET['ssh_port'];
|
||||
$command = 'sudo ssh -i /var/www/.ssh/id_rsa -f -N -R "'.$ssh_port.':localhost:22" -p 50221 -o StrictHostKeyChecking=no "airlab_server1@aircarto.fr"';
|
||||
$output = shell_exec($command);
|
||||
echo $output;
|
||||
}
|
||||
|
||||
if ($type == "reboot") {
|
||||
$command = 'sudo reboot';
|
||||
$output = shell_exec($command);
|
||||
}
|
||||
|
||||
if ($type == "npm") {
|
||||
$port=$_GET['port'];
|
||||
$command = '/usr/bin/python3 /var/www/nebuleair_pro_4g/npm.py ' . $port;
|
||||
$output = shell_exec($command);
|
||||
echo $output;
|
||||
}
|
||||
|
||||
if ($type == "envea") {
|
||||
$port=$_GET['port'];
|
||||
$name=$_GET['name'];
|
||||
$command = '/usr/bin/python3 /var/www/nebuleair_pro_4g/envea/read_value.py ' . $port;
|
||||
$output = shell_exec($command);
|
||||
echo $output;
|
||||
}
|
||||
|
||||
if ($type == "noise") {
|
||||
$command = '/var/www/nebuleair_pro_4g/sound_meter/sound_meter';
|
||||
$output = shell_exec($command);
|
||||
echo $output;
|
||||
}
|
||||
|
||||
if ($type == "BME280") {
|
||||
$command = '/usr/bin/python3 /var/www/nebuleair_pro_4g/BME280/read.py';
|
||||
$output = shell_exec($command);
|
||||
echo $output;
|
||||
}
|
||||
|
||||
# SARA R4 COMMANDS
|
||||
if ($type == "sara") {
|
||||
$port=$_GET['port'];
|
||||
$sara_command=$_GET['command'];
|
||||
$sara_command = escapeshellcmd($sara_command);
|
||||
$timeout=$_GET['timeout'];
|
||||
$command = '/usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara.py ' . $port . ' ' . $sara_command . ' ' . $timeout;
|
||||
$output = shell_exec($command);
|
||||
echo $output;
|
||||
}
|
||||
|
||||
# SARA R4 COMMANDS (MQTT)
|
||||
if ($type == "sara_getMQTT_config") {
|
||||
$port=$_GET['port'];
|
||||
$timeout=$_GET['timeout'];
|
||||
$command = '/usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/MQTT/get_config.py ' . $port . ' ' . $timeout;
|
||||
$output = shell_exec($command);
|
||||
echo $output;
|
||||
}
|
||||
|
||||
# SARA R4 COMMANDS (MQTT)
|
||||
if ($type == "sara_getMQTT_login_logout") {
|
||||
$port=$_GET['port'];
|
||||
$timeout=$_GET['timeout'];
|
||||
$login_logout=$_GET['login_logout'];
|
||||
$command = '/usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/MQTT/login_logout.py ' . $port . ' ' . $login_logout . ' ' . $timeout;
|
||||
$output = shell_exec($command);
|
||||
echo $output;
|
||||
}
|
||||
|
||||
# SARA R4 COMMANDS (MQTT -> publish)
|
||||
if ($type == "sara_MQTT_publish") {
|
||||
$port=$_GET['port'];
|
||||
$timeout=$_GET['timeout'];
|
||||
$message=$_GET['message'];
|
||||
$command = '/usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/MQTT/publish.py ' . $port . ' ' . $message . ' ' . $timeout;
|
||||
$output = shell_exec($command);
|
||||
echo $output;
|
||||
}
|
||||
|
||||
#Connect to network
|
||||
if ($type == "sara_connectNetwork") {
|
||||
$port=$_GET['port'];
|
||||
$timeout=$_GET['timeout'];
|
||||
$networkID=$_GET['networkID'];
|
||||
$command = '/usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara_connectNetwork.py ' . $port . ' ' . $networkID . ' ' . $timeout;
|
||||
$output = shell_exec($command);
|
||||
echo $output;
|
||||
}
|
||||
|
||||
#SET THE URL for messaging
|
||||
if ($type == "sara_setURL") {
|
||||
$port=$_GET['port'];
|
||||
$url=$_GET['url'];
|
||||
$command = '/usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara_setURL.py ' . $port . ' ' . $url;
|
||||
$output = shell_exec($command);
|
||||
echo $output;
|
||||
}
|
||||
|
||||
#SET APN
|
||||
if ($type == "sara_APN") {
|
||||
$port=$_GET['port'];
|
||||
$timeout=$_GET['timeout'];
|
||||
$APN_address=$_GET['APN_address'];
|
||||
$command = '/usr/bin/python3 /var/www/moduleair_pro_4g/SARA/sara_setAPN.py ' . $port . ' ' . $APN_address . ' ' . $timeout;
|
||||
$output = shell_exec($command);
|
||||
echo $output;
|
||||
}
|
||||
|
||||
#TO WRITE MESSAGE TO MEMORY
|
||||
if ($type == "sara_writeMessage") {
|
||||
$port=$_GET['port'];
|
||||
$message=$_GET['message'];
|
||||
$message = escapeshellcmd($message);
|
||||
$type2=$_GET['type2'];
|
||||
|
||||
if ($type2 === "write") {
|
||||
$command = '/usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara_writeMessage.py ' . $port . ' ' . $message;
|
||||
$output = shell_exec($command);
|
||||
}
|
||||
if ($type2 === "read") {
|
||||
$command = '/usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara_readMessage.py ' . $port . ' ' . $message;
|
||||
$output = shell_exec($command);
|
||||
}
|
||||
if ($type2 === "erase") {
|
||||
$command = '/usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara_eraseMessage.py ' . $port . ' ' . $message;
|
||||
$output = shell_exec($command);
|
||||
}
|
||||
|
||||
echo $output;
|
||||
}
|
||||
|
||||
#Send the typed message to server (for ntfy notification)
|
||||
if ($type == "sara_sendMessage") {
|
||||
$port=$_GET['port'];
|
||||
$endpoint=$_GET['endpoint'];
|
||||
$endpoint = escapeshellcmd($endpoint);
|
||||
$command = '/usr/bin/python3 /var/www/nebuleair_pro_4g/SARA/sara_sendMessage.py ' . $port . ' ' . $endpoint;
|
||||
$output = shell_exec($command);
|
||||
echo $output;
|
||||
}
|
||||
|
||||
if ($type == "internet") {
|
||||
//eth0
|
||||
$command = 'nmcli -g GENERAL.STATE device show eth0';
|
||||
$eth0_connStatus = shell_exec($command);
|
||||
$eth0_connStatus = str_replace("\n", "", $eth0_connStatus);
|
||||
$command = 'nmcli -g IP4.ADDRESS device show eth0';
|
||||
$eth0_IPAddr = shell_exec($command);
|
||||
$eth0_IPAddr = str_replace("\n", "", $eth0_IPAddr);
|
||||
|
||||
//wlan0
|
||||
$command = 'nmcli -g GENERAL.STATE device show wlan0';
|
||||
$wlan0_connStatus = shell_exec($command);
|
||||
$wlan0_connStatus = str_replace("\n", "", $wlan0_connStatus);
|
||||
$command = 'nmcli -g IP4.ADDRESS device show wlan0';
|
||||
$wlan0_IPAddr = shell_exec($command);
|
||||
$wlan0_IPAddr = str_replace("\n", "", $wlan0_IPAddr);
|
||||
|
||||
$data= array(
|
||||
"ethernet" => array(
|
||||
"connection" => $eth0_connStatus,
|
||||
"IP" => $eth0_IPAddr
|
||||
),
|
||||
"wifi" => array(
|
||||
"connection" => $wlan0_connStatus,
|
||||
"IP" => $wlan0_IPAddr
|
||||
)
|
||||
);
|
||||
$json_data = json_encode($data);
|
||||
|
||||
echo $json_data;
|
||||
}
|
||||
|
||||
# IMPORTANT
|
||||
# c'est ici que la connexion vers le WIFI du client s'effectue.
|
||||
if ($type == "wifi_connect") {
|
||||
$SSID=$_GET['SSID'];
|
||||
$PASS=$_GET['pass'];
|
||||
|
||||
echo "will try to connect to </br>";
|
||||
echo "SSID: " . $SSID;
|
||||
echo "</br>";
|
||||
echo "Password: " . $PASS;
|
||||
echo "</br>";
|
||||
echo "</br>";
|
||||
|
||||
echo "You will be disconnected. If connection is successfull you can find the device on your local network.";
|
||||
|
||||
$script_path = __DIR__ . '/connexion.sh';
|
||||
$log_file = __DIR__ . '/logs/app.log';
|
||||
shell_exec("$script_path $SSID $PASS >> $log_file 2>&1 &");
|
||||
|
||||
#$output = shell_exec('sudo nmcli connection down Hotspot');
|
||||
#$output2 = shell_exec('sudo nmcli device wifi connect "AirLab" password "123plouf"');
|
||||
|
||||
}
|
||||
|
||||
|
||||
if ($type == "wifi_scan") {
|
||||
|
||||
// Set the path to your CSV file
|
||||
$csvFile = '/var/www/nebuleair_pro_4g/wifi_list.csv';
|
||||
|
||||
// Initialize an array to hold the JSON data
|
||||
$jsonData = [];
|
||||
|
||||
// Open the CSV file for reading
|
||||
if (($handle = fopen($csvFile, 'r')) !== false) {
|
||||
// Get the headers from the first row
|
||||
$headers = fgetcsv($handle);
|
||||
|
||||
// Loop through the rest of the rows
|
||||
while (($row = fgetcsv($handle)) !== false) {
|
||||
// Combine headers with row data to create an associative array
|
||||
$jsonData[] = array_combine($headers, $row);
|
||||
}
|
||||
|
||||
// Close the file handle
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
// Set the content type to JSON
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Convert the array to JSON format and output it
|
||||
echo json_encode($jsonData, JSON_PRETTY_PRINT);
|
||||
|
||||
|
||||
}
|
||||
|
||||
if ($type == "wifi_scan_old") {
|
||||
|
||||
$output = shell_exec('nmcli device wifi list ifname wlan0');
|
||||
// Split the output into lines
|
||||
$lines = explode("\n", trim($output));
|
||||
|
||||
// Initialize an array to hold the results
|
||||
$wifiNetworks = [];
|
||||
|
||||
// Loop through each line and extract the relevant information
|
||||
foreach ($lines as $index => $line) {
|
||||
// Skip the header line
|
||||
if ($index === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Split the line by whitespace and filter empty values
|
||||
$columns = preg_split('/\s+/', $line);
|
||||
|
||||
// If the line has enough columns, store the relevant data
|
||||
if (count($columns) >= 5) {
|
||||
$wifiNetworks[] = [
|
||||
'SSID' => $columns[2], // Network name
|
||||
'BARS' => $columns[8],
|
||||
'SIGNAL' => $columns[7], // Signal strength
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
$json_data = json_encode($wifiNetworks);
|
||||
|
||||
echo $json_data;
|
||||
|
||||
}
|
||||
168
html/logs.html
Executable file
168
html/logs.html
Executable file
@@ -0,0 +1,168 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Responsive Sidebar Template</title>
|
||||
<link rel="stylesheet" href="assets/css/bootstrap.min.css">
|
||||
<style>
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
#sidebar a.nav-link {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
#sidebar a.nav-link:hover {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
#sidebar a.nav-link svg {
|
||||
margin-right: 8px; /* Add spacing between icons and text */
|
||||
}
|
||||
#sidebar {
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
.offcanvas-backdrop {
|
||||
z-index: 1040;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Topbar -->
|
||||
<span id="topbar"></span>
|
||||
|
||||
<!-- Sidebar Offcanvas for Mobile -->
|
||||
<div class="offcanvas offcanvas-start text-white bg-dark" tabindex="-1" id="sidebarOffcanvas" aria-labelledby="sidebarOffcanvasLabel">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title" id="sidebarOffcanvasLabel">NebuleAir</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body" id="sidebar_mobile">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid mt-5">
|
||||
<div class="row">
|
||||
<aside class="col-md-2 col-lg-1 d-none d-md-block vh-100 position-fixed bg-dark text-white" id="sidebar">
|
||||
</aside>
|
||||
<!-- Main content -->
|
||||
<main class="col-md-10 ms-sm-auto col-lg-11 offset-md-2 offset-lg-1 px-md-4">
|
||||
<h1 class="mt-4">Le journal</h1>
|
||||
<p>Le journal des logs permet de savoir si les processus du capteur se déroulent correctement.</p>
|
||||
<div class="row">
|
||||
<!-- card 1 -->
|
||||
<div class="col-lg-6 col-12">
|
||||
<div class="card" style="height: 80vh;">
|
||||
<div class="card-header">
|
||||
Loop logs
|
||||
</div>
|
||||
<div class="card-body overflow-auto" id="card_loop_content">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- card 2 -->
|
||||
<div class="col-lg-6 col-12">
|
||||
<div class="card" style="height: 80vh;">
|
||||
<div class="card-header">
|
||||
Boot logs
|
||||
</div>
|
||||
<div class="card-body overflow-auto" id="card_boot_content">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JAVASCRIPT -->
|
||||
|
||||
<!-- Link Ajax locally -->
|
||||
<script src="assets/jquery/jquery-3.7.1.min.js"></script>
|
||||
<!-- Link Bootstrap JS and Popper.js locally -->
|
||||
<script src="assets/js/bootstrap.bundle.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const elementsToLoad = [
|
||||
{ id: 'topbar', file: 'topbar.html' },
|
||||
{ id: 'sidebar', file: 'sidebar.html' },
|
||||
{ id: 'sidebar_mobile', file: 'sidebar.html' }
|
||||
];
|
||||
|
||||
elementsToLoad.forEach(({ id, file }) => {
|
||||
fetch(file)
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.innerHTML = data;
|
||||
}
|
||||
})
|
||||
.catch(error => console.error(`Error loading ${file}:`, error));
|
||||
});
|
||||
|
||||
const loop_card_content = document.getElementById('card_loop_content');
|
||||
const boot_card_content = document.getElementById('card_boot_content');
|
||||
|
||||
fetch('../logs/loop.log')
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch the log file.');
|
||||
}
|
||||
return response.text();
|
||||
})
|
||||
.then((data) => {
|
||||
const lines = data.split('\n');
|
||||
|
||||
// Format log content
|
||||
const formattedLog = lines
|
||||
.map((line) => line.trim()) // Remove extra whitespace
|
||||
.filter((line) => line) // Remove empty lines
|
||||
.join('<br>'); // Join formatted lines with line breaks
|
||||
|
||||
loop_card_content.innerHTML = `<pre style="white-space: pre-wrap; word-wrap: break-word; margin: 0;">${formattedLog}</pre>`;
|
||||
loop_card_content.scrollTop = loop_card_content.scrollHeight; // Scroll to the bottom
|
||||
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
loop_card_content.textContent = 'Error loading log file.';
|
||||
});
|
||||
|
||||
fetch('../logs/app.log')
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch the log file.');
|
||||
}
|
||||
return response.text();
|
||||
})
|
||||
.then((data) => {
|
||||
const lines = data.split('\n');
|
||||
|
||||
// Format log content
|
||||
const formattedLog = lines
|
||||
.map((line) => line.trim()) // Remove extra whitespace
|
||||
.filter((line) => line) // Remove empty lines
|
||||
.join('<br>'); // Join formatted lines with line breaks
|
||||
|
||||
boot_card_content.innerHTML = `<pre style="white-space: pre-wrap; word-wrap: break-word; margin: 0;">${formattedLog}</pre>`;
|
||||
boot_card_content.scrollTop = loop_card_content.scrollHeight; // Scroll to the bottom
|
||||
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
boot_card_content.textContent = 'Error loading log file.';
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
628
html/saraR4.html
Executable file
628
html/saraR4.html
Executable file
@@ -0,0 +1,628 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Responsive Sidebar Template</title>
|
||||
<link rel="stylesheet" href="assets/css/bootstrap.min.css">
|
||||
<style>
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
#sidebar a.nav-link {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
#sidebar a.nav-link:hover {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
#sidebar a.nav-link svg {
|
||||
margin-right: 8px; /* Add spacing between icons and text */
|
||||
}
|
||||
#sidebar {
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
.offcanvas-backdrop {
|
||||
z-index: 1040;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Topbar -->
|
||||
<span id="topbar"></span>
|
||||
|
||||
<!-- Sidebar Offcanvas for Mobile -->
|
||||
<div class="offcanvas offcanvas-start text-white bg-dark" tabindex="-1" id="sidebarOffcanvas" aria-labelledby="sidebarOffcanvasLabel">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title" id="sidebarOffcanvasLabel">NebuleAir</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body" id="sidebar_mobile">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid mt-5">
|
||||
<div class="row">
|
||||
<aside class="col-md-2 col-lg-1 d-none d-md-block vh-100 position-fixed bg-dark text-white" id="sidebar">
|
||||
</aside>
|
||||
<!-- Main content -->
|
||||
<main class="col-md-10 ms-sm-auto col-lg-11 offset-md-2 offset-lg-1 px-md-4">
|
||||
<h1 class="mt-4">Modem 4G</h1>
|
||||
<p>Votre capteur est équipé d'un modem 4G et d'une carte SIM afin d'envoyer les mesures sur internet.</p>
|
||||
<h3>
|
||||
Status
|
||||
<span id="modem-status" class="badge">Loading...</span>
|
||||
</h3>
|
||||
<div class="row mb-3">
|
||||
|
||||
<div class="col-sm-3">
|
||||
<div class="card">
|
||||
|
||||
<div class="card-body">
|
||||
<p class="card-text">General information. </p>
|
||||
<button class="btn btn-primary" onclick="getData_saraR4('ttyAMA2', 'ATI', 2)">Get Data</button>
|
||||
<div id="loading_ttyAMA2_ATI" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<div id="response_ttyAMA2_ATI"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-3">
|
||||
<div class="card">
|
||||
|
||||
<div class="card-body">
|
||||
<p class="card-text">SIM card information.</p>
|
||||
<button class="btn btn-primary" onclick="getData_saraR4('ttyAMA2', 'AT+CCID?', 2)">Get Data</button>
|
||||
<div id="loading_ttyAMA2_AT_CCID_" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<div id="response_ttyAMA2_AT_CCID_"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-3">
|
||||
<div class="card">
|
||||
|
||||
<div class="card-body">
|
||||
<p class="card-text">Actual Network connection</p>
|
||||
<button class="btn btn-primary" onclick="getData_saraR4('ttyAMA2', 'AT+COPS?', 2)">Get Data</button>
|
||||
<div id="loading_ttyAMA2_AT_COPS_" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<div id="response_ttyAMA2_AT_COPS_"></div>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p class="card-text">Signal strength </p>
|
||||
<button class="btn btn-primary" onclick="getData_saraR4('ttyAMA2', 'AT+CSQ', 2)">Get Data</button>
|
||||
<div id="loading_ttyAMA2_AT_CSQ" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<div id="response_ttyAMA2_AT_CSQ"></div>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<h3>Connexion 4G Network</h3>
|
||||
<div class="row mb-3">
|
||||
|
||||
<div class="col-sm-6">
|
||||
<div class="card text-dark bg-light">
|
||||
|
||||
<div class="card-body">
|
||||
<p class="card-text">Network scan. Attention: 2 min scan.</p>
|
||||
<button class="btn btn-primary" onclick="getData_saraR4('ttyAMA2', 'AT+COPS=?', 120)">Scan</button>
|
||||
<div id="loading_ttyAMA2_AT_COPS__" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<div id="response_ttyAMA2_AT_COPS__"></div>
|
||||
<div id="table-network"></div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-3">
|
||||
<div class="card text-dark bg-light">
|
||||
|
||||
<div class="card-body">
|
||||
<p class="card-text">Network connexion.</p>
|
||||
<div class="input-group input-group-sm mb-3">
|
||||
<span class="input-group-text" id="inputGroup-sizing-sm">Numeric Operator</span>
|
||||
<input type="text" id="messageInput_network" class="form-control" aria-label="Sizing example input" aria-describedby="inputGroup-sizing-sm">
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="connectNetwork_saraR4('ttyAMA2', document.getElementById('messageInput_network').value, 60)">Connect</button>
|
||||
<div id="loading_ttyAMA2_AT_COPS_Connect" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<div id="response_ttyAMA2_AT_COPS_Connect"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-3">
|
||||
<div class="card text-dark bg-light">
|
||||
<div class="card-body">
|
||||
<p class="card-text">APN</p>
|
||||
<div class="input-group input-group-sm mb-3">
|
||||
<span class="input-group-text" id="inputGroup-sizing-sm">Address</span>
|
||||
<input type="text" id="messageInput_APN" class="form-control" aria-label="Sizing example input" aria-describedby="inputGroup-sizing-sm">
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="connectAPN_saraR4('ttyAMA2', document.getElementById('messageInput_APN').value, 5)">Connect</button>
|
||||
<button class="btn btn-secondary" onclick="getData_saraR4('ttyAMA2','AT+CGDCONT?', 5)">Get APN</button>
|
||||
|
||||
<div id="loading_ttyAMA2_APN" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<div id="response_ttyAMA2_APN"></div>
|
||||
|
||||
<div id="loading_ttyAMA2_AT_CGDCONT_" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<div id="response_ttyAMA2_AT_CGDCONT_"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<h3>MQTT</h3>
|
||||
<div class="row mb-3">
|
||||
<!-- Get CONFIG -->
|
||||
<div class="col-sm-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p class="card-text">Get config.</p>
|
||||
<button class="btn btn-primary" onclick="mqtt_getConfig_saraR4('ttyAMA2', 2)">Get Data</button>
|
||||
<div id="loading_mqtt_getConfig" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<div id="response_mqtt_getConfig"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MQTT LOGIN -->
|
||||
|
||||
<div class="col-sm-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p class="card-text">Login / logout</p>
|
||||
|
||||
<button class="btn btn-success" onclick="mqtt_login_logout('ttyAMA2', 1, 6)">Login </button>
|
||||
<button class="btn btn-danger" onclick="mqtt_login_logout('ttyAMA2', 0, 6)">Logout </button>
|
||||
<div id="loading_mqtt_login_logout" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<div id="response_mqtt_login_logout"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Send MESSAGE -->
|
||||
|
||||
<div class="col-sm-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p class="card-text">Send message (MQTT publish) .</p>
|
||||
<div class="input-group input-group-sm mb-3">
|
||||
<span class="input-group-text" id="inputGroup-sizing-sm">Text</span>
|
||||
<input type="text" id="MQTTmessageInput" class="form-control" aria-label="Sizing example input" aria-describedby="inputGroup-sizing-sm">
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="mqtt_publish('ttyAMA2', document.getElementById('MQTTmessageInput').value, 2)">Send Message</button>
|
||||
<div id="loading_mqtt_publish" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<div id="response_mqtt_publish"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Send message (test)</h3>
|
||||
<div class="row mb-3">
|
||||
<!-- SET URL -->
|
||||
|
||||
<div class="col-sm-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p class="card-text">Set url (HTTP).</p>
|
||||
<button class="btn btn-primary" onclick="setURL_saraR4('ttyAMA2', 'data.nebuleair.fr')">Set URL</button>
|
||||
<div id="loading_ttyAMA2_setURL" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<div id="response_ttyAMA2_setURL"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- WRITE MESSAGE to memory -->
|
||||
|
||||
<div class="col-sm-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p class="card-text">Write message (local storage).</p>
|
||||
<div class="input-group input-group-sm mb-3">
|
||||
<span class="input-group-text" id="inputGroup-sizing-sm">Text</span>
|
||||
<input type="text" id="messageInput" class="form-control" aria-label="Sizing example input" aria-describedby="inputGroup-sizing-sm">
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="writeMessage_saraR4('ttyAMA2', document.getElementById('messageInput').value , 'write')">Write </button>
|
||||
<button class="btn btn-warning" onclick="writeMessage_saraR4('ttyAMA2', 'Hello', 'read')">Read </button>
|
||||
<button class="btn btn-danger" onclick="writeMessage_saraR4('ttyAMA2', 'Hello', 'erase')">Empty </button>
|
||||
<div id="loading_ttyAMA2_message_write" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<div id="response_ttyAMA2_message_write"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Send MESSAGE -->
|
||||
|
||||
<div class="col-sm-4">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p class="card-text">Send message .</p>
|
||||
<button class="btn btn-primary" onclick="sendMessage_saraR4('ttyAMA2', '/pro_4G/notif_message.php')">Send Message</button>
|
||||
<div id="loading_ttyAMA2_message_send" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<div id="response_ttyAMA2_message_send"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JAVASCRIPT -->
|
||||
|
||||
<!-- Link Ajax locally -->
|
||||
<script src="assets/jquery/jquery-3.7.1.min.js"></script>
|
||||
<!-- Link Bootstrap JS and Popper.js locally -->
|
||||
<script src="assets/js/bootstrap.bundle.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const elementsToLoad = [
|
||||
{ id: 'topbar', file: 'topbar.html' },
|
||||
{ id: 'sidebar', file: 'sidebar.html' },
|
||||
{ id: 'sidebar_mobile', file: 'sidebar.html' }
|
||||
];
|
||||
|
||||
elementsToLoad.forEach(({ id, file }) => {
|
||||
fetch(file)
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.innerHTML = data;
|
||||
}
|
||||
})
|
||||
.catch(error => console.error(`Error loading ${file}:`, error));
|
||||
});
|
||||
});
|
||||
|
||||
function getData_saraR4(port, command, timeout){
|
||||
console.log("Data from SaraR4");
|
||||
console.log("Port: " + port );
|
||||
console.log("Command: " + command );
|
||||
console.log("Timeout: " + timeout );
|
||||
|
||||
const safeCommand = command.replace(/[?+=]/g, "_");
|
||||
console.log(safeCommand);
|
||||
|
||||
$("#loading_"+port+"_"+safeCommand).show();
|
||||
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=sara&port='+port+'&command='+encodeURIComponent(command)+'&timeout='+timeout,
|
||||
//dataType: 'json', // Specify that you expect a JSON response
|
||||
method: 'GET', // Use GET or POST depending on your needs
|
||||
success: function(response) {
|
||||
//log response
|
||||
console.log(response);
|
||||
//hide spinning wheel
|
||||
$("#loading_"+port+"_"+safeCommand).hide();
|
||||
|
||||
// si on fait le scan de network on veut une liste des réseaux
|
||||
if (command == "AT+COPS=?") {
|
||||
// Extract data within parentheses
|
||||
const matches = response.match(/\(.*?\)/g); // Matches all `(...)` sections
|
||||
const container = document.getElementById('table-network'); // Bootstrap container
|
||||
// Check if matches exist
|
||||
if (matches) {
|
||||
const table = document.createElement('table');
|
||||
table.className = 'table table-striped'; // Add Bootstrap table styling
|
||||
const thead = document.createElement('thead');
|
||||
const tbody = document.createElement('tbody');
|
||||
|
||||
// Table header (you can customize this based on your data)
|
||||
const headerRow = document.createElement('tr');
|
||||
const header1 = document.createElement('th');
|
||||
header1.textContent = 'Status';
|
||||
const header2 = document.createElement('th');
|
||||
header2.textContent = 'Long oper';
|
||||
const header3 = document.createElement('th');
|
||||
header3.textContent = 'Short opeer';
|
||||
const header4 = document.createElement('th');
|
||||
header4.textContent = 'Numeric oper';
|
||||
const header5 = document.createElement('th');
|
||||
header5.textContent = 'AcT';
|
||||
headerRow.appendChild(header1);
|
||||
headerRow.appendChild(header2);
|
||||
headerRow.appendChild(header3);
|
||||
headerRow.appendChild(header4);
|
||||
headerRow.appendChild(header5);
|
||||
thead.appendChild(headerRow);
|
||||
table.appendChild(thead);
|
||||
|
||||
// Loop through each match and create a row in the table
|
||||
matches.forEach((item) => {
|
||||
// Skip empty sections
|
||||
if (item === "()") return;
|
||||
|
||||
const row = document.createElement('tr');
|
||||
const values = item.slice(1, -1).split(','); // Remove parentheses and split by commas
|
||||
|
||||
// Add table cells (td) for each value
|
||||
values.forEach((value) => {
|
||||
const cell = document.createElement('td');
|
||||
cell.textContent = value.trim(); // Remove extra spaces
|
||||
row.appendChild(cell);
|
||||
});
|
||||
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
|
||||
// Add tbody to table and append the table to the container
|
||||
table.appendChild(tbody);
|
||||
container.appendChild(table);
|
||||
} else {
|
||||
console.error('No valid data found in response.');
|
||||
}
|
||||
} else{
|
||||
// si c'est une commande AT normale
|
||||
// Replace newline characters with <br> tags
|
||||
const formattedResponse = response.replace(/\n/g, "<br>");
|
||||
$("#response_"+port+"_"+safeCommand).html(formattedResponse);
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX request failed:', status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function connectNetwork_saraR4(port, networkID, timeout){
|
||||
console.log(" Connect to network (port "+port+" and network id "+networkID+"):");
|
||||
$("#loading_"+port+"_AT_COPS_Connect").show();
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=sara_connectNetwork&port='+port+'&networkID='+encodeURIComponent(networkID)+'&timeout='+timeout,
|
||||
//dataType: 'json', // Specify that you expect a JSON response
|
||||
method: 'GET', // Use GET or POST depending on your needs
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
$("#loading_"+port+"_AT_COPS_Connect").hide();
|
||||
// Replace newline characters with <br> tags
|
||||
const formattedResponse = response.replace(/\n/g, "<br>");
|
||||
$("#response_"+port+"_AT_COPS_Connect").html(formattedResponse);
|
||||
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX request failed:', status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function mqtt_getConfig_saraR4(port, timeout){
|
||||
console.log("GET MQTT config (port "+port+"):");
|
||||
$("#loading_mqtt_getConfig").show();
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=sara_getMQTT_config&port='+port+'&timeout='+timeout,
|
||||
//dataType: 'json', // Specify that you expect a JSON response
|
||||
method: 'GET', // Use GET or POST depending on your needs
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
$("#loading_mqtt_getConfig").hide();
|
||||
// Replace newline characters with <br> tags
|
||||
const formattedResponse = response.replace(/\n/g, "<br>");
|
||||
$("#response_mqtt_getConfig").html(formattedResponse);
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX request failed:', status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function mqtt_login_logout(port, login_logout, timeout){
|
||||
console.log("GET MQTT login / logout (port "+port+"):");
|
||||
$("#loading_mqtt_login_logout").show();
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=sara_getMQTT_login_logout&port='+port+'&login_logout='+login_logout+'&timeout='+timeout,
|
||||
//dataType: 'json', // Specify that you expect a JSON response
|
||||
method: 'GET', // Use GET or POST depending on your needs
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
$("#loading_mqtt_login_logout").hide();
|
||||
const formattedResponse = response.replace(/\n/g, "<br>");
|
||||
$("#response_mqtt_login_logout").html(formattedResponse);
|
||||
|
||||
const regex = /^\+UMQTTC:\s*(\d+),(\d+)/m; // Match "+UMQTTC:", followed by two numbers separated by a comma
|
||||
const match = response.match(regex);
|
||||
|
||||
if (match) {
|
||||
const firstNumber = match[1]; // The first number after ":"
|
||||
const secondNumber = match[2]; // The second number after the ","
|
||||
if (firstNumber == 0) {
|
||||
console.log("MQTT LOGOUT:");
|
||||
$("#response_mqtt_login_logout").append("<p>logout</p>");
|
||||
}
|
||||
if (firstNumber == 1) {
|
||||
console.log("MQTT LOGIN:");
|
||||
$("#response_mqtt_login_logout").append("<p>login</p>");
|
||||
}
|
||||
if (secondNumber == 0) {
|
||||
console.log("ERROR");
|
||||
$("#response_mqtt_login_logout").append("<p>error</p>");
|
||||
}
|
||||
if (secondNumber == 1) {
|
||||
console.log("SUCCESS");
|
||||
$("#response_mqtt_login_logout").append("<p>success</p>");
|
||||
}
|
||||
|
||||
} else {
|
||||
console.log("No matching line found");
|
||||
}
|
||||
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX request failed:', status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function mqtt_publish(port, message, timeout){
|
||||
console.log(" MQTT publish (port "+port+"):");
|
||||
$("#loading_mqtt_publish").show();
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=sara_MQTT_publish&port='+port+'&timeout='+timeout+'&message='+message,
|
||||
//dataType: 'json', // Specify that you expect a JSON response
|
||||
method: 'GET', // Use GET or POST depending on your needs
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
$("#loading_mqtt_publish").hide();
|
||||
// Replace newline characters with <br> tags
|
||||
const formattedResponse = response.replace(/\n/g, "<br>");
|
||||
$("#response_mqtt_publish").html(formattedResponse);
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX request failed:', status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setURL_saraR4(port, url){
|
||||
console.log("Set URL for HTTP (port "+port+" and URL "+url+"):");
|
||||
$("#loading_"+port+"_setURL").show();
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=sara_setURL&port='+port+'&url='+encodeURIComponent(url),
|
||||
//dataType: 'json', // Specify that you expect a JSON response
|
||||
method: 'GET', // Use GET or POST depending on your needs
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
$("#loading_"+port+"_setURL").hide();
|
||||
// Replace newline characters with <br> tags
|
||||
const formattedResponse = response.replace(/\n/g, "<br>");
|
||||
$("#response_"+port+"_setURL").html(formattedResponse);
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX request failed:', status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function writeMessage_saraR4(port, message, type){
|
||||
console.log(type +" message to SARA R4 memory (port "+port+" and message "+message+"):");
|
||||
$("#loading_"+port+"_message_write").show();
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=sara_writeMessage&port='+port+'&message='+encodeURIComponent(message)+'&type2='+type,
|
||||
//dataType: 'json', // Specify that you expect a JSON response
|
||||
method: 'GET', // Use GET or POST depending on your needs
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
$("#loading_"+port+"_message_write").hide();
|
||||
// Replace newline characters with <br> tags
|
||||
const formattedResponse = response.replace(/\n/g, "<br>");
|
||||
$("#response_"+port+"_message_write").html(formattedResponse);
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX request failed:', status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sendMessage_saraR4(port, endpoint){
|
||||
console.log("Send message from SaraR4 (port "+port+" and endpoint "+endpoint+"):");
|
||||
|
||||
$("#loading_"+port+"_message_send").show();
|
||||
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=sara_sendMessage&port='+port+'&endpoint='+encodeURIComponent(endpoint),
|
||||
//dataType: 'json', // Specify that you expect a JSON response
|
||||
method: 'GET', // Use GET or POST depending on your needs
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
$("#loading_"+port+"_message_send").hide();
|
||||
// Replace newline characters with <br> tags
|
||||
const formattedResponse = response.replace(/\n/g, "<br>");
|
||||
$("#response_"+port+"_message_send").html(formattedResponse);
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX request failed:', status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function connectAPN_saraR4(port, APN_address, timeout){
|
||||
console.log(" Set APN (port "+port+" and adress "+APN_address+"):");
|
||||
$("#loading_"+port+"_APN").show();
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=sara_APN&port='+port+'&APN_address='+encodeURIComponent(APN_address)+'&timeout='+timeout,
|
||||
//dataType: 'json', // Specify that you expect a JSON response
|
||||
method: 'GET', // Use GET or POST depending on your needs
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
$("#loading_"+port+"_APN").hide();
|
||||
// Replace newline characters with <br> tags
|
||||
const formattedResponse = response.replace(/\n/g, "<br>");
|
||||
$("#response_"+port+"_APN").html(formattedResponse);
|
||||
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX request failed:', status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
window.onload = function() {
|
||||
fetch('../config.json') // Replace 'deviceID.txt' with 'config.json'
|
||||
.then(response => response.json()) // Parse response as JSON
|
||||
.then(data => {
|
||||
//get device ID
|
||||
const deviceID = data.deviceID.trim().toUpperCase();
|
||||
document.getElementById('pageTitle_plus_ID').innerText = 'token: ' + deviceID;
|
||||
|
||||
//get SARA_R4 connection status
|
||||
const SARA_statusElement = document.getElementById("modem-status");
|
||||
console.log("SARA R4 is: " + data.SARA_R4_network_status);
|
||||
|
||||
if (data.SARA_R4_network_status === "connected") {
|
||||
SARA_statusElement.textContent = "Connected";
|
||||
SARA_statusElement.className = "badge text-bg-success";
|
||||
} else if (data.SARA_R4_network_status === "disconnected") {
|
||||
SARA_statusElement.textContent = "Disconnected";
|
||||
SARA_statusElement.className = "badge text-bg-danger";
|
||||
} else {
|
||||
SARA_statusElement.textContent = "Unknown";
|
||||
SARA_statusElement.className = "badge text-bg-secondary";
|
||||
}
|
||||
|
||||
//get local RTC
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=RTC_time',
|
||||
dataType: 'text', // Specify that you expect a JSON response
|
||||
method: 'GET', // Use GET or POST depending on your needs
|
||||
success: function(response) {
|
||||
console.log("Local RTC: " + response);
|
||||
const RTC_Element = document.getElementById("RTC_time");
|
||||
RTC_Element.textContent = response;
|
||||
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX request failed:', status, error);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
.catch(error => console.error('Error loading config.json:', error));
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
370
html/sensors.html
Executable file
370
html/sensors.html
Executable file
@@ -0,0 +1,370 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Responsive Sidebar Template</title>
|
||||
<link rel="stylesheet" href="assets/css/bootstrap.min.css">
|
||||
<style>
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
#sidebar a.nav-link {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
#sidebar a.nav-link:hover {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
#sidebar a.nav-link svg {
|
||||
margin-right: 8px; /* Add spacing between icons and text */
|
||||
}
|
||||
#sidebar {
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
.offcanvas-backdrop {
|
||||
z-index: 1040;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Topbar -->
|
||||
<span id="topbar"></span>
|
||||
|
||||
<!-- Sidebar Offcanvas for Mobile -->
|
||||
<div class="offcanvas offcanvas-start text-white bg-dark" tabindex="-1" id="sidebarOffcanvas" aria-labelledby="sidebarOffcanvasLabel">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title" id="sidebarOffcanvasLabel">NebuleAir</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body" id="sidebar_mobile">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid mt-5">
|
||||
<div class="row">
|
||||
<aside class="col-md-2 col-lg-1 d-none d-md-block vh-100 position-fixed bg-dark text-white" id="sidebar">
|
||||
</aside>
|
||||
<!-- Main content -->
|
||||
<main class="col-md-10 ms-sm-auto col-lg-11 offset-md-2 offset-lg-1 px-md-4">
|
||||
<h1 class="mt-4">Les sondes de mesure</h1>
|
||||
<p>Votre capteur NebuleAir est équipé de une ou plusieurs sondes qui permettent de mesurer certaines variables environnementales. La mesure
|
||||
est automatique mais vous pouvez ici vous assurer de leur bon fonctionnement.
|
||||
</p>
|
||||
<div class="row mb-3" id="card-container"></div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JAVASCRIPT -->
|
||||
|
||||
<!-- Link Ajax locally -->
|
||||
<script src="assets/jquery/jquery-3.7.1.min.js"></script>
|
||||
<!-- Link Bootstrap JS and Popper.js locally -->
|
||||
<script src="assets/js/bootstrap.bundle.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const elementsToLoad = [
|
||||
{ id: 'topbar', file: 'topbar.html' },
|
||||
{ id: 'sidebar', file: 'sidebar.html' },
|
||||
{ id: 'sidebar_mobile', file: 'sidebar.html' }
|
||||
];
|
||||
|
||||
elementsToLoad.forEach(({ id, file }) => {
|
||||
fetch(file)
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.innerHTML = data;
|
||||
}
|
||||
})
|
||||
.catch(error => console.error(`Error loading ${file}:`, error));
|
||||
});
|
||||
});
|
||||
|
||||
function getNPM_values(port){
|
||||
console.log("Data from NPM (port "+port+"):");
|
||||
$("#loading_"+port).show();
|
||||
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=npm&port='+port,
|
||||
dataType: 'json', // Specify that you expect a JSON response
|
||||
method: 'GET', // Use GET or POST depending on your needs
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
const tableBody = document.getElementById("data-table-body_"+port);
|
||||
tableBody.innerHTML = "";
|
||||
|
||||
$("#loading_"+port).hide();
|
||||
// Create an array of the desired keys
|
||||
const keysToShow = ["PM1", "PM25", "PM10"];
|
||||
// Add only the specified elements to the table
|
||||
keysToShow.forEach(key => {
|
||||
if (response[key] !== undefined) { // Check if the key exists in the response
|
||||
const value = response[key];
|
||||
$("#data-table-body_"+port).append(`
|
||||
<tr>
|
||||
<td>${key}</td>
|
||||
<td>${value} µg/m³</td>
|
||||
</tr>
|
||||
`);
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX request failed:', status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getENVEA_values(port, name){
|
||||
console.log("Data from Envea "+ name+" (port "+port+"):");
|
||||
$("#loading_envea"+name).show();
|
||||
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=envea&port='+port+'&name='+name,
|
||||
dataType: 'json', // Specify that you expect a JSON response
|
||||
method: 'GET', // Use GET or POST depending on your needs
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
const tableBody = document.getElementById("data-table-body_envea"+name);
|
||||
tableBody.innerHTML = "";
|
||||
|
||||
$("#loading_envea"+name).hide();
|
||||
// Create an array of the desired keys
|
||||
// Create an array of the desired keys
|
||||
const keysToShow = [name];
|
||||
// Add only the specified elements to the table
|
||||
keysToShow.forEach(key => {
|
||||
if (response !== undefined) { // Check if the key exists in the response
|
||||
const value = response;
|
||||
$("#data-table-body_envea"+name).append(`
|
||||
<tr>
|
||||
<td>${key}</td>
|
||||
<td>${value} ppb</td>
|
||||
</tr>
|
||||
`);
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX request failed:', status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getNoise_values(){
|
||||
console.log("Data from I2C Noise Sensor:");
|
||||
$("#loading_noise").show();
|
||||
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=noise',
|
||||
method: 'GET', // Use GET or POST depending on your needs
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
const tableBody = document.getElementById("data-table-body_noise");
|
||||
tableBody.innerHTML = "";
|
||||
$("#loading_noise").hide();
|
||||
|
||||
// Create an array of the desired keys
|
||||
const keysToShow = ["Noise"];
|
||||
// Add only the specified elements to the table
|
||||
keysToShow.forEach(key => {
|
||||
if (response !== undefined) { // Check if the key exists in the response
|
||||
const value = response;
|
||||
$("#data-table-body_noise").append(`
|
||||
<tr>
|
||||
<td>${key}</td>
|
||||
<td>${value} DB</td>
|
||||
</tr>
|
||||
`);
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX request failed:', status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getBME280_values(){
|
||||
console.log("Data from I2C BME280:");
|
||||
$("#loading_BME280").show();
|
||||
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=BME280',
|
||||
method: 'GET', // Use GET or POST depending on your needs
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
|
||||
const tableBody = document.getElementById("data-table-body_BME280");
|
||||
tableBody.innerHTML = "";
|
||||
$("#loading_BME280").hide();
|
||||
|
||||
// Parse the JSON response
|
||||
const data = JSON.parse(response);
|
||||
const keysToShow = ["temp", "hum", "press"];
|
||||
|
||||
|
||||
// Add only the specified elements to the table
|
||||
keysToShow.forEach(key => {
|
||||
if (response !== undefined) { // Check if the key exists in the response
|
||||
const value = data[key];
|
||||
const unit = key === "temp" ? "°C"
|
||||
: key === "hum" ? "%"
|
||||
: key === "press" ? "hPa"
|
||||
: ""; // Add appropriate units
|
||||
|
||||
$("#data-table-body_BME280").append(`
|
||||
<tr>
|
||||
<td>${key.charAt(0).toUpperCase() + key.slice(1)}</td>
|
||||
<td>${value} ${unit}</td>
|
||||
</tr>
|
||||
`);
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX request failed:', status, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
window.onload = function() {
|
||||
fetch('../config.json') // Replace 'deviceID.txt' with 'config.json'
|
||||
.then(response => response.json()) // Parse response as JSON
|
||||
.then(data => {
|
||||
//get device ID
|
||||
const deviceID = data.deviceID.trim().toUpperCase();
|
||||
document.getElementById('pageTitle_plus_ID').innerText = 'token: ' + deviceID;
|
||||
|
||||
//get local RTC
|
||||
$.ajax({
|
||||
url: 'launcher.php?type=RTC_time',
|
||||
dataType: 'text', // Specify that you expect a JSON response
|
||||
method: 'GET', // Use GET or POST depending on your needs
|
||||
success: function(response) {
|
||||
console.log("Local RTC: " + response);
|
||||
const RTC_Element = document.getElementById("RTC_time");
|
||||
RTC_Element.textContent = response;
|
||||
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX request failed:', status, error);
|
||||
}
|
||||
});
|
||||
|
||||
const container = document.getElementById('card-container'); // Conteneur des cartes
|
||||
|
||||
//creates NPM cards
|
||||
const NPM_ports = data.NextPM_ports; // Récupère les ports
|
||||
NPM_ports.forEach((port, index) => {
|
||||
const cardHTML = `
|
||||
<div class="col-sm-3">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Port UART ${port.replace('ttyAMA', '')}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">NextPM ${String.fromCharCode(65 + index)}</h5>
|
||||
<p class="card-text">Capteur particules fines.</p>
|
||||
<button class="btn btn-primary" onclick="getNPM_values('${port}')">Get Data</button>
|
||||
<div id="loading_${port}" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<table class="table table-striped-columns">
|
||||
<tbody id="data-table-body_${port}"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
container.innerHTML += cardHTML; // Ajouter la carte au conteneur
|
||||
});
|
||||
|
||||
//creates ENVEA cards
|
||||
const ENVEA_sensors = data.envea_sondes.filter(sonde => sonde.connected); // Filter only connected sondes
|
||||
|
||||
ENVEA_sensors.forEach((sensor, index) => {
|
||||
const port = sensor.port; // Port from the sensor object
|
||||
const name = sensor.name; // Port from the sensor object
|
||||
|
||||
const cardHTML = `
|
||||
<div class="col-sm-3">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Port UART ${port.replace('ttyAMA', '')}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Sonde Envea ${name}</h5>
|
||||
<p class="card-text">Capteur gas.</p>
|
||||
<button class="btn btn-primary" onclick="getENVEA_values('${port}','${name}')">Get Data</button>
|
||||
<div id="loading_envea${name}" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<table class="table table-striped-columns">
|
||||
<tbody id="data-table-body_envea${name}"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
container.innerHTML += cardHTML; // Ajouter la carte au conteneur
|
||||
});
|
||||
|
||||
//creates i2c BME280 card
|
||||
if (data.i2c_BME) {
|
||||
const i2C_BME_HTML = `
|
||||
<div class="col-sm-3">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Port I2C
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">BME280 Temp/Hum sensor</h5>
|
||||
<p class="card-text">Capteur température et humidité sur le port I2C.</p>
|
||||
<button class="btn btn-primary mb-1" onclick="getBME280_values()">Get Data</button>
|
||||
<br>
|
||||
<div id="loading_BME280" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<table class="table table-striped-columns">
|
||||
<tbody id="data-table-body_BME280"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
container.innerHTML += i2C_BME_HTML; // Add the I2C card if condition is met
|
||||
}
|
||||
|
||||
//creates i2c sound card
|
||||
if (data.i2C_sound) {
|
||||
const i2C_HTML = `
|
||||
<div class="col-sm-3">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Port I2C
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Decibel Meter</h5>
|
||||
<p class="card-text">Capteur bruit sur le port I2C.</p>
|
||||
<button class="btn btn-primary mb-1" onclick="getNoise_values()">Get Data</button>
|
||||
<br>
|
||||
<button class="btn btn-success" onclick="startNoise()">Start recording</button>
|
||||
<button class="btn btn-danger" onclick="stopNoise()">Stop recording</button>
|
||||
<div id="loading_noise" class="spinner-border spinner-border-sm" style="display: none;" role="status"></div>
|
||||
<table class="table table-striped-columns">
|
||||
<tbody id="data-table-body_noise"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
container.innerHTML += i2C_HTML; // Add the I2C card if condition is met
|
||||
}
|
||||
|
||||
})
|
||||
.catch(error => console.error('Error loading config.json:', error));
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
44
html/sidebar.html
Executable file
44
html/sidebar.html
Executable file
@@ -0,0 +1,44 @@
|
||||
<!-- Sidebar -->
|
||||
<nav class="nav flex-column">
|
||||
<a class="nav-link text-white mt-4" href="index.html">
|
||||
<svg class="bi me-2" width="16" height="16" fill="currentColor" aria-hidden="true">
|
||||
<use xlink:href="assets/icons/bootstrap-icons.svg#house"></use>
|
||||
</svg>
|
||||
Home
|
||||
</a>
|
||||
<a class="nav-link text-white" href="sensors.html">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-thermometer-sun" viewBox="0 0 16 16">
|
||||
<path d="M5 12.5a1.5 1.5 0 1 1-2-1.415V2.5a.5.5 0 0 1 1 0v8.585A1.5 1.5 0 0 1 5 12.5"/>
|
||||
<path d="M1 2.5a2.5 2.5 0 0 1 5 0v7.55a3.5 3.5 0 1 1-5 0zM3.5 1A1.5 1.5 0 0 0 2 2.5v7.987l-.167.15a2.5 2.5 0 1 0 3.333 0L5 10.486V2.5A1.5 1.5 0 0 0 3.5 1m5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0v-1a.5.5 0 0 1 .5-.5m4.243 1.757a.5.5 0 0 1 0 .707l-.707.708a.5.5 0 1 1-.708-.708l.708-.707a.5.5 0 0 1 .707 0M8 5.5a.5.5 0 0 1 .5-.5 3 3 0 1 1 0 6 .5.5 0 0 1 0-1 2 2 0 0 0 0-4 .5.5 0 0 1-.5-.5M12.5 8a.5.5 0 0 1 .5-.5h1a.5.5 0 1 1 0 1h-1a.5.5 0 0 1-.5-.5m-1.172 2.828a.5.5 0 0 1 .708 0l.707.708a.5.5 0 0 1-.707.707l-.708-.707a.5.5 0 0 1 0-.708M8.5 12a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0v-1a.5.5 0 0 1 .5-.5"/>
|
||||
</svg>
|
||||
Capteurs
|
||||
</a>
|
||||
<a class="nav-link text-white" href="saraR4.html">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-reception-4" viewBox="0 0 16 16">
|
||||
<path d="M0 11.5a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5zm4-3a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5zm4-3a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5zm4-3a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v11a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
||||
Modem 4G
|
||||
</a>
|
||||
<a class="nav-link text-white" href="wifi.html">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-wifi" viewBox="0 0 16 16">
|
||||
<path d="M15.384 6.115a.485.485 0 0 0-.047-.736A12.44 12.44 0 0 0 8 3C5.259 3 2.723 3.882.663 5.379a.485.485 0 0 0-.048.736.52.52 0 0 0 .668.05A11.45 11.45 0 0 1 8 4c2.507 0 4.827.802 6.716 2.164.205.148.49.13.668-.049"/>
|
||||
<path d="M13.229 8.271a.482.482 0 0 0-.063-.745A9.46 9.46 0 0 0 8 6c-1.905 0-3.68.56-5.166 1.526a.48.48 0 0 0-.063.745.525.525 0 0 0 .652.065A8.46 8.46 0 0 1 8 7a8.46 8.46 0 0 1 4.576 1.336c.206.132.48.108.653-.065m-2.183 2.183c.226-.226.185-.605-.1-.75A6.5 6.5 0 0 0 8 9c-1.06 0-2.062.254-2.946.704-.285.145-.326.524-.1.75l.015.015c.16.16.407.19.611.09A5.5 5.5 0 0 1 8 10c.868 0 1.69.201 2.42.56.203.1.45.07.61-.091zM9.06 12.44c.196-.196.198-.52-.04-.66A2 2 0 0 0 8 11.5a2 2 0 0 0-1.02.28c-.238.14-.236.464-.04.66l.706.706a.5.5 0 0 0 .707 0l.707-.707z"/>
|
||||
</svg>
|
||||
WIFI
|
||||
</a>
|
||||
<a class="nav-link text-white" href="logs.html">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-journal-code" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8.646 5.646a.5.5 0 0 1 .708 0l2 2a.5.5 0 0 1 0 .708l-2 2a.5.5 0 0 1-.708-.708L10.293 8 8.646 6.354a.5.5 0 0 1 0-.708m-1.292 0a.5.5 0 0 0-.708 0l-2 2a.5.5 0 0 0 0 .708l2 2a.5.5 0 0 0 .708-.708L5.707 8l1.647-1.646a.5.5 0 0 0 0-.708"/>
|
||||
<path d="M3 0h10a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-1h1v1a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v1H1V2a2 2 0 0 1 2-2"/>
|
||||
<path d="M1 5v-.5a.5.5 0 0 1 1 0V5h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1zm0 3v-.5a.5.5 0 0 1 1 0V8h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1zm0 3v-.5a.5.5 0 0 1 1 0v.5h.5a.5.5 0 0 1 0 1h-2a.5.5 0 0 1 0-1z"/>
|
||||
</svg>
|
||||
Logs
|
||||
</a>
|
||||
<a class="nav-link text-white" href="admin.html">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-tools" viewBox="0 0 16 16">
|
||||
<path d="M1 0 0 1l2.2 3.081a1 1 0 0 0 .815.419h.07a1 1 0 0 1 .708.293l2.675 2.675-2.617 2.654A3.003 3.003 0 0 0 0 13a3 3 0 1 0 5.878-.851l2.654-2.617.968.968-.305.914a1 1 0 0 0 .242 1.023l3.27 3.27a.997.997 0 0 0 1.414 0l1.586-1.586a.997.997 0 0 0 0-1.414l-3.27-3.27a1 1 0 0 0-1.023-.242L10.5 9.5l-.96-.96 2.68-2.643A3.005 3.005 0 0 0 16 3q0-.405-.102-.777l-2.14 2.141L12 4l-.364-1.757L13.777.102a3 3 0 0 0-3.675 3.68L7.462 6.46 4.793 3.793a1 1 0 0 1-.293-.707v-.071a1 1 0 0 0-.419-.814zm9.646 10.646a.5.5 0 0 1 .708 0l2.914 2.915a.5.5 0 0 1-.707.707l-2.915-2.914a.5.5 0 0 1 0-.708M3 11l.471.242.529.026.287.445.445.287.026.529L5 13l-.242.471-.026.529-.445.287-.287.445-.529.026L3 15l-.471-.242L2 14.732l-.287-.445L1.268 14l-.026-.529L1 13l.242-.471.026-.529.445-.287.287-.445.529-.026z"/>
|
||||
</svg>
|
||||
Admin
|
||||
</a>
|
||||
|
||||
</nav>
|
||||
14
html/topbar.html
Executable file
14
html/topbar.html
Executable file
@@ -0,0 +1,14 @@
|
||||
<!-- Topbar -->
|
||||
<nav class="navbar navbar-dark fixed-top" style="background-color: #8d8d8f;" id="topbar">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">
|
||||
<img src="assets/img/LogoNebuleAir.png" alt="Logo" height="30" class="d-inline-block align-text-top">
|
||||
</a>
|
||||
<div class="d-flex">
|
||||
<button class="btn btn-outline-light d-md-none me-2" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarOffcanvas" aria-controls="sidebarOffcanvas" aria-label="Toggle Sidebar">☰</button>
|
||||
<!-- Texte centré au milieu -->
|
||||
<span id="pageTitle_plus_ID">Texte au milieu</span>
|
||||
<button type="button" class="btn btn-outline-light" id="RTC_time">loading...</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
88
html/wifi.html
Executable file
88
html/wifi.html
Executable file
@@ -0,0 +1,88 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Responsive Sidebar Template</title>
|
||||
<link rel="stylesheet" href="assets/css/bootstrap.min.css">
|
||||
<style>
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
#sidebar a.nav-link {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
#sidebar a.nav-link:hover {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
#sidebar a.nav-link svg {
|
||||
margin-right: 8px; /* Add spacing between icons and text */
|
||||
}
|
||||
#sidebar {
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
.offcanvas-backdrop {
|
||||
z-index: 1040;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Topbar -->
|
||||
<span id="topbar"></span>
|
||||
|
||||
<!-- Sidebar Offcanvas for Mobile -->
|
||||
<div class="offcanvas offcanvas-start text-white bg-dark" tabindex="-1" id="sidebarOffcanvas" aria-labelledby="sidebarOffcanvasLabel">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title" id="sidebarOffcanvasLabel">NebuleAir</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body" id="sidebar_mobile">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid mt-5">
|
||||
<div class="row">
|
||||
<aside class="col-md-2 col-lg-1 d-none d-md-block vh-100 position-fixed bg-dark text-white" id="sidebar">
|
||||
</aside>
|
||||
<!-- Main content -->
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 offset-md-3 offset-lg-2 px-md-4">
|
||||
<h1 class="mt-4">Connection WIFI</h1>
|
||||
<p>La connexion WIFI n'est pas obligatoire mais elle vous permet d'effectuer des mises à jour et d'activer le contrôle à distance.</p>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JAVASCRIPT -->
|
||||
|
||||
<!-- Link Ajax locally -->
|
||||
<script src="assets/jquery/jquery-3.7.1.min.js"></script>
|
||||
<!-- Link Bootstrap JS and Popper.js locally -->
|
||||
<script src="assets/js/bootstrap.bundle.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const elementsToLoad = [
|
||||
{ id: 'topbar', file: 'topbar.html' },
|
||||
{ id: 'sidebar', file: 'sidebar.html' },
|
||||
{ id: 'sidebar_mobile', file: 'sidebar.html' }
|
||||
];
|
||||
|
||||
elementsToLoad.forEach(({ id, file }) => {
|
||||
fetch(file)
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.innerHTML = data;
|
||||
}
|
||||
})
|
||||
.catch(error => console.error(`Error loading ${file}:`, error));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user