diff --git a/app/css/custom.css b/app/css/custom.css
new file mode 100644
index 00000000..3cc72d61
--- /dev/null
+++ b/app/css/custom.css
@@ -0,0 +1,110 @@
+.page-header {
+ margin: 20px 0 20px;
+}
+
+.page-header .logo {
+ margin-right: 5px;
+}
+
+.panel-primary {
+ border-color: #c61931;
+}
+
+.panel-primary>.panel-heading {
+ border-color: #d8224c;
+ background-color: #d8224c;
+}
+
+.panel-footer {
+ background-color: #f2f1f0;
+}
+
+.btn-primary.btn-outline {
+ color: #c61931;
+ border-color: #c61931;
+}
+
+.btn-primary:hover {
+ background-color: #c61931;
+ border-color: #c61931;
+}
+
+.info-item {
+ width: 140px;
+ float: left;
+}
+
+.webconsole {
+ width:100%;
+ height:100%;
+ border:1px solid;
+}
+
+#console {
+ height:500px;
+}
+
+.systemtabcontent {
+ height:100%;
+ min-height:500px;
+}
+
+.service-status-running,
+.service-status-stopped {
+ background-color: #fff;
+ color: #333;
+ text-transform: uppercase;
+ line-height: inherit;
+}
+.service-status-running:before,
+.service-status-stopped:before {
+ display: inline-block;
+ height: 16px;
+ width: 16px;
+ content: "\2022";
+ font-size: 3.5em;
+ color: green;
+ line-height: 16px;
+ vertical-align: bottom;
+ margin-right: 2px;
+}
+.service-status-stopped:before {
+ color: red;
+ animation: flash 1s linear infinite;
+}
+@keyframes flash {
+ 50% {
+ opacity: 0;
+ }
+}
+
+.logoutput {
+ width:100%;
+ height:300px;
+}
+
+pre.unstyled {
+ border-width: 0;
+ background-color: transparent;
+ padding: 0;
+}
+
+.dhcp-static-leases {
+ margin-top: 1em;
+ margin-bottom: 1em;
+}
+
+.dhcp-static-lease-row {
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+}
+
+.loading-spinner {
+ background: url("../../img/loading-spinner.gif") no-repeat scroll center center transparent;
+ min-height: 150px;
+ width: 100%;
+}
+
+.js-wifi-stations .panel-body {
+ min-height: 256px;
+}
diff --git a/app/css/hackernews.css b/app/css/hackernews.css
new file mode 100644
index 00000000..840658c8
--- /dev/null
+++ b/app/css/hackernews.css
@@ -0,0 +1,112 @@
+html * {
+ font-family: Verdana, Geneva, sans-serif;
+ font-size: 10pt;
+ color: #828282;
+}
+
+.nav>li>a {
+ font-size: 12pt;
+}
+
+a:focus, a:hover {
+ color: #666;
+}
+
+h4 {
+ font-size: 18px;
+}
+
+.panel {
+ border-radius: 1px;
+}
+
+.panel-primary {
+ border-color: #ff6600;
+}
+
+.panel-primary>.panel-heading {
+ border-color: #ff6600;
+ background-color: #ff6600;
+ color: #000;
+ font-size: 14pt;
+ font-weight: bold;
+}
+
+.panel-heading {
+ border-top-left-radius: 1px;
+ border-top-right-radius: 1px;
+}
+
+.panel-primary>.panel-heading .fa {
+ color: #fff;
+}
+
+.page-header {
+ font-size: 26pt;
+ margin: 10px 0 20px;
+}
+
+#wrapper,#page-wrapper,.panel-body,.nav>li>a,.navbar-default {
+ background-color: #f6f6ef;
+}
+
+.panel-footer {
+ background-color: #eee;
+}
+
+.btn-primary.btn-outline {
+ color: #c61931;
+ border-color: #c61931;
+}
+
+.btn-primary:hover {
+ background-color: #c61931;
+ border-color: #c61931;
+}
+
+.progress {
+ background-color: #eee;
+}
+
+.progress-bar {
+ color: #eee;
+}
+
+.info-item {
+ width: 140px;
+ float: left;
+}
+
+.logoutput {
+ width: 100%;
+ height: 300px;
+}
+
+.service-status-running,
+.service-status-stopped {
+ background-color: #fff;
+ color: #333;
+ text-transform: uppercase;
+ line-height: inherit;
+}
+.service-status-running:before,
+.service-status-stopped:before {
+ display: inline-block;
+ height: 18px;
+ width: 16px;
+ content: "\2022";
+ font-size: 1.5em;
+ color: green;
+ line-height: 16px;
+ vertical-align: bottom;
+ margin-right: 2px;
+}
+.service-status-stopped:before {
+ color: red;
+ animation: flash 1s linear infinite;
+}
+@keyframes flash {
+ 50% {
+ opacity: 0;
+ }
+}
diff --git a/app/css/terminal.css b/app/css/terminal.css
new file mode 100644
index 00000000..5eabd096
--- /dev/null
+++ b/app/css/terminal.css
@@ -0,0 +1,301 @@
+html * {
+ font-family: Courier New, Andale Mono, monospace;
+ font-size: 10pt;
+ color: #33ff00;
+}
+
+#page-wrapper {
+ padding: 0 20px;
+ border-left: 1px solid #33ff00;
+}
+
+.nav>li>a {
+ font-size: 10pt;
+}
+
+.nav>li>a:focus, .nav>li>a:hover {
+ background-color: #000;
+}
+
+.nav-tabs>li.active>a, .nav-tabs>li.active>a:focus, .nav-tabs>li.active>a:hover, .input-group-addon {
+ color: #33ff00;
+ cursor: default;
+ background-color: #000;
+ border: 1px solid #33ff00;
+ border-bottom-color: #33ff00;
+}
+
+.nav-tabs>li>a,.nav-tabs>li>a:hover {
+ border: 1px solid #33ff00;
+}
+
+.nav-tabs {
+ border-bottom: 1px solid #33ff00;
+}
+
+.navbar-default {
+ border-color: #33ff00;
+}
+
+.navbar-default .navbar-brand, .navbar-default .navbar-brand:hover {
+ color: #33ff00;
+}
+
+.navbar-default .navbar-toggle {
+ border-color: #33ff00;
+}
+
+.navbar-default .navbar-toggle .icon-bar {
+ background-color: #33ff00;
+}
+
+.navbar-default .navbar-toggle:focus, .navbar-default .navbar-toggle:hover {
+ background-color: #000;
+}
+
+.logo {
+ visibility: collapse;
+ width: 0px;
+}
+
+a:focus, a:hover {
+ color: #33ff00;
+}
+
+.panel-primary {
+ border-color: #33ff00;
+}
+
+.panel-primary>.panel-heading, .panel-default>.panel-heading {
+ border-color: #33ff00;
+ background-color: #33ff00;
+ color: #000;
+ font-size: 12pt;
+}
+
+.panel-primary>.panel-heading .fa {
+ color: #000;
+}
+
+.panel {
+ margin-bottom: 20px;
+ border: 1px solid #33ff00;
+ border-radius: 0px;
+ background-color: #000;
+}
+
+hr {
+ border-top: 1px solid #33ff00;
+}
+
+.page-header {
+ font-size: 24pt;
+ margin: 10px 0 20px;
+ border-bottom: 1px solid #33ff00;
+}
+
+#wrapper,#page-wrapper,.panel-body,.nav>li>a,.navbar-default {
+ background-color: #000;
+}
+
+.panel-footer {
+ background-color: #000;
+ border-top: 1px solid #33ff00;
+}
+
+.panel-primary>.panel-heading::before, .navbar-default::before {
+ content: " ";
+ display: block;
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06));
+ z-index: 2;
+ background-size: 100% 2px, 3px 100%;
+ pointer-events: none;
+}
+
+.sidebar ul li a.active,.sidebar ul li a.hover {
+ background-color: #000;
+}
+
+.sidebar ul li {
+ border-bottom: 1px solid #33ff00;
+}
+
+.btn-default.active, .btn-default.active:focus, .btn-default.active:hover {
+ color: #33ff00;
+ background-color: #000;
+}
+
+
+.btn-primary.btn-outline,.btn-warning,.btn-default,.btn-danger {
+ color: #33ff00;
+ border-color: #33ff00;
+}
+
+.btn-primary:hover,.btn-primary:focus,.btn-warning:hover,.btn-warning:focus,.btn-primary:active,.btn-default:hover,.btn-danger:hover,.btn-default:active,.btn-default:focus {
+ color: #33ff00;
+ background-color: #000;
+ border-color: #33ff00;
+}
+
+.btn-primary.btn-outline:hover,.btn-success, .btn-success.btn-outline:hover, .btn-info.btn-outline:hover, .btn-warning.btn-outline:hover, .btn-danger.btn-outline:hover {
+ color: #33ff00;
+}
+
+label.btn.btn-primary {
+ color: #33ff00;
+}
+
+label.btn.btn-primary.active, label.btn.btn-warning.active {
+ background-color: #33ff00;
+ border-color: #33ff00;
+ color: #000;
+}
+
+.label-warning {
+ background-color: #33ff00;
+}
+
+span.label.label-warning {
+ color: #000;
+}
+
+.btn.btn-primary {
+ border-color: #33ff00;
+}
+
+.table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table>tfoot>tr>th, .table>thead>tr>td, .table>thead>tr>th {
+ background-color: #000;
+ border-top: 1px solid #000;
+}
+
+.table>thead>tr>th {
+ vertical-align: bottom;
+ border-bottom: 1px solid #33ff00;
+}
+
+.btn-info, .btn-info:hover, .btn-info[disabled], .btn-danger.disabled, .btn-danger.disabled.active, .btn-danger.disabled.focus, .btn-danger.disabled:active, .btn-danger.disabled:focus, .btn-danger.disabled:hover, .btn-danger[disabled], .btn-danger[disabled].active, .btn-danger[disabled].focus, .btn-danger[disabled]:active, .btn-danger[disabled]:focus, .btn-danger[disabled]:hover, fieldset[disabled] .btn-danger, fieldset[disabled] .btn-danger.active, fieldset[disabled] .btn-danger.focus, fieldset[disabled] .btn-danger:active, fieldset[disabled] .btn-danger:focus, fieldset[disabled] .btn-danger:hover, .btn-info:hover {
+ background-color: #000;
+ border-color: #33ff00;
+ color: #33ff00;
+}
+
+.btn {
+ background-color: #000;
+ border-radius: 0px;
+}
+
+.alert {
+ border-radius: 0px;
+}
+
+.alert-success,.alert-warning,.alert-info,.alert-dismissable,.alert-danger {
+ color: #33ff00;
+ background-color: #000;
+ border-color: #33ff00;
+ border: 1px dashed;
+}
+
+.close {
+ font-size: 18px;
+ font-weight: normal;
+ text-shadow: 0 0px 0 #000;
+ opacity: 1;
+}
+
+.form-control, .form-control:focus {
+ color: #33ff00;
+ background-color: #000;
+ border: 1px solid #33ff00;
+ border-radius: 0px;
+ transition: unset;
+}
+
+input[type="text"]{
+ color: #33ff00 !important
+}
+
+.form-control::-webkit-input-placeholder { color: #33ff00; }
+.form-control:-moz-placeholder { color: #33ff00; }
+.form-control::-moz-placeholder { color: #33ff00; }
+.form-control:-ms-input-placeholder { color: #33ff00; }
+.form-control::-ms-input-placeholder { color: #33ff00; }
+
+.progress {
+ background-color: #000;
+ border-radius: 0px;
+}
+
+.progress-bar {
+ color: #000;
+}
+
+.progress-bar.progress-bar-info.progress-bar-striped.active {
+ background-color: #33ff00;
+}
+
+.info-item {
+ width: 140px;
+ float: left;
+}
+
+.logoutput {
+ width: 100%;
+ height: 300px;
+ background-color: #000;
+ border-color: #33ff00;
+}
+
+.webconsole {
+ width: 100%;
+ height: 100%;
+ border-color: #33ff00;
+ border-bottom: 1px solid;
+ border-left: 1px solid;
+ border-top: 0px;
+ border-right: 1px solid;
+}
+
+#console {
+ height: 500px;
+}
+
+.systemtabcontent {
+ height: 100%;
+ min-height: 500px;
+}
+
+tspan, rect {
+ fill: #33ff00;
+}
+
+.morris-hover.morris-default-style {
+ background: none;
+ border-radius: 0px;
+ border-color: #33ff00;
+ border: dashed 1px #33ff00;
+}
+
+.morris-hover-point {
+ color: #33ff00 !important;
+}
+
+path {
+ stroke: #33ff00;
+}
+
+span.service-status-running {
+ color: #000;
+ font-size: 10pt;
+ text-transform: uppercase;
+ line-height: inherit;
+}
+
+pre {
+ background-color: #000;
+ border: #000;
+}
diff --git a/app/css/timeline.css b/app/css/timeline.css
new file mode 100755
index 00000000..92161ebe
--- /dev/null
+++ b/app/css/timeline.css
@@ -0,0 +1,180 @@
+.timeline {
+ position: relative;
+ padding: 20px 0 20px;
+ list-style: none;
+}
+
+.timeline:before {
+ content: " ";
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 50%;
+ width: 3px;
+ margin-left: -1.5px;
+ background-color: #eeeeee;
+}
+
+.timeline > li {
+ position: relative;
+ margin-bottom: 20px;
+}
+
+.timeline > li:before,
+.timeline > li:after {
+ content: " ";
+ display: table;
+}
+
+.timeline > li:after {
+ clear: both;
+}
+
+.timeline > li:before,
+.timeline > li:after {
+ content: " ";
+ display: table;
+}
+
+.timeline > li:after {
+ clear: both;
+}
+
+.timeline > li > .timeline-panel {
+ float: left;
+ position: relative;
+ width: 46%;
+ padding: 20px;
+ border: 1px solid #d4d4d4;
+ border-radius: 2px;
+ -webkit-box-shadow: 0 1px 6px rgba(0,0,0,0.175);
+ box-shadow: 0 1px 6px rgba(0,0,0,0.175);
+}
+
+.timeline > li > .timeline-panel:before {
+ content: " ";
+ display: inline-block;
+ position: absolute;
+ top: 26px;
+ right: -15px;
+ border-top: 15px solid transparent;
+ border-right: 0 solid #ccc;
+ border-bottom: 15px solid transparent;
+ border-left: 15px solid #ccc;
+}
+
+.timeline > li > .timeline-panel:after {
+ content: " ";
+ display: inline-block;
+ position: absolute;
+ top: 27px;
+ right: -14px;
+ border-top: 14px solid transparent;
+ border-right: 0 solid #fff;
+ border-bottom: 14px solid transparent;
+ border-left: 14px solid #fff;
+}
+
+.timeline > li > .timeline-badge {
+ z-index: 100;
+ position: absolute;
+ top: 16px;
+ left: 50%;
+ width: 50px;
+ height: 50px;
+ margin-left: -25px;
+ border-radius: 50% 50% 50% 50%;
+ text-align: center;
+ font-size: 1.4em;
+ line-height: 50px;
+ color: #fff;
+ background-color: #999999;
+}
+
+.timeline > li.timeline-inverted > .timeline-panel {
+ float: right;
+}
+
+.timeline > li.timeline-inverted > .timeline-panel:before {
+ right: auto;
+ left: -15px;
+ border-right-width: 15px;
+ border-left-width: 0;
+}
+
+.timeline > li.timeline-inverted > .timeline-panel:after {
+ right: auto;
+ left: -14px;
+ border-right-width: 14px;
+ border-left-width: 0;
+}
+
+.timeline-badge.primary {
+ background-color: #2e6da4 !important;
+}
+
+.timeline-badge.success {
+ background-color: #3f903f !important;
+}
+
+.timeline-badge.warning {
+ background-color: #f0ad4e !important;
+}
+
+.timeline-badge.danger {
+ background-color: #d9534f !important;
+}
+
+.timeline-badge.info {
+ background-color: #5bc0de !important;
+}
+
+.timeline-title {
+ margin-top: 0;
+ color: inherit;
+}
+
+.timeline-body > p,
+.timeline-body > ul {
+ margin-bottom: 0;
+}
+
+.timeline-body > p + p {
+ margin-top: 5px;
+}
+
+@media(max-width:767px) {
+ ul.timeline:before {
+ left: 40px;
+ }
+
+ ul.timeline > li > .timeline-panel {
+ width: calc(100% - 90px);
+ width: -moz-calc(100% - 90px);
+ width: -webkit-calc(100% - 90px);
+ }
+
+ ul.timeline > li > .timeline-badge {
+ top: 16px;
+ left: 15px;
+ margin-left: 0;
+ }
+
+ ul.timeline > li > .timeline-panel {
+ float: right;
+ }
+
+ ul.timeline > li > .timeline-panel:before {
+ right: auto;
+ left: -15px;
+ border-right-width: 15px;
+ border-left-width: 0;
+ }
+
+ ul.timeline > li > .timeline-panel:after {
+ right: auto;
+ left: -14px;
+ border-right-width: 14px;
+ border-left-width: 0;
+ }
+}
\ No newline at end of file
diff --git a/app/icons/android-chrome-192x192.png b/app/icons/android-chrome-192x192.png
new file mode 100644
index 00000000..ccfa35f7
Binary files /dev/null and b/app/icons/android-chrome-192x192.png differ
diff --git a/app/icons/apple-touch-icon.png b/app/icons/apple-touch-icon.png
new file mode 100644
index 00000000..34fb57ae
Binary files /dev/null and b/app/icons/apple-touch-icon.png differ
diff --git a/app/icons/browserconfig.xml b/app/icons/browserconfig.xml
new file mode 100644
index 00000000..087d949b
--- /dev/null
+++ b/app/icons/browserconfig.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+ #b91d47
+
+
+
diff --git a/app/icons/favicon-16x16.png b/app/icons/favicon-16x16.png
new file mode 100644
index 00000000..25a09c4a
Binary files /dev/null and b/app/icons/favicon-16x16.png differ
diff --git a/app/icons/favicon-32x32.png b/app/icons/favicon-32x32.png
new file mode 100644
index 00000000..0925e9f8
Binary files /dev/null and b/app/icons/favicon-32x32.png differ
diff --git a/app/icons/favicon.png b/app/icons/favicon.png
new file mode 100644
index 00000000..6fcc22cc
Binary files /dev/null and b/app/icons/favicon.png differ
diff --git a/app/icons/mstile-150x150.png b/app/icons/mstile-150x150.png
new file mode 100644
index 00000000..96b9d4e6
Binary files /dev/null and b/app/icons/mstile-150x150.png differ
diff --git a/app/icons/safari-pinned-tab.svg b/app/icons/safari-pinned-tab.svg
new file mode 100644
index 00000000..f00af1b9
--- /dev/null
+++ b/app/icons/safari-pinned-tab.svg
@@ -0,0 +1,48 @@
+
+
+
diff --git a/app/icons/site.webmanifest b/app/icons/site.webmanifest
new file mode 100644
index 00000000..321ab7db
--- /dev/null
+++ b/app/icons/site.webmanifest
@@ -0,0 +1,14 @@
+{
+ "name": "RaspAP",
+ "short_name": "RaspAP",
+ "icons": [
+ {
+ "src": "/dist/icons/android-chrome-192x192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ }
+ ],
+ "theme_color": "#ffffff",
+ "background_color": "#ffffff",
+ "display": "standalone"
+}
diff --git a/app/js/bandwidthcharts.js b/app/js/bandwidthcharts.js
new file mode 100644
index 00000000..bfede760
--- /dev/null
+++ b/app/js/bandwidthcharts.js
@@ -0,0 +1,92 @@
+(function($, _t) {
+ "use strict";
+
+ /**
+ * Create a Morris.js barchart.
+ */
+ function CreateBarChart(placeholder, datasizeunits) {
+ var barchart = new Morris.Bar({
+ element: placeholder,
+ xkey: 'date',
+ ykeys: ['rx', 'tx'],
+ labels: [_t['receive']+' '+datasizeunits.toUpperCase(),
+ _t['send']+' '+datasizeunits.toUpperCase()]
+ });
+
+ return barchart;
+ }
+
+ /**
+ * Create a jquery bootstrap datatable.
+ */
+ function CreateDataTable(placeholder, timeunits) {
+ $("#"+placeholder).append('
');
+ }
+
+ /**
+ * Figure out which tab is selected and remove all existing charts and then
+ * construct the proper barchart.
+ */
+ function ShowBandwidthChartHandler(e) {
+ // Remove all morrisjs charts
+ $('#divChartBandwidthhourly').empty();
+ $('#divChartBandwidthdaily').empty();
+ $('#divChartBandwidthmonthly').empty();
+ // Remove all datatables
+ $('#divTableBandwidthhourly').empty();
+ $('#divTableBandwidthdaily').empty();
+ $('#divTableBandwidthmonthly').empty();
+ // Construct ajax uri for getting the proper data.
+ var timeunit = $('ul#tabbarBandwidth li.active a').attr('href').substr(1);
+ var uri = 'ajax/bandwidth/get_bandwidth.php?';
+ uri += 'inet=';
+ uri += encodeURIComponent($('#cbxInterface'+timeunit+' option:selected').text());
+ uri += '&tu=';
+ uri += encodeURIComponent(timeunit.substr(0, 1));
+ var datasizeunits = 'mb';
+ uri += '&dsu='+encodeURIComponent(datasizeunits);
+ // Init. chart
+ var barchart = CreateBarChart('divChartBandwidth'+timeunit, datasizeunits);
+ // Init. datatable html
+ var datatable = CreateDataTable('divTableBandwidth'+timeunit, timeunit);
+ // Get data for chart
+ $.ajax({
+ url: uri,
+ dataType: 'json',
+ beforeSend: function() {
+ $('#divLoaderBandwidth'+timeunit).removeClass('hidden');
+ }
+ }).done(function(jsondata) {
+ $('#divLoaderBandwidth'+timeunit).addClass('hidden');
+ barchart.setData(jsondata);
+ $('#tableBandwidth'+timeunit).DataTable({
+ 'searching': false,
+ 'paging': false,
+ 'data': jsondata,
+ 'order': [[ 0, 'ASC' ]],
+ 'columns': [
+ { 'data': 'date' },
+ { 'data': 'rx', "title": _t['receive']+' '+datasizeunits.toUpperCase() },
+ { 'data': 'tx', "title": _t['send']+' '+datasizeunits.toUpperCase() }]
+ });
+ }).fail(function(xhr, textStatus) {
+ if (window.console) {
+ console.error('server error');
+ } else {
+ alert("server error");
+ }
+ });
+ }
+
+ $(document).ready(function() {
+ $('#tabbarBandwidth a[data-toggle="tab"]').on('shown.bs.tab', ShowBandwidthChartHandler);
+ $('#cbxInterfacehourly').on('change', ShowBandwidthChartHandler);
+ $('#cbxInterfacedaily').on('change', ShowBandwidthChartHandler);
+ $('#cbxInterfacemonthly').on('change', ShowBandwidthChartHandler);
+ ShowBandwidthChartHandler();
+ });
+
+})(jQuery, t);
+
diff --git a/app/js/bandwidthcharts.min.js b/app/js/bandwidthcharts.min.js
new file mode 100644
index 00000000..400015dd
--- /dev/null
+++ b/app/js/bandwidthcharts.min.js
@@ -0,0 +1,7 @@
+/*!
+ * RaspAP - RaspAP WiFi Configuration Portal v1.6.1 (https://github.com/billz/raspap-webgui)
+ * Copyright 2013-2019 RaspAP Developers
+ * Licensed under MIT (https://github.com/raspap-webgui/raspap-webgui/blob/master/LICENSE)
+ */
+
+!function(r,i){"use strict";function t(t){r("#divChartBandwidthhourly").empty(),r("#divChartBandwidthdaily").empty(),r("#divChartBandwidthmonthly").empty(),r("#divTableBandwidthhourly").empty(),r("#divTableBandwidthdaily").empty(),r("#divTableBandwidthmonthly").empty();var e=r("ul#tabbarBandwidth li.active a").attr("href").substr(1),a="ajax/bandwidth/get_bandwidth.php?";a+="inet=",a+=encodeURIComponent(r("#cbxInterface"+e+" option:selected").text()),a+="&tu=",a+=encodeURIComponent(e.substr(0,1));var d="mb";a+="&dsu="+encodeURIComponent(d);var n=function(t,e){return new Morris.Bar({element:t,xkey:"date",ykeys:["rx","tx"],labels:[i.receive+" "+e.toUpperCase(),i.send+" "+e.toUpperCase()]})}("divChartBandwidth"+e,d);!function(t,e){r("#"+t).append('')}("divTableBandwidth"+e,e);r.ajax({url:a,dataType:"json",beforeSend:function(){r("#divLoaderBandwidth"+e).removeClass("hidden")}}).done(function(t){r("#divLoaderBandwidth"+e).addClass("hidden"),n.setData(t),r("#tableBandwidth"+e).DataTable({searching:!1,paging:!1,data:t,order:[[0,"ASC"]],columns:[{data:"date"},{data:"rx",title:i.receive+" "+d.toUpperCase()},{data:"tx",title:i.send+" "+d.toUpperCase()}]})}).fail(function(t,e){window.console?console.error("server error"):alert("server error")})}r(document).ready(function(){r('#tabbarBandwidth a[data-toggle="tab"]').on("shown.bs.tab",t),r("#cbxInterfacehourly").on("change",t),r("#cbxInterfacedaily").on("change",t),r("#cbxInterfacemonthly").on("change",t),t()})}(jQuery,t);
\ No newline at end of file
diff --git a/app/js/custom.js b/app/js/custom.js
new file mode 100644
index 00000000..d056ccdf
--- /dev/null
+++ b/app/js/custom.js
@@ -0,0 +1,291 @@
+function msgShow(retcode,msg) {
+ if(retcode == 0) {
+ var alertType = 'success';
+ } else if(retcode == 2 || retcode == 1) {
+ var alertType = 'danger';
+ }
+ var htmlMsg = ''+msg+'
';
+ return htmlMsg;
+}
+
+function createNetmaskAddr(bitCount) {
+ var mask=[];
+ for(i=0;i<4;i++) {
+ var n = Math.min(bitCount, 8);
+ mask.push(256 - Math.pow(2, 8-n));
+ bitCount -= n;
+ }
+ return mask.join('.');
+}
+
+function loadSummary(strInterface) {
+ $.post('/ajax/networking/get_ip_summary.php',{interface:strInterface},function(data){
+ jsonData = JSON.parse(data);
+ console.log(jsonData);
+ if(jsonData['return'] == 0) {
+ $('#'+strInterface+'-summary').html(jsonData['output'].join('
'));
+ } else if(jsonData['return'] == 2) {
+ $('#'+strInterface+'-summary').append(''+jsonData['output'].join('
')+'
');
+ }
+ });
+}
+
+function getAllInterfaces() {
+ $.get('/ajax/networking/get_all_interfaces.php',function(data){
+ jsonData = JSON.parse(data);
+ $.each(jsonData,function(ind,value){
+ loadSummary(value)
+ });
+ });
+}
+
+function setupTabs() {
+ $('a[data-toggle="tab"]').on('shown.bs.tab',function(e){
+ var target = $(e.target).attr('href');
+ if(!target.match('summary')) {
+ var int = target.replace("#","");
+ loadCurrentSettings(int);
+ }
+ });
+}
+
+function loadCurrentSettings(strInterface) {
+ $.post('/ajax/networking/get_int_config.php',{interface:strInterface},function(data){
+ jsonData = JSON.parse(data);
+ $.each(jsonData['output'],function(i,v) {
+ var int = v['interface'];
+ $.each(v,function(i2,v2) {
+ switch(i2) {
+ case "static":
+ if(v2 == 'true') {
+ $('#'+int+'-static').click();
+ $('#'+int+'-nofailover').click();
+ } else {
+ $('#'+int+'-dhcp').click();
+ }
+ break;
+ case "failover":
+ if(v2 === 'true') {
+ $('#'+int+'-failover').click();
+ } else {
+ $('#'+int+'-nofailover').click();
+ }
+ break;
+ case "ip_address":
+ var arrIPNetmask = v2.split('/');
+ $('#'+int+'-ipaddress').val(arrIPNetmask[0]);
+ $('#'+int+'-netmask').val(createNetmaskAddr(arrIPNetmask[1]));
+ break;
+ case "routers":
+ $('#'+int+'-gateway').val(v2);
+ break;
+ case "domain_name_server":
+ svrsDNS = v2.split(" ");
+ $('#'+int+'-dnssvr').val(svrsDNS[0]);
+ $('#'+int+'-dnssvralt').val(svrsDNS[1]);
+ break;
+ }
+ });
+ });
+ });
+}
+
+function saveNetworkSettings(int) {
+
+ var frmInt = $('#frm-'+int).find(':input');
+ var arrFormData = {};
+ $.each(frmInt,function(i3,v3){
+ if($(v3).attr('type') == 'radio') {
+ arrFormData[$(v3).attr('id')] = $(v3).prop('checked');
+ } else {
+ arrFormData[$(v3).attr('id')] = $(v3).val();
+ }
+ });
+ arrFormData['interface'] = int;
+ $.post('/ajax/networking/save_int_config.php',arrFormData,function(data){
+ //console.log(data);
+ var jsonData = JSON.parse(data);
+ $('#msgNetworking').html(msgShow(jsonData['return'],jsonData['output']));
+ });
+}
+
+function applyNetworkSettings() {
+ var int = $(this).data('int');
+ arrFormData = {};
+ arrFormData['generate'] = '';
+ $.post('/ajax/networking/gen_int_config.php',arrFormData,function(data){
+ console.log(data);
+ var jsonData = JSON.parse(data);
+ $('#msgNetworking').html(msgShow(jsonData['return'],jsonData['output']));
+ });
+}
+
+$(document).on("click", ".js-add-dhcp-static-lease", function(e) {
+ e.preventDefault();
+ var container = $(".js-new-dhcp-static-lease");
+ var mac = $("input[name=mac]", container).val().trim();
+ var ip = $("input[name=ip]", container).val().trim();
+ if (mac == "" || ip == "") {
+ return;
+ }
+
+ var row = $("#js-dhcp-static-lease-row").html()
+ .replace("{{ mac }}", mac)
+ .replace("{{ ip }}", ip);
+ $(".js-dhcp-static-lease-container").append(row);
+
+ $("input[name=mac]", container).val("");
+ $("input[name=ip]", container).val("");
+});
+
+$(document).on("click", ".js-remove-dhcp-static-lease", function(e) {
+ e.preventDefault();
+ $(this).parents(".js-dhcp-static-lease-row").remove();
+});
+
+$(document).on("submit", ".js-dhcp-settings-form", function(e) {
+ $(".js-add-dhcp-static-lease").trigger("click");
+});
+
+function setupBtns() {
+ $('#btnSummaryRefresh').click(function(){getAllInterfaces();});
+
+ $('.intsave').click(function(){
+ var int = $(this).data('int');
+ saveNetworkSettings(int);
+ });
+
+ $('.intapply').click(function(){
+ applyNetworkSettings();
+ });
+}
+
+function setCSRFTokenHeader(event, xhr, settings) {
+ var csrfToken = $('meta[name=csrf_token]').attr('content');
+ if (/^(POST|PATCH|PUT|DELETE)$/i.test(settings.type)) {
+ xhr.setRequestHeader("X-CSRF-Token", csrfToken);
+ }
+}
+
+function contentLoaded() {
+ pageCurrent = window.location.href.split("?")[1].split("=")[1];
+ pageCurrent = pageCurrent.replace("#","");
+ $('#side-menu').metisMenu();
+ switch(pageCurrent) {
+ case "network_conf":
+ getAllInterfaces();
+ setupTabs();
+ setupBtns();
+ break;
+ }
+}
+
+function loadWifiStations(refresh) {
+ return function() {
+ var complete = function() { $(this).removeClass('loading-spinner'); }
+ var qs = refresh === true ? '?refresh' : '';
+ $('.js-wifi-stations')
+ .addClass('loading-spinner')
+ .empty()
+ .load('/ajax/networking/wifi_stations.php'+qs, complete);
+ };
+}
+
+$(".js-reload-wifi-stations").on("click", loadWifiStations(true));
+
+$(document).on("click", ".js-toggle-password", function(e) {
+ var button = $(e.target)
+ var field = $(button.data("target"));
+ if (field.is(":input")) {
+ e.preventDefault();
+
+ if (!button.data("__toggle-with-initial")) {
+ button.data("__toggle-with-initial", button.text())
+ }
+
+ if (field.attr("type") === "password") {
+ button.text(button.data("toggle-with"));
+ field.attr("type", "text");
+ } else {
+ button.text(button.data("__toggle-with-initial"));
+ field.attr("type", "password");
+ }
+ }
+});
+
+$(document).on("keyup", ".js-validate-psk", function(e) {
+ var field = $(e.target);
+ var colors = field.data("colors").split(",");
+ var target = $(field.data("target"));
+ if (field.val().length < 8 || field.val().length > 63) {
+ field.css("backgroundColor", colors[0]);
+ target.attr("disabled", true);
+ } else {
+ field.css("backgroundColor", colors[1]);
+ target.attr("disabled", false);
+ }
+});
+
+$(function() {
+ $('#theme-select').change(function() {
+ var theme = themes[$( "#theme-select" ).val() ];
+ set_theme(theme);
+ });
+});
+
+function set_theme(theme) {
+ $('link[title="main"]').attr('href', 'app/css/' + theme);
+
+ // persist selected theme in cookie
+ setCookie('theme',theme,90);
+}
+
+function setCookie(cname, cvalue, exdays) {
+ var d = new Date();
+ d.setTime(d.getTime() + (exdays*24*60*60*1000));
+ var expires = "expires="+ d.toUTCString();
+ document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
+}
+
+var themes = {
+ "default": "custom.css",
+ "hackernews" : "hackernews.css",
+ "terminal" : "terminal.css",
+}
+
+//Loads the correct sidebar on window load,
+//collapses the sidebar on window resize.
+// Sets the min-height of #page-wrapper to window size
+$(function() {
+ $(window).bind("load resize", function() {
+ topOffset = 50;
+ width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width;
+ if (width < 768) {
+ $('div.navbar-collapse').addClass('collapse');
+ topOffset = 100; // 2-row-menu
+ } else {
+ $('div.navbar-collapse').removeClass('collapse');
+ }
+
+ height = ((this.window.innerHeight > 0) ? this.window.innerHeight : this.screen.height) - 1;
+ height = height - topOffset;
+ if (height < 1) height = 1;
+ if (height > topOffset) {
+ $("#page-wrapper").css("min-height", (height) + "px");
+ }
+ });
+
+ var url = window.location;
+ var element = $('ul.nav a').filter(function() {
+ return this.href == url || url.href.indexOf(this.href) == 0;
+ }).addClass('active').parent().parent().addClass('in').parent();
+ if (element.is('li')) {
+ element.addClass('active');
+ }
+});
+
+
+$(document)
+ .ajaxSend(setCSRFTokenHeader)
+ .ready(contentLoaded)
+ .ready(loadWifiStations());
diff --git a/app/js/custom.min.js b/app/js/custom.min.js
new file mode 100644
index 00000000..67444fb6
--- /dev/null
+++ b/app/js/custom.min.js
@@ -0,0 +1,7 @@
+/*!
+ * RaspAP - RaspAP WiFi Configuration Portal v1.6.1 (https://github.com/billz/raspap-webgui)
+ * Copyright 2013-2019 RaspAP Developers
+ * Licensed under MIT (https://github.com/raspap-webgui/raspap-webgui/blob/master/LICENSE)
+ */
+
+function msgShow(t,a){if(0==t)var e="success";else if(2==t||1==t)e="danger";return''+a+"
"}function createNetmaskAddr(t){var a=[];for(i=0;i<4;i++){var e=Math.min(t,8);a.push(256-Math.pow(2,8-e)),t-=e}return a.join(".")}function loadSummary(a){$.post("/ajax/networking/get_ip_summary.php",{interface:a},function(t){jsonData=JSON.parse(t),console.log(jsonData),0==jsonData.return?$("#"+a+"-summary").html(jsonData.output.join("
")):2==jsonData.return&&$("#"+a+"-summary").append(''+jsonData.output.join("
")+"
")})}function getAllInterfaces(){$.get("/ajax/networking/get_all_interfaces.php",function(t){jsonData=JSON.parse(t),$.each(jsonData,function(t,a){loadSummary(a)})})}function setupTabs(){$('a[data-toggle="tab"]').on("shown.bs.tab",function(t){var a=$(t.target).attr("href");a.match("summary")||loadCurrentSettings(a.replace("#",""))})}function loadCurrentSettings(t){$.post("/ajax/networking/get_int_config.php",{interface:t},function(t){jsonData=JSON.parse(t),$.each(jsonData.output,function(t,a){var n=a.interface;$.each(a,function(t,a){switch(t){case"static":"true"==a?($("#"+n+"-static").click(),$("#"+n+"-nofailover").click()):$("#"+n+"-dhcp").click();break;case"failover":"true"===a?$("#"+n+"-failover").click():$("#"+n+"-nofailover").click();break;case"ip_address":var e=a.split("/");$("#"+n+"-ipaddress").val(e[0]),$("#"+n+"-netmask").val(createNetmaskAddr(e[1]));break;case"routers":$("#"+n+"-gateway").val(a);break;case"domain_name_server":svrsDNS=a.split(" "),$("#"+n+"-dnssvr").val(svrsDNS[0]),$("#"+n+"-dnssvralt").val(svrsDNS[1])}})})})}function saveNetworkSettings(t){var a=$("#frm-"+t).find(":input"),e={};$.each(a,function(t,a){"radio"==$(a).attr("type")?e[$(a).attr("id")]=$(a).prop("checked"):e[$(a).attr("id")]=$(a).val()}),e.interface=t,$.post("/ajax/networking/save_int_config.php",e,function(t){var a=JSON.parse(t);$("#msgNetworking").html(msgShow(a.return,a.output))})}function applyNetworkSettings(){$(this).data("int");arrFormData={generate:""},$.post("/ajax/networking/gen_int_config.php",arrFormData,function(t){console.log(t);var a=JSON.parse(t);$("#msgNetworking").html(msgShow(a.return,a.output))})}function setupBtns(){$("#btnSummaryRefresh").click(function(){getAllInterfaces()}),$(".intsave").click(function(){saveNetworkSettings($(this).data("int"))}),$(".intapply").click(function(){applyNetworkSettings()})}function setCSRFTokenHeader(t,a,e){var n=$("meta[name=csrf_token]").attr("content");/^(POST|PATCH|PUT|DELETE)$/i.test(e.type)&&a.setRequestHeader("X-CSRF-Token",n)}function contentLoaded(){switch(pageCurrent=window.location.href.split("?")[1].split("=")[1],pageCurrent=pageCurrent.replace("#",""),$("#side-menu").metisMenu(),pageCurrent){case"network_conf":getAllInterfaces(),setupTabs(),setupBtns()}}function loadWifiStations(a){return function(){var t=!0===a?"?refresh":"";$(".js-wifi-stations").addClass("loading-spinner").empty().load("/ajax/networking/wifi_stations.php"+t,function(){$(this).removeClass("loading-spinner")})}}$(document).on("click",".js-add-dhcp-static-lease",function(t){t.preventDefault();var a=$(".js-new-dhcp-static-lease"),e=$("input[name=mac]",a).val().trim(),n=$("input[name=ip]",a).val().trim();if(""!=e&&""!=n){var i=$("#js-dhcp-static-lease-row").html().replace("{{ mac }}",e).replace("{{ ip }}",n);$(".js-dhcp-static-lease-container").append(i),$("input[name=mac]",a).val(""),$("input[name=ip]",a).val("")}}),$(document).on("click",".js-remove-dhcp-static-lease",function(t){t.preventDefault(),$(this).parents(".js-dhcp-static-lease-row").remove()}),$(document).on("submit",".js-dhcp-settings-form",function(t){$(".js-add-dhcp-static-lease").trigger("click")}),$(".js-reload-wifi-stations").on("click",loadWifiStations(!0)),$(document).on("click",".js-toggle-password",function(t){var a=$(t.target),e=$(a.data("target"));e.is(":input")&&(t.preventDefault(),a.data("__toggle-with-initial")||a.data("__toggle-with-initial",a.text()),"password"===e.attr("type")?(a.text(a.data("toggle-with")),e.attr("type","text")):(a.text(a.data("__toggle-with-initial")),e.attr("type","password")))}),$(document).on("keyup",".js-validate-psk",function(t){var a=$(t.target),e=a.data("colors").split(","),n=$(a.data("target"));a.val().length<8||63