diff --git a/README.md b/README.md
index fc97dab..82e9656 100644
--- a/README.md
+++ b/README.md
@@ -1,33 +1,43 @@
-# pi-hole-midnight
+# pi-hole-material-dark
Custom dark theme CSS for pi-hole to replace skin-blue AdminLTE theme.
Created for use with [pi-hole](https://github.com/pi-hole/pi-hole).
-[![Screenshot](https://i.imgur.com/UrmgpyN.png)](https://i.imgur.com/UrmgpyN.png)
-
----
+![Screenshot](dashboard.png)
+----------
## Install
-Type the following commands into SSH, line by line.
-```
-cd /var/www/html/admin/style/vendor/
-sudo git clone https://github.com/jacobbates/pi-hole-midnight.git
-sudo rm -f skin-blue.min.css
-sudo cp pi-hole-midnight/skin-blue.min.css .
-sudo rm -rf pi-hole-midnight
-```
-Don't forget the trailing " ." on the cp line (it means copy to current directory).
+1. open a terminal (or SSH if you're remoting in) ( Ctrl + Alt + T )
+2. Type: `cd /var/www/html/`
+3. Type: `sudo wget https://raw.githubusercontent.com/MBarrows20/pi-hole-material-dark/master/install_Material_Dark_Theme.sh`
+4. Type: `sudo chmod +x install_Material_Dark_Theme.sh`
+5. Type: `sudo ./install_Material_Dark_Theme.sh`
+6. Follow the onscreen instructions!
+
+If you get a 400 Bad Request after running step 3 above, it's likely that GitHub thinks your PiHole is a webcrawler. In such an event, run the following steps before continuing to step 4:
+1. Type: `sudo git clone https://github.com/MBarrows20/pi-hole-material-dark.git`
+2. Type: `sudo cp pi-hole-material-dark/install_Material_Dark_Theme.sh install_Material_Dark_Theme.sh`
+3. Type: `sudo rm -rf pi-hole-material-dark`
+
+---------
+## Update
+
+1. open a terminal (or SSH if you're remoting in) ( Ctrl + Alt + T )
+2. Type: `cd /var/www/html/`
+3. Type: `sudo ./install_Material_Dark_Theme.sh`
+4. Follow the onscreen instructions!
+
+----------
## Uninstall/Revert
Type the following commands into SSH, line by line.
```
-cd /var/www/html/admin/style/vendor/
+cd /var/www/html/admin
sudo git reset --hard
```
----
-
+----------
### License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
diff --git a/dashboard.png b/dashboard.png
new file mode 100644
index 0000000..ae581b9
Binary files /dev/null and b/dashboard.png differ
diff --git a/install_Material_Dark_Theme.sh b/install_Material_Dark_Theme.sh
new file mode 100644
index 0000000..32b34fb
--- /dev/null
+++ b/install_Material_Dark_Theme.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+while true; do
+ read -p "Create a backup of the current files? (Y/n): " yn
+ case $yn in
+ [Yy]* )
+ cp -f admin/style/vendor/skin-blue.min.css admin/style/vendor/skin-blue.min.bkp.css # Backup Skin StyleSheet...
+ cp -f admin/style/vendor/daterangepicker.css admin/style/vendor/daterangepicker.bkp.css # Backup Calendar StyleSheet...
+ cp -f admin/scripts/vendor/app.min.js admin/scripts/vendor/app.min.bkp.js # Backup app.js
+ cp -f admin/scripts/pi-hole/js/index.js admin/scripts/pi-hole/js/index.bkp.js # Backup index.js
+ echo "Backup completed! Backup files contain the .bkp suffix before their extension."
+ break ;;
+ [Nn]* ) break;;
+ * ) echo "Please answer Yes (y) or No (n).";;
+ esac
+done
+while true; do
+ read -p "Install MBarrows20's Material Dark Theme now? (Y/n): " yn
+ case $yn in
+ [Yy]* )
+ git clone https://github.com/mbarrows20/pi-hole-material-dark.git temp
+ rm -f admin/style/vendor/skin-blue.min.css
+ rm -f admin/style/vendor/daterangepicker.css
+ rm -f admin/scripts/vendor/app.min.js
+ rm -f admin/scripts/pi-hole/js/index.js
+ cp temp/style/vendor/skin-blue.min.css admin/style/vendor/skin-blue.min.css
+ cp temp/style/vendor/daterangepicker.css admin/style/vendor/daterangepicker.css
+ cp temp/scripts/vendor/app.min.js admin/scripts/vendor/app.min.js
+ cp temp/scripts/pi-hole/js/index.js admin/scripts/pi-hole/js/index.js
+ rm -rf temp/
+ break;;
+ [Nn]* ) break;;
+ * ) echo "Please answer Yes (y) or No (n).";;
+ esac
+done
+echo "All done!"
diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js
new file mode 100644
index 0000000..5fc252c
--- /dev/null
+++ b/scripts/pi-hole/js/index.js
@@ -0,0 +1,1083 @@
+/* Pi-hole: A black hole for Internet advertisements
+* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
+* Network-wide ad blocking via your own hardware.
+*
+* This file is copyright under the latest version of the EUPL.
+* Please see LICENSE file for your rights under this license. */
+// Define global variables
+/* global Chart */
+var timeLineChart, queryTypeChart, forwardDestinationChart;
+var queryTypePieChart, forwardDestinationPieChart, clientsChart;
+
+function padNumber(num) {
+ return ("00" + num).substr(-2, 2);
+}
+
+// Helper function needed for converting the Objects to Arrays
+
+function objectToArray(p) {
+ var keys = Object.keys(p);
+ keys.sort(function (a, b) {
+ return a - b;
+ });
+
+ var arr = [], idx = [];
+ for (var i = 0; i < keys.length; i++) {
+ arr.push(p[keys[i]]);
+ idx.push(keys[i]);
+ }
+ return [idx, arr];
+}
+
+var lastTooltipTime = 0;
+
+var customTooltips = function (tooltip) {
+ // Tooltip Element
+ var tooltipEl = document.getElementById("chartjs-tooltip");
+ if (!tooltipEl) {
+ tooltipEl = document.createElement("div");
+ tooltipEl.id = "chartjs-tooltip";
+ document.body.appendChild(tooltipEl);
+ $(tooltipEl).html("
");
+ }
+ // Hide if no tooltip
+ if (tooltip.opacity === 0) {
+ tooltipEl.style.opacity = 0;
+ return;
+ }
+
+ // Limit rendering to once every 50ms. This gives the DOM time to react,
+ // and avoids "lag" caused by not giving the DOM time to reapply CSS.
+ var now = Date.now();
+ if (now - lastTooltipTime < 50) {
+ return;
+ }
+ lastTooltipTime = now;
+
+ // Set caret Position
+ tooltipEl.classList.remove("above", "below", "no-transform");
+ if (tooltip.yAlign) {
+ tooltipEl.classList.add(tooltip.yAlign);
+ } else {
+ tooltipEl.classList.add("above");
+ }
+ function getBody(bodyItem) {
+ return bodyItem.lines;
+ }
+ // Set Text
+ if (tooltip.body) {
+ var titleLines = tooltip.title || [];
+ var bodyLines = tooltip.body.map(getBody);
+ var innerHtml = "";
+ titleLines.forEach(function (title) {
+ innerHtml += "" + title + " |
";
+ });
+ innerHtml += "";
+ var printed = 0;
+ bodyLines.forEach(function (body, i) {
+ var colors = tooltip.labelColors[i];
+ var style = "background:" + colors.backgroundColor;
+ style += "; border-color:" + colors.borderColor;
+ style += "; border-width: 2px";
+ var span = "";
+ var num = body[0].split(": ");
+ // remove percent symbol from amount to allow numeric comparison
+ var number = num[1].replace(/%/i, "");
+ if (number > 0) {
+ innerHtml += "" + span + body + " |
";
+ printed++;
+ }
+ });
+ if (printed < 1) {
+ innerHtml += "No activity recorded |
";
+ }
+ innerHtml += "
";
+ $(tooltipEl).html(innerHtml);
+ }
+
+ // Display, position, and set styles for font
+ var position = this._chart.canvas.getBoundingClientRect();
+ var width = tooltip.caretX;
+ // Prevent compression of the tooltip at the right edge of the screen
+ if ($(document).width() - tooltip.caretX < 400) {
+ width = $(document).width() - 400;
+ }
+ // Prevent tooltip disapearing behind the sidebar
+ if (tooltip.caretX < 100) {
+ width = 100;
+ }
+ tooltipEl.style.opacity = 1;
+ tooltipEl.style.left = position.left + width + "px";
+ tooltipEl.style.top = position.top + tooltip.caretY + window.scrollY + "px";
+ tooltipEl.style.fontFamily = tooltip._bodyFontFamily;
+ tooltipEl.style.fontSize = tooltip.bodyFontSize + "px";
+ tooltipEl.style.fontStyle = tooltip._bodyFontStyle;
+ tooltipEl.style.padding = tooltip.yPadding + "px " + tooltip.xPadding + "px";
+};
+
+// Functions to update data in page
+
+var failures = 0;
+function updateQueriesOverTime() {
+ $.getJSON("api.php?overTimeData10mins", function (data) {
+
+ if ("FTLnotrunning" in data) {
+ return;
+ }
+
+ // convert received objects to arrays
+ data.domains_over_time = objectToArray(data.domains_over_time);
+ data.ads_over_time = objectToArray(data.ads_over_time);
+ // remove last data point since it not representative
+ data.ads_over_time[0].splice(-1, 1);
+ // Remove possibly already existing data
+ timeLineChart.data.labels = [];
+ timeLineChart.data.datasets[0].data = [];
+ timeLineChart.data.datasets[1].data = [];
+
+ // Add data for each hour that is available
+ for (var hour in data.ads_over_time[0]) {
+ if ({}.hasOwnProperty.call(data.ads_over_time[0], hour)) {
+ var d, h;
+ h = parseInt(data.domains_over_time[0][hour]);
+ if (parseInt(data.ads_over_time[0][0]) < 1200) {
+ // Fallback - old style
+ d = new Date().setHours(Math.floor(h / 6), 10 * (h % 6), 0, 0);
+ }
+ else {
+ // New style: Get Unix timestamps
+ d = new Date(1000 * h);
+ }
+
+ timeLineChart.data.labels.push(d);
+ timeLineChart.data.datasets[0].data.push(data.domains_over_time[1][hour]);
+ timeLineChart.data.datasets[1].data.push(data.ads_over_time[1][hour]);
+ }
+ }
+ $("#queries-over-time .overlay").hide();
+ timeLineChart.update();
+ }).done(function () {
+ // Reload graph after 10 minutes
+ failures = 0;
+ setTimeout(updateQueriesOverTime, 600000);
+ }).fail(function () {
+ failures++;
+ if (failures < 5) {
+ // Try again after 1 minute only if this has not failed more
+ // than five times in a row
+ setTimeout(updateQueriesOverTime, 60000);
+ }
+ });
+}
+
+function updateQueryTypesPie() {
+ $.getJSON("api.php?getQueryTypes", function (data) {
+
+ if ("FTLnotrunning" in data) {
+ return;
+ }
+
+ var colors = [];
+ // Get colors from AdminLTE
+ $.each($.AdminLTE.options.colors, function (key, value) { colors.push(value); });
+ var v = [], c = [], k = [], iter;
+ // Collect values and colors, and labels
+ if (data.hasOwnProperty("querytypes")) {
+ iter = data.querytypes;
+ }
+ else {
+ iter = data;
+ }
+ var dd = [];
+ $.each(iter, function (key, value) {
+ v = [value];
+ c = colors.shift();
+ k = key ;
+ dd.push({data: v, backgroundColor: c, label: k});
+ });
+ dd.sort((a,b) => (a.data[0] < b.data[0]) ? 1 : -1);
+ //dd.sort((a,b) => (a[1] < b[1]) ? 1 : -1)
+ // Build a single dataset with the data to be pushed
+ // and push it at once
+ queryTypePieChart.data.labels = ["Type"];
+ queryTypePieChart.data.datasets = [];
+ $.each(dd, function(key,value){
+ queryTypePieChart.data.datasets.push(dd[key]);
+ });
+ $("#query-types-pie .overlay").hide();
+ queryTypePieChart.update();
+ queryTypePieChart.chart.config.options.cutoutPercentage = 50;
+ queryTypePieChart.update();
+ // Don't use rotation animation for further updates
+ queryTypePieChart.options.animation.duration = 0;
+ // Generate legend in separate div
+ $("#query-types-legend").html(queryTypePieChart.generateLegend());
+ $("#query-types-legend > ul > li").on("mousedown", function (e) {
+ if (e.which === 2) // which == 2 is middle mouse button
+ {
+ $(this).toggleClass("strike");
+ var index = $(this).index();
+ var ci = e.view.queryTypePieChart;
+ var meta = ci.data.datasets[0]._meta;
+ for (let i in meta) {
+ if ({}.hasOwnProperty.call(meta, i)) {
+ var curr = meta[i].data[index];
+ curr.hidden = !curr.hidden;
+ }
+ }
+ ci.update();
+ }
+ else if (e.which === 1) // which == 1 is left mouse button
+ {
+ window.open("queries.php?querytype=" + ($(this).index() + 1), "_self");
+ }
+ });
+ }).done(function () {
+ // Reload graph after minute
+ setTimeout(updateQueryTypesPie, 60000);
+ });
+}
+
+function updateClientsOverTime() {
+ $.getJSON("api.php?overTimeDataClients&getClientNames", function (data) {
+
+ if ("FTLnotrunning" in data) {
+ return;
+ }
+
+ // convert received objects to arrays
+ data.over_time = objectToArray(data.over_time);
+
+ // Remove graph if there are no results (e.g. privacy mode enabled)
+ if (jQuery.isEmptyObject(data.over_time)) {
+ $("#clients").parent().remove();
+ return;
+ }
+ // remove last data point since it not representative
+ data.over_time[0].splice(-1, 1);
+ var timestamps = data.over_time[0];
+ var plotdata = data.over_time[1];
+ var labels = [];
+ var key, i, j;
+ for (key in data.clients) {
+ if (!{}.hasOwnProperty.call(data.clients, key)) {
+ continue;
+ }
+ var clientname;
+ if (data.clients[key].name.length > 0) {
+ clientname = data.clients[key].name;
+ }
+ else {
+ clientname = data.clients[key].ip;
+ }
+ labels.push(clientname);
+ }
+ // Get colors from AdminLTE
+ var colors = [];
+ $.each($.AdminLTE.options.colors, function (key, value) { colors.push(value); });
+ var v = [], c = [], k = [];
+
+ // Remove possibly already existing data
+ clientsChart.data.labels = [];
+ clientsChart.data.datasets[0].data = [];
+ for (i = 1; i < clientsChart.data.datasets.length; i++) {
+ clientsChart.data.datasets[i].data = [];
+ }
+
+ // Collect values and colors, and labels
+ clientsChart.data.datasets[0].backgroundColor = "transparent";
+ clientsChart.data.datasets[0].borderColor = colors[0]
+ clientsChart.data.datasets[0].pointRadius = 0;
+ clientsChart.data.datasets[0].pointHitRadius = 5;
+ clientsChart.data.datasets[0].pointHoverRadius = 5;
+ clientsChart.data.datasets[0].label = labels[0];
+
+ for (i = clientsChart.data.datasets.length; plotdata.length && i < plotdata[0].length; i++) {
+ clientsChart.data.datasets.push({
+ data: [],
+ // If we ran out of colors, make a random one
+ borderColor: i < colors.length
+ ? colors[i]
+ : "#" + parseInt("" + Math.random() * 0xffffff, 10).toString(16).padStart(6, "0"),
+ backgroundColor: "transparent",
+ pointRadius: 0,
+ pointHitRadius: 5,
+ pointHoverRadius: 5,
+ label: labels[i],
+ cubicInterpolationMode: "monotone"
+ });
+ }
+
+ // Add data for each dataset that is available
+ for (j in timestamps) {
+ if (!{}.hasOwnProperty.call(timestamps, j)) {
+ continue;
+ }
+ for (key in plotdata[j]) {
+ if (!{}.hasOwnProperty.call(plotdata[j], key)) {
+ continue;
+ }
+ clientsChart.data.datasets[key].data.push(plotdata[j][key]);
+ }
+
+ var d = new Date(1000 * parseInt(timestamps[j]));
+ clientsChart.data.labels.push(d);
+ }
+ $("#clients .overlay").hide();
+ clientsChart.update();
+ }).done(function () {
+ // Reload graph after 10 minutes
+ failures = 0;
+ setTimeout(updateClientsOverTime, 600000);
+ }).fail(function () {
+ failures++;
+ if (failures < 5) {
+ // Try again after 1 minute only if this has not failed more
+ // than five times in a row
+ setTimeout(updateClientsOverTime, 60000);
+ }
+ });
+}
+
+function updateForwardDestinationsPie() {
+ $.getJSON("api.php?getForwardDestinations", function (data) {
+
+ if ("FTLnotrunning" in data) {
+ return;
+ }
+
+ var colors = [];
+ // Get colors from AdminLTE
+ $.each($.AdminLTE.options.colors, function (key, value) { colors.push(value); });
+ var v = [], c = [], k = [], values = [];
+
+ // Collect values and colors
+ $.each(data.forward_destinations, function (key, value) {
+ if (key.indexOf("|") > -1) {
+ key = key.substr(0, key.indexOf("|"));
+ }
+ values.push([key, value, colors.shift()]);
+ });
+ var dd = [];
+ values.sort((a,b) => (a[1] < b[1]) ? 1 : -1)
+ // Split data into individual arrays for the graphs
+ $.each(values, function (key, value) {
+ k = value[0];
+ v = [value[1]];
+ c = value[2];
+ dd.push({data: v, backgroundColor: c, label: k});
+ });
+ // Build a single dataset with the data to be pushed
+ // and push it at once
+ forwardDestinationPieChart.data.labels = ["Destination"];
+ forwardDestinationPieChart.data.datasets = [];
+ $.each(dd, function(key,value){
+ forwardDestinationPieChart.data.datasets.push(dd[key]);
+ });
+ // and push it at once
+ $("#forward-destinations-pie .overlay").hide();
+ forwardDestinationPieChart.update();
+ forwardDestinationPieChart.chart.config.options.cutoutPercentage = 50;
+ forwardDestinationPieChart.update();
+ // Don't use rotation animation for further updates
+ forwardDestinationPieChart.options.animation.duration = 0;
+ // Generate legend in separate div
+ $("#forward-destinations-legend").html(forwardDestinationPieChart.generateLegend());
+ $("#forward-destinations-legend > ul > li").on("mousedown", function (e) {
+ if (e.which === 2) // which == 2 is middle mouse button
+ {
+ $(this).toggleClass("strike");
+ var index = $(this).index();
+ var ci = e.view.forwardDestinationPieChart;
+ var meta = ci.data.datasets[0]._meta;
+ for (let i in meta) {
+ if ({}.hasOwnProperty.call(meta, i)) {
+ var curr = meta[i].data[index];
+ curr.hidden = !curr.hidden;
+ }
+ }
+ ci.update();
+ }
+ else if (e.which === 1) // which == 1 is left mouse button
+ {
+ var obj = encodeURIComponent(e.target.innerText);
+ window.open("queries.php?forwarddest=" + obj, "_self");
+ }
+ });
+ }).done(function () {
+ // Reload graph after one minute
+ setTimeout(updateForwardDestinationsPie, 60000);
+ });
+}
+
+// Credit: http://stackoverflow.com/questions/1787322/htmlspecialchars-equivalent-in-javascript/4835406#4835406
+function escapeHtml(text) {
+ var map = {
+ "&": "&",
+ "<": "<",
+ ">": ">",
+ "\"": """,
+ "\'": "'"
+ };
+
+ return text.replace(/[&<>"']/g, function (m) { return map[m]; });
+}
+
+function updateTopClientsChart() {
+ $.getJSON("api.php?summaryRaw&getQuerySources&topClientsBlocked", function (data) {
+
+ if ("FTLnotrunning" in data) {
+ return;
+ }
+
+ // Clear tables before filling them with data
+ $("#client-frequency td").parent().remove();
+ var clienttable = $("#client-frequency").find("tbody:last");
+ var client, percentage, clientname, clientip, idx, url;
+ for (client in data.top_sources) {
+
+ if ({}.hasOwnProperty.call(data.top_sources, client)) {
+ // Sanitize client
+ if (escapeHtml(client) !== client) {
+ // Make a copy with the escaped index if necessary
+ data.top_sources[escapeHtml(client)] = data.top_sources[client];
+ }
+ client = escapeHtml(client);
+ if (client.indexOf("|") > -1) {
+ idx = client.indexOf("|");
+ clientname = client.substr(0, idx);
+ clientip = client.substr(idx + 1, client.length - idx);
+ }
+ else {
+ clientname = client;
+ clientip = client;
+ }
+
+ url = "" + clientname + "";
+ percentage = data.top_sources[client] / data.dns_queries_today * 100;
+ clienttable.append(" " + url +
+ " | " + data.top_sources[client] + " | |
");
+ }
+ }
+
+ // Clear tables before filling them with data
+ $("#client-frequency-blocked td").parent().remove();
+ var clientblockedtable = $("#client-frequency-blocked").find("tbody:last");
+ for (client in data.top_sources_blocked) {
+
+ if ({}.hasOwnProperty.call(data.top_sources_blocked, client)) {
+ // Sanitize client
+ if (escapeHtml(client) !== client) {
+ // Make a copy with the escaped index if necessary
+ data.top_sources_blocked[escapeHtml(client)] = data.top_sources_blocked[client];
+ }
+ client = escapeHtml(client);
+ if (client.indexOf("|") > -1) {
+ idx = client.indexOf("|");
+ clientname = client.substr(0, idx);
+ clientip = client.substr(idx + 1, client.length - idx);
+ }
+ else {
+ clientname = client;
+ clientip = client;
+ }
+
+ url = "" + clientname + "";
+ percentage = data.top_sources_blocked[client] / data.ads_blocked_today * 100;
+ clientblockedtable.append(" " + url +
+ " | " + data.top_sources_blocked[client] + " | |
");
+ }
+ }
+
+ // Remove table if there are no results (e.g. privacy mode enabled)
+ if (jQuery.isEmptyObject(data.top_sources)) {
+ $("#client-frequency").parent().remove();
+ }
+
+ // Remove table if there are no results (e.g. privacy mode enabled)
+ if (jQuery.isEmptyObject(data.top_sources_blocked)) {
+ $("#client-frequency-blocked").parent().remove();
+ }
+
+ $("#client-frequency .overlay").hide();
+ $("#client-frequency-blocked .overlay").hide();
+ // Update top clients list data every ten seconds
+ setTimeout(updateTopClientsChart, 10000);
+ });
+}
+
+function updateTopLists() {
+ $.getJSON("api.php?summaryRaw&topItems", function (data) {
+
+ if ("FTLnotrunning" in data) {
+ return;
+ }
+
+ // Clear tables before filling them with data
+ $("#domain-frequency td").parent().remove();
+ $("#ad-frequency td").parent().remove();
+ var domaintable = $("#domain-frequency").find("tbody:last");
+ var adtable = $("#ad-frequency").find("tbody:last");
+ var url, domain, percentage;
+ for (domain in data.top_queries) {
+ if ({}.hasOwnProperty.call(data.top_queries, domain)) {
+ // Sanitize domain
+ if (escapeHtml(domain) !== domain) {
+ // Make a copy with the escaped index if necessary
+ data.top_queries[escapeHtml(domain)] = data.top_queries[domain];
+ }
+ domain = escapeHtml(domain);
+ url = "" + domain + "";
+ percentage = data.top_queries[domain] / data.dns_queries_today * 100;
+ domaintable.append(" " + url +
+ " | " + data.top_queries[domain] + " | |
");
+ }
+ }
+
+ // Remove table if there are no results (e.g. privacy mode enabled)
+ if (jQuery.isEmptyObject(data.top_queries)) {
+ $("#domain-frequency").parent().remove();
+ }
+
+ for (domain in data.top_ads) {
+ if ({}.hasOwnProperty.call(data.top_ads, domain)) {
+ // Sanitize domain
+ if (escapeHtml(domain) !== domain) {
+ // Make a copy with the escaped index if necessary
+ data.top_ads[escapeHtml(domain)] = data.top_ads[domain];
+ }
+ domain = escapeHtml(domain);
+ url = "" + domain + "";
+ percentage = data.top_ads[domain] / data.ads_blocked_today * 100;
+ adtable.append(" " + url +
+ " | " + data.top_ads[domain] + " | |
");
+ }
+ }
+
+ // Remove table if there are no results (e.g. privacy mode enabled)
+ if (jQuery.isEmptyObject(data.top_ads)) {
+ $("#ad-frequency").parent().remove();
+ }
+
+ $("#domain-frequency .overlay").hide();
+ $("#ad-frequency .overlay").hide();
+ // Update top lists data every 10 seconds
+ setTimeout(updateTopLists, 10000);
+ });
+}
+
+var FTLoffline = false;
+function updateSummaryData(runOnce) {
+ var setTimer = function (timeInSeconds) {
+ if (!runOnce) {
+ setTimeout(updateSummaryData, timeInSeconds * 1000);
+ }
+ };
+ $.getJSON("api.php?summary", function LoadSummaryData(data) {
+
+ updateSessionTimer();
+
+ if ("FTLnotrunning" in data) {
+ data["dns_queries_today"] = "Lost";
+ data["ads_blocked_today"] = "connection";
+ data["ads_percentage_today"] = "to";
+ data["domains_being_blocked"] = "API";
+ // Adjust text
+ $("#temperature").html(" FTL offline");
+ // Show spinner
+ $("#queries-over-time .overlay").show();
+ $("#forward-destinations .overlay").show();
+ $("#query-types .overlay").show();
+ $("#client-frequency .overlay").show();
+ $("#domain-frequency .overlay").show();
+ $("#ad-frequency .overlay").show();
+
+ FTLoffline = true;
+ }
+ else if (FTLoffline) {
+ // FTL was previously offline
+ FTLoffline = false;
+ $("#temperature").text(" ");
+ updateQueriesOverTime();
+ updateTopClientsChart();
+ updateTopLists();
+ }
+
+ ["ads_blocked_today", "dns_queries_today", "ads_percentage_today", "unique_clients"].forEach(function (today) {
+ var todayElement = $("span#" + today);
+ todayElement.text() !== data[today] &&
+ todayElement.text() !== data[today] + "%" &&
+ $("span#" + today).addClass("glow");
+ });
+
+ if (data.hasOwnProperty("dns_queries_all_types")) {
+ $("#total_queries").prop("title", "only A + AAAA queries (" + data["dns_queries_all_types"] + " in total)");
+ }
+
+ window.setTimeout(function () {
+ ["ads_blocked_today", "dns_queries_today", "domains_being_blocked", "ads_percentage_today", "unique_clients"].forEach(function (header, idx) {
+ var textData = (idx === 3 && data[header] !== "to") ? data[header] + "%" : data[header];
+ $("span#" + header).text(textData);
+ });
+ $("span.glow").removeClass("glow");
+ }, 500);
+
+ }).done(function () {
+ if (!FTLoffline) {
+ setTimer(1);
+ }
+ else {
+ setTimer(10);
+ }
+ }).fail(function () {
+ setTimer(300);
+ });
+}
+
+$(document).ready(function () {
+
+ var isMobile = {
+ Windows: function () {
+ return /IEMobile/i.test(navigator.userAgent);
+ },
+ Android: function () {
+ return /Android/i.test(navigator.userAgent);
+ },
+ BlackBerry: function () {
+ return /BlackBerry/i.test(navigator.userAgent);
+ },
+ iOS: function () {
+ return /iPhone|iPad|iPod/i.test(navigator.userAgent);
+ },
+ any: function () {
+ return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Windows());
+ }
+ };
+
+ // Pull in data via AJAX
+
+ updateSummaryData();
+
+ var ctx = document.getElementById("queryOverTimeChart").getContext("2d");
+ timeLineChart = new Chart(ctx, {
+ type: "line",
+ data: {
+ labels: [],
+ datasets: [
+ {
+ label: "Total",
+ fill: false,
+ backgroundColor: "transparent",
+ borderColor: "rgba(25, 138, 78, 1)",
+ pointBorderColor: "rgba(25, 138, 78, 1)",
+ pointRadius: 1,
+ pointHoverRadius: 5,
+ data: [],
+ pointHitRadius: 5,
+ cubicInterpolationMode: "monotone"
+ },
+ {
+ label: "Blocked",
+ fill: false,
+ backgroundColor: "transparent",
+ borderColor: "rgba(46,165,187,1)",
+ pointBorderColor: "rgba(46,165,187,1)",
+ pointRadius: 1,
+ pointHoverRadius: 5,
+ data: [],
+ pointHitRadius: 5,
+ cubicInterpolationMode: "monotone"
+ }
+ ]
+ },
+ options: {
+ tooltips: {
+ enabled: true,
+ mode: "x-axis",
+ callbacks: {
+ title: function (tooltipItem, data) {
+ var label = tooltipItem[0].xLabel;
+ var time = label.match(/(\d?\d):?(\d?\d?)/);
+ var h = parseInt(time[1], 10);
+ var m = parseInt(time[2], 10) || 0;
+ var from = padNumber(h) + ":" + padNumber(m - 5) + ":00";
+ var to = padNumber(h) + ":" + padNumber(m + 4) + ":59";
+ return "Upstreams from " + from + " to " + to;
+ },
+ label: function (tooltipItems, data) {
+ if (tooltipItems.datasetIndex === 1) {
+ var percentage = 0.0;
+ var total = parseInt(data.datasets[0].data[tooltipItems.index]);
+ var blocked = parseInt(data.datasets[1].data[tooltipItems.index]);
+ if (total > 0) {
+ percentage = 100.0 * blocked / total;
+ }
+ return data.datasets[tooltipItems.datasetIndex].label + ": " + tooltipItems.yLabel + " (" + percentage.toFixed(1) + "%)";
+ }
+ else {
+ return data.datasets[tooltipItems.datasetIndex].label + ": " + tooltipItems.yLabel;
+ }
+ }
+ }
+ },
+ legend: {
+ display: false
+ },
+ scales: {
+ xAxes: [{
+ gridLines:{display:true,color:"#414141"},
+ type: "time",
+ time: {
+ unit: "hour",
+ displayFormats: {
+ hour: "HH:mm"
+ },
+ tooltipFormat: "HH:mm"
+ }
+ }],
+ yAxes: [{
+ gridLines:{display:true,color:"#414141"},
+ ticks: {
+ beginAtZero: true
+ }
+ }]
+ },
+ maintainAspectRatio: false
+ }
+ });
+
+ // Pull in data via AJAX
+
+ updateQueriesOverTime();
+
+ // Create / load "Forward Destinations over Time" only if authorized
+ if (document.getElementById("forwardDestinationChart")) {
+ ctx = document.getElementById("forwardDestinationChart").getContext("2d");
+ forwardDestinationChart = new Chart(ctx, {
+ type: "line",
+ data: {
+ labels: [],
+ datasets: [{ data: [] }]
+ },
+ options: {
+ tooltips: {
+ enabled: true,
+ mode: "x-axis",
+ callbacks: {
+ title: function (tooltipItem, data) {
+ var label = tooltipItem[0].xLabel;
+ var time = label.match(/(\d?\d):?(\d?\d?)/);
+ var h = parseInt(time[1], 10);
+ var m = parseInt(time[2], 10) || 0;
+ var from = padNumber(h) + ":" + padNumber(m - 5) + ":00";
+ var to = padNumber(h) + ":" + padNumber(m + 4) + ":59";
+ return "Forward destinations from " + from + " to " + to;
+ },
+ label: function (tooltipItems, data) {
+ return data.datasets[tooltipItems.datasetIndex].label + ": " + (100.0 * tooltipItems.yLabel).toFixed(1) + "%";
+ }
+ }
+ },
+ legend: {
+ display: false
+ },
+ scales: {
+ xAxes: [{
+ type: "time",
+ time: {
+ unit: "hour",
+ displayFormats: {
+ hour: "HH:mm"
+ },
+ tooltipFormat: "HH:mm"
+ }
+ }],
+ yAxes: [{
+ ticks: {
+ mix: 0.0,
+ max: 1.0,
+ beginAtZero: true,
+ callback: function (value, index, values) {
+ return Math.round(value * 100) + " %";
+ }
+ },
+ stacked: true
+ }]
+ },
+ maintainAspectRatio: true
+ }
+ });
+
+ // Pull in data via AJAX
+ updateForwardedOverTime();
+ }
+
+ // Create / load "Top Clients over Time" only if authorized
+ if (document.getElementById("clientsChart")) {
+ ctx = document.getElementById("clientsChart").getContext("2d");
+ clientsChart = new Chart(ctx, {
+ type: "line",
+ data: {
+ labels: [],
+ datasets: [{ data: [] }]
+ },
+ options: {
+ tooltips: {
+ enabled: false,
+ mode: "x-axis",
+ custom: customTooltips,
+ itemSort: function (a, b) {
+ return b.yLabel - a.yLabel;
+ },
+ callbacks: {
+ title: function (tooltipItem, data) {
+ var label = tooltipItem[0].xLabel;
+ var time = label.match(/(\d?\d):?(\d?\d?)/);
+ var h = parseInt(time[1], 10);
+ var m = parseInt(time[2], 10) || 0;
+ var from = padNumber(h) + ":" + padNumber(m - 5) + ":00";
+ var to = padNumber(h) + ":" + padNumber(m + 4) + ":59";
+ return "Client activity from " + from + " to " + to;
+ },
+ label: function (tooltipItems, data) {
+ return data.datasets[tooltipItems.datasetIndex].label + ": " + tooltipItems.yLabel;
+ },
+ labelColor: function (tooltipItem, data) {
+ var color = data.tooltip._data.datasets[tooltipItem.datasetIndex].borderColor;
+ return {
+ borderColor: "transparent",
+ backgroundColor: color
+ };
+ }
+ }
+ },
+ legend: {
+ display: false
+ },
+ scales: {
+ xAxes: [{
+ gridLines:{display:true,color:"#414141"},
+ type: "time",
+ time: {
+ unit: "hour",
+ displayFormats: {
+ hour: "HH:mm"
+ },
+ tooltipFormat: "HH:mm"
+ }
+ }],
+ yAxes: [{
+ gridLines:{display:true,color:"#414141"},
+ ticks: {
+ beginAtZero: true
+ },
+ stacked: false
+ }]
+ },
+ maintainAspectRatio: true
+ }
+ });
+
+ // Pull in data via AJAX
+ updateClientsOverTime();
+ }
+
+ // Create / load "Query Types over Time" only if authorized
+ if (document.getElementById("queryTypeChart")) {
+ ctx = document.getElementById("queryTypeChart").getContext("2d");
+ queryTypeChart = new Chart(ctx, {
+ type: "line",
+ data: {
+ labels: [],
+ datasets: [
+ {
+ label: "A: IPv4 queries",
+ pointRadius: 0,
+ pointHitRadius: 5,
+ pointHoverRadius: 5,
+ data: [],
+ cubicInterpolationMode: "monotone"
+ },
+ {
+ label: "AAAA: IPv6 queries",
+ pointRadius: 0,
+ pointHitRadius: 5,
+ pointHoverRadius: 5,
+ data: [],
+ cubicInterpolationMode: "monotone"
+ }
+ ]
+ },
+ options: {
+ tooltips: {
+ enabled: true,
+ mode: "x-axis",
+ callbacks: {
+ title: function (tooltipItem, data) {
+ var label = tooltipItem[0].xLabel;
+ var time = label.match(/(\d?\d):?(\d?\d?)/);
+ var h = parseInt(time[1], 10);
+ var m = parseInt(time[2], 10) || 0;
+ var from = padNumber(h) + ":" + padNumber(m - 5) + ":00";
+ var to = padNumber(h) + ":" + padNumber(m + 4) + ":59";
+ return "Query types from " + from + " to " + to;
+ },
+ label: function (tooltipItems, data) {
+ return data.datasets[tooltipItems.datasetIndex].label + ": " + (100.0 * tooltipItems.yLabel).toFixed(1) + "%";
+ }
+ }
+ },
+ legend: {
+ display: false
+ },
+ scales: {
+ xAxes: [{
+ type: "time",
+ time: {
+ unit: "hour",
+ displayFormats: {
+ hour: "HH:mm"
+ },
+ tooltipFormat: "HH:mm"
+ }
+ }],
+ yAxes: [{
+ ticks: {
+ mix: 0.0,
+ max: 1.0,
+ beginAtZero: true,
+ callback: function (value, index, values) {
+ return Math.round(value * 100) + " %";
+ }
+ },
+ stacked: true
+ }]
+ },
+ maintainAspectRatio: true
+ }
+ });
+
+ // Pull in data via AJAX
+ updateQueryTypesOverTime();
+ }
+
+ // Create / load "Top Domains" and "Top Advertisers" only if authorized
+ if (document.getElementById("domain-frequency")
+ && document.getElementById("ad-frequency")) {
+ updateTopLists();
+ }
+
+ // Create / load "Top Clients" only if authorized
+ if (document.getElementById("client-frequency")) {
+ updateTopClientsChart();
+ }
+
+ $("#queryOverTimeChart").click(function (evt) {
+ var activePoints = timeLineChart.getElementAtEvent(evt);
+ if (activePoints.length > 0) {
+ //get the internal index of slice in pie chart
+ var clickedElementindex = activePoints[0]["_index"];
+
+ //get specific label by index
+ var label = timeLineChart.data.labels[clickedElementindex];
+
+ //get value by index
+ var from = label / 1000 - 300;
+ var until = label / 1000 + 300;
+ window.location.href = "queries.php?from=" + from + "&until=" + until;
+ }
+ return false;
+ });
+
+ if (document.getElementById("queryTypePieChart")) {
+ ctx = document.getElementById("queryTypePieChart").getContext("2d");
+ queryTypePieChart = new Chart(ctx, {
+ type: "bar",
+ data: {
+ labels: [],
+ datasets: [{ data: [] }]
+ },
+ options: {
+ legend: {
+ display: false
+ },
+ tooltips: {
+ enabled: false,
+ custom: customTooltips,
+ callbacks: {
+ title: function (tooltipItem, data) {
+ return "Query types";
+ },
+ label: function (tooltipItems, data) {
+ var dataset = data.datasets[tooltipItems.datasetIndex];
+ var label = dataset.label;
+ return label + ": " + dataset.data[tooltipItems.index].toFixed(1) + "%";
+ }
+ }
+ },
+ animation: {
+ duration: 750
+ },
+ cutoutPercentage: 0,
+ scales:{
+ xAxes:[{stacked:true}],
+ yAxes:[{stacked:true,
+ gridLines:{display:true,color:"#414141"},
+ ticks:{min: 0, max: 100, stepSize: 25}
+ }]
+ }
+ }
+ });
+
+ // Pull in data via AJAX
+ updateQueryTypesPie();
+ }
+
+ if (document.getElementById("forwardDestinationPieChart")) {
+ ctx = document.getElementById("forwardDestinationPieChart").getContext("2d");
+ forwardDestinationPieChart = new Chart(ctx, {
+ type: "bar",
+ data: {
+ labels: [],
+ datasets: [{ data: [] }]
+ },
+ options: {
+ legend: {
+ display:false
+ },
+ tooltips: {
+ enabled: false,
+ custom: customTooltips,
+ callbacks: {
+ title: function (tooltipItem, data) {
+ return "Forward destinations";
+ },
+ label: function (tooltipItems, data) {
+ var dataset = data.datasets[tooltipItems.datasetIndex];
+ var label = dataset.label;
+ return label + ": " + dataset.data[tooltipItems.index].toFixed(1) + "%";
+ }
+ }
+ },
+ animation: {
+ duration: 750
+ },
+ cutoutPercentage: 0,
+ scales:{
+ xAxes:[{stacked:true}],
+ yAxes:[{stacked:true,
+ gridLines:{display:true,color:"#414141"},
+ ticks:{min: 0, max: 100, stepSize: 25}
+ }]
+ }
+ }
+ });
+
+ // Pull in data via AJAX
+ updateForwardDestinationsPie();
+ }
+});
diff --git a/scripts/vendor/app.js b/scripts/vendor/app.js
new file mode 100644
index 0000000..6dff74d
--- /dev/null
+++ b/scripts/vendor/app.js
@@ -0,0 +1,349 @@
+/*! AdminLTE app.js
+ * ================
+ * Main JS application file for AdminLTE v2. This file
+ * should be included in all pages. It controls some layout
+ * options and implements exclusive AdminLTE plugins.
+ *
+ * @Author Almsaeed Studio
+ * @Support
+ * @Email
+ * @version 2.1.2
+ * @license MIT
+ */
+"use strict";
+function _init() {
+ $.AdminLTE.layout = {
+ activate: function() {
+ var a = this;
+ a.fix(),
+ a.fixSidebar(),
+ $(window, ".wrapper").resize(function() {
+ a.fix(),
+ a.fixSidebar()
+ })
+ },
+ fix: function() {
+ var a = $(".main-header").outerHeight() + $(".main-footer").outerHeight()
+ , b = $(window).height()
+ , c = $(".sidebar").height();
+ if ($("body").hasClass("fixed"))
+ $(".content-wrapper, .right-side").css("min-height", b - $(".main-footer").outerHeight());
+ else {
+ var d;
+ b >= c ? ($(".content-wrapper, .right-side").css("min-height", b - a),
+ d = b - a) : ($(".content-wrapper, .right-side").css("min-height", c),
+ d = c);
+ var e = $($.AdminLTE.options.controlSidebarOptions.selector);
+ "undefined" != typeof e && e.height() > d && $(".content-wrapper, .right-side").css("min-height", e.height())
+ }
+ },
+ fixSidebar: function() {
+ return $("body").hasClass("fixed") ? ("undefined" == typeof $.fn.slimScroll && console && console.error("Error: the fixed layout requires the slimscroll plugin!"),
+ void ($.AdminLTE.options.sidebarSlimScroll && "undefined" != typeof $.fn.slimScroll && ($(".sidebar").slimScroll({
+ destroy: !0
+ }).height("auto"),
+ $(".sidebar").slimscroll({
+ height: $(window).height() - $(".main-header").height() + "px",
+ color: "rgba(0,0,0,0.2)",
+ size: "3px"
+ })))) : void ("undefined" != typeof $.fn.slimScroll && $(".sidebar").slimScroll({
+ destroy: !0
+ }).height("auto"))
+ }
+ },
+ $.AdminLTE.pushMenu = {
+ activate: function(a) {
+ var b = $.AdminLTE.options.screenSizes;
+ $(a).on("click", function(a) {
+ a.preventDefault(),
+ $(window).width() > b.sm - 1 ? $("body").hasClass("sidebar-collapse") ? $("body").removeClass("sidebar-collapse").trigger("expanded.pushMenu") : $("body").addClass("sidebar-collapse").trigger("collapsed.pushMenu") : $("body").hasClass("sidebar-open") ? $("body").removeClass("sidebar-open").removeClass("sidebar-collapse").trigger("collapsed.pushMenu") : $("body").addClass("sidebar-open").trigger("expanded.pushMenu")
+ }),
+ $(".content-wrapper").click(function() {
+ $(window).width() <= b.sm - 1 && $("body").hasClass("sidebar-open") && $("body").removeClass("sidebar-open")
+ }),
+ ($.AdminLTE.options.sidebarExpandOnHover || $("body").hasClass("fixed") && $("body").hasClass("sidebar-mini")) && this.expandOnHover()
+ },
+ expandOnHover: function() {
+ var a = this
+ , b = $.AdminLTE.options.screenSizes.sm - 1;
+ $(".main-sidebar").hover(function() {
+ $("body").hasClass("sidebar-mini") && $("body").hasClass("sidebar-collapse") && $(window).width() > b && a.expand()
+ }, function() {
+ $("body").hasClass("sidebar-mini") && $("body").hasClass("sidebar-expanded-on-hover") && $(window).width() > b && a.collapse()
+ })
+ },
+ expand: function() {
+ $("body").removeClass("sidebar-collapse").addClass("sidebar-expanded-on-hover")
+ },
+ collapse: function() {
+ $("body").hasClass("sidebar-expanded-on-hover") && $("body").removeClass("sidebar-expanded-on-hover").addClass("sidebar-collapse")
+ }
+ },
+ $.AdminLTE.tree = function(a) {
+ var b = this
+ , c = $.AdminLTE.options.animationSpeed;
+ $(document).on("click", a + " li a", function(a) {
+ var d = $(this)
+ , e = d.next();
+ if (e.is(".treeview-menu") && e.is(":visible"))
+ e.slideUp(c, function() {
+ e.removeClass("menu-open")
+ }),
+ e.parent("li").removeClass("active");
+ else if (e.is(".treeview-menu") && !e.is(":visible")) {
+ var f = d.parents("ul").first()
+ , g = f.find("ul:visible").slideUp(c);
+ g.removeClass("menu-open");
+ var h = d.parent("li");
+ e.slideDown(c, function() {
+ e.addClass("menu-open"),
+ f.find("li.active").removeClass("active"),
+ h.addClass("active"),
+ b.layout.fix()
+ })
+ }
+ e.is(".treeview-menu") && a.preventDefault()
+ })
+ }
+ ,
+ $.AdminLTE.controlSidebar = {
+ activate: function() {
+ var a = this
+ , b = $.AdminLTE.options.controlSidebarOptions
+ , c = $(b.selector)
+ , d = $(b.toggleBtnSelector);
+ d.on("click", function(d) {
+ d.preventDefault(),
+ c.hasClass("control-sidebar-open") || $("body").hasClass("control-sidebar-open") ? a.close(c, b.slide) : a.open(c, b.slide)
+ });
+ var e = $(".control-sidebar-bg");
+ a._fix(e),
+ $("body").hasClass("fixed") ? a._fixForFixed(c) : $(".content-wrapper, .right-side").height() < c.height() && a._fixForContent(c)
+ },
+ open: function(a, b) {
+ b ? a.addClass("control-sidebar-open") : $("body").addClass("control-sidebar-open")
+ },
+ close: function(a, b) {
+ b ? a.removeClass("control-sidebar-open") : $("body").removeClass("control-sidebar-open")
+ },
+ _fix: function(a) {
+ var b = this;
+ $("body").hasClass("layout-boxed") ? (a.css("position", "absolute"),
+ a.height($(".wrapper").height()),
+ $(window).resize(function() {
+ b._fix(a)
+ })) : a.css({
+ position: "fixed",
+ height: "auto"
+ })
+ },
+ _fixForFixed: function(a) {
+ a.css({
+ position: "fixed",
+ "max-height": "100%",
+ overflow: "auto",
+ "padding-bottom": "50px"
+ })
+ },
+ _fixForContent: function(a) {
+ $(".content-wrapper, .right-side").css("min-height", a.height())
+ }
+ },
+ $.AdminLTE.boxWidget = {
+ selectors: $.AdminLTE.options.boxWidgetOptions.boxWidgetSelectors,
+ icons: $.AdminLTE.options.boxWidgetOptions.boxWidgetIcons,
+ animationSpeed: $.AdminLTE.options.animationSpeed,
+ activate: function(a) {
+ var b = this;
+ a || (a = document),
+ $(a).on("click", b.selectors.collapse, function(a) {
+ a.preventDefault(),
+ b.collapse($(this))
+ }),
+ $(a).on("click", b.selectors.remove, function(a) {
+ a.preventDefault(),
+ b.remove($(this))
+ })
+ },
+ collapse: function(a) {
+ var b = this
+ , c = a.parents(".box").first()
+ , d = c.find("> .box-body, > .box-footer, > form >.box-body, > form > .box-footer");
+ c.hasClass("collapsed-box") ? (a.children(":first").removeClass(b.icons.open).addClass(b.icons.collapse),
+ d.slideDown(b.animationSpeed, function() {
+ c.removeClass("collapsed-box")
+ })) : (a.children(":first").removeClass(b.icons.collapse).addClass(b.icons.open),
+ d.slideUp(b.animationSpeed, function() {
+ c.addClass("collapsed-box")
+ }))
+ },
+ remove: function(a) {
+ var b = a.parents(".box").first();
+ b.slideUp(this.animationSpeed)
+ }
+ }
+}
+if ("undefined" == typeof jQuery)
+ throw new Error("AdminLTE requires jQuery");
+$.AdminLTE = {},
+$.AdminLTE.options = {
+ navbarMenuSlimscroll: !0,
+ navbarMenuSlimscrollWidth: "3px",
+ navbarMenuHeight: "200px",
+ animationSpeed: 500,
+ sidebarToggleSelector: "[data-toggle='offcanvas']",
+ sidebarPushMenu: !0,
+ sidebarSlimScroll: !0,
+ sidebarExpandOnHover: !1,
+ enableBoxRefresh: !0,
+ enableBSToppltip: !0,
+ BSTooltipSelector: "[data-toggle='tooltip']",
+ enableFastclick: !0,
+ enableControlSidebar: !0,
+ controlSidebarOptions: {
+ toggleBtnSelector: "[data-toggle='control-sidebar']",
+ selector: ".control-sidebar",
+ slide: !0
+ },
+ enableBoxWidget: !0,
+ boxWidgetOptions: {
+ boxWidgetIcons: {
+ collapse: "fa-minus",
+ open: "fa-plus",
+ remove: "fa-times"
+ },
+ boxWidgetSelectors: {
+ remove: '[data-widget="remove"]',
+ collapse: '[data-widget="collapse"]'
+ }
+ },
+ directChat: {
+ enable: !0,
+ contactToggleSelector: '[data-widget="chat-pane-toggle"]'
+ },
+ colors: {
+ hotPink: "#cb1c64",
+ electricBlue: "#2ea5bb",
+ energyGreen: "#01ffc3",
+ darkBlue: "#23568f",
+ lavender: "#9d72ff",
+ lightPink: "#ed71a7",
+ robinsEgg: "#648faf",
+ slate: "#0a3949",
+
+ lightBlue: "#3c8dbc",
+ red: "#f56954",
+ green: "#00a65a",
+ aqua: "#00c0ef",
+ yellow: "#f39c12",
+ blue: "#0073b7",
+ navy: "#001F3F",
+ teal: "#39CCCC",
+ // olive: "#3D9970",
+ // lime: "#01FF70",
+ // orange: "#FF851B",
+ // fuchsia: "#F012BE",
+ // purple: "#8E24AA",
+ // maroon: "#D81B60",
+ // black: "#222222",
+ // gray: "#d2d6de"
+ },
+ screenSizes: {
+ xs: 480,
+ sm: 768,
+ md: 992,
+ lg: 1200
+ }
+},
+$(function() {
+ "undefined" != typeof AdminLTEOptions && $.extend(!0, $.AdminLTE.options, AdminLTEOptions);
+ var a = $.AdminLTE.options;
+ _init(),
+ $.AdminLTE.layout.activate(),
+ $.AdminLTE.tree(".sidebar"),
+ a.enableControlSidebar && $.AdminLTE.controlSidebar.activate(),
+ a.navbarMenuSlimscroll && "undefined" != typeof $.fn.slimscroll && $(".navbar .menu").slimscroll({
+ height: a.navbarMenuHeight,
+ alwaysVisible: !1,
+ size: a.navbarMenuSlimscrollWidth
+ }).css("width", "100%"),
+ a.sidebarPushMenu && $.AdminLTE.pushMenu.activate(a.sidebarToggleSelector),
+ a.enableBSToppltip && $("body").tooltip({
+ selector: a.BSTooltipSelector
+ }),
+ a.enableBoxWidget && $.AdminLTE.boxWidget.activate(),
+ a.enableFastclick && "undefined" != typeof FastClick && FastClick.attach(document.body),
+ a.directChat.enable && $(document).on("click", a.directChat.contactToggleSelector, function() {
+ var a = $(this).parents(".direct-chat").first();
+ a.toggleClass("direct-chat-contacts-open")
+ }),
+ $('.btn-group[data-toggle="btn-toggle"]').each(function() {
+ var a = $(this);
+ $(this).find(".btn").on("click", function(b) {
+ a.find(".btn.active").removeClass("active"),
+ $(this).addClass("active"),
+ b.preventDefault()
+ })
+ })
+}),
+function(a) {
+ a.fn.boxRefresh = function(b) {
+ function c(a) {
+ a.append(f),
+ e.onLoadStart.call(a)
+ }
+ function d(a) {
+ a.find(f).remove(),
+ e.onLoadDone.call(a)
+ }
+ var e = a.extend({
+ trigger: ".refresh-btn",
+ source: "",
+ onLoadStart: function(a) {},
+ onLoadDone: function(a) {}
+ }, b)
+ , f = a('');
+ return this.each(function() {
+ if ("" === e.source)
+ return void (console && console.log("Please specify a source first - boxRefresh()"));
+ var b = a(this)
+ , f = b.find(e.trigger).first();
+ f.on("click", function(a) {
+ a.preventDefault(),
+ c(b),
+ b.find(".box-body").load(e.source, function() {
+ d(b)
+ })
+ })
+ })
+ }
+}(jQuery),
+function(a) {
+ a.fn.activateBox = function() {
+ a.AdminLTE.boxWidget.activate(this)
+ }
+}(jQuery),
+function(a) {
+ a.fn.todolist = function(b) {
+ var c = a.extend({
+ onCheck: function(a) {},
+ onUncheck: function(a) {}
+ }, b);
+ return this.each(function() {
+ "undefined" != typeof a.fn.iCheck ? (a("input", this).on("ifChecked", function(b) {
+ var d = a(this).parents("li").first();
+ d.toggleClass("done"),
+ c.onCheck.call(d)
+ }),
+ a("input", this).on("ifUnchecked", function(b) {
+ var d = a(this).parents("li").first();
+ d.toggleClass("done"),
+ c.onUncheck.call(d)
+ })) : a("input", this).on("change", function(b) {
+ var d = a(this).parents("li").first();
+ d.toggleClass("done"),
+ c.onCheck.call(d)
+ })
+ })
+ }
+}(jQuery);
diff --git a/scripts/vendor/app.min.js b/scripts/vendor/app.min.js
new file mode 100644
index 0000000..249e330
--- /dev/null
+++ b/scripts/vendor/app.min.js
@@ -0,0 +1,13 @@
+/*! AdminLTE app.js
+ * ================
+ * Main JS application file for AdminLTE v2. This file
+ * should be included in all pages. It controls some layout
+ * options and implements exclusive AdminLTE plugins.
+ *
+ * @Author Almsaeed Studio
+ * @Support
+ * @Email
+ * @version 2.1.2
+ * @license MIT
+ */
+"use strict";function _init(){$.AdminLTE.layout={activate:function(){var e=this;e.fix(),e.fixSidebar(),$(window,".wrapper").resize(function(){e.fix(),e.fixSidebar()})},fix:function(){var e=$(".main-header").outerHeight()+$(".main-footer").outerHeight(),i=$(window).height(),o=$(".sidebar").height();if($("body").hasClass("fixed"))$(".content-wrapper, .right-side").css("min-height",i-$(".main-footer").outerHeight());else{var n;i>=o?($(".content-wrapper, .right-side").css("min-height",i-e),n=i-e):($(".content-wrapper, .right-side").css("min-height",o),n=o);var t=$($.AdminLTE.options.controlSidebarOptions.selector);void 0!==t&&t.height()>n&&$(".content-wrapper, .right-side").css("min-height",t.height())}},fixSidebar:function(){return $("body").hasClass("fixed")?(void 0===$.fn.slimScroll&&console&&console.error("Error: the fixed layout requires the slimscroll plugin!"),void($.AdminLTE.options.sidebarSlimScroll&&void 0!==$.fn.slimScroll&&($(".sidebar").slimScroll({destroy:!0}).height("auto"),$(".sidebar").slimscroll({height:$(window).height()-$(".main-header").height()+"px",color:"rgba(0,0,0,0.2)",size:"3px"})))):void(void 0!==$.fn.slimScroll&&$(".sidebar").slimScroll({destroy:!0}).height("auto"))}},$.AdminLTE.pushMenu={activate:function(e){var i=$.AdminLTE.options.screenSizes;$(e).on("click",function(e){e.preventDefault(),$(window).width()>i.sm-1?$("body").hasClass("sidebar-collapse")?$("body").removeClass("sidebar-collapse").trigger("expanded.pushMenu"):$("body").addClass("sidebar-collapse").trigger("collapsed.pushMenu"):$("body").hasClass("sidebar-open")?$("body").removeClass("sidebar-open").removeClass("sidebar-collapse").trigger("collapsed.pushMenu"):$("body").addClass("sidebar-open").trigger("expanded.pushMenu")}),$(".content-wrapper").click(function(){$(window).width()<=i.sm-1&&$("body").hasClass("sidebar-open")&&$("body").removeClass("sidebar-open")}),($.AdminLTE.options.sidebarExpandOnHover||$("body").hasClass("fixed")&&$("body").hasClass("sidebar-mini"))&&this.expandOnHover()},expandOnHover:function(){var e=this,i=$.AdminLTE.options.screenSizes.sm-1;$(".main-sidebar").hover(function(){$("body").hasClass("sidebar-mini")&&$("body").hasClass("sidebar-collapse")&&$(window).width()>i&&e.expand()},function(){$("body").hasClass("sidebar-mini")&&$("body").hasClass("sidebar-expanded-on-hover")&&$(window).width()>i&&e.collapse()})},expand:function(){$("body").removeClass("sidebar-collapse").addClass("sidebar-expanded-on-hover")},collapse:function(){$("body").hasClass("sidebar-expanded-on-hover")&&$("body").removeClass("sidebar-expanded-on-hover").addClass("sidebar-collapse")}},$.AdminLTE.tree=function(e){var i=this,o=$.AdminLTE.options.animationSpeed;$(document).on("click",e+" li a",function(e){var n=$(this),t=n.next();if(t.is(".treeview-menu")&&t.is(":visible"))t.slideUp(o,function(){t.removeClass("menu-open")}),t.parent("li").removeClass("active");else if(t.is(".treeview-menu")&&!t.is(":visible")){var s=n.parents("ul").first();s.find("ul:visible").slideUp(o).removeClass("menu-open");var a=n.parent("li");t.slideDown(o,function(){t.addClass("menu-open"),s.find("li.active").removeClass("active"),a.addClass("active"),i.layout.fix()})}t.is(".treeview-menu")&&e.preventDefault()})},$.AdminLTE.controlSidebar={activate:function(){var e=this,i=$.AdminLTE.options.controlSidebarOptions,o=$(i.selector);$(i.toggleBtnSelector).on("click",function(n){n.preventDefault(),o.hasClass("control-sidebar-open")||$("body").hasClass("control-sidebar-open")?e.close(o,i.slide):e.open(o,i.slide)});var n=$(".control-sidebar-bg");e._fix(n),$("body").hasClass("fixed")?e._fixForFixed(o):$(".content-wrapper, .right-side").height() .box-body, > .box-footer, > form >.box-body, > form > .box-footer");o.hasClass("collapsed-box")?(e.children(":first").removeClass(i.icons.open).addClass(i.icons.collapse),n.slideDown(i.animationSpeed,function(){o.removeClass("collapsed-box")})):(e.children(":first").removeClass(i.icons.collapse).addClass(i.icons.open),n.slideUp(i.animationSpeed,function(){o.addClass("collapsed-box")}))},remove:function(e){e.parents(".box").first().slideUp(this.animationSpeed)}}}if("undefined"==typeof jQuery)throw new Error("AdminLTE requires jQuery");$.AdminLTE={},$.AdminLTE.options={navbarMenuSlimscroll:!0,navbarMenuSlimscrollWidth:"3px",navbarMenuHeight:"200px",animationSpeed:500,sidebarToggleSelector:"[data-toggle='offcanvas']",sidebarPushMenu:!0,sidebarSlimScroll:!0,sidebarExpandOnHover:!1,enableBoxRefresh:!0,enableBSToppltip:!0,BSTooltipSelector:"[data-toggle='tooltip']",enableFastclick:!0,enableControlSidebar:!0,controlSidebarOptions:{toggleBtnSelector:"[data-toggle='control-sidebar']",selector:".control-sidebar",slide:!0},enableBoxWidget:!0,boxWidgetOptions:{boxWidgetIcons:{collapse:"fa-minus",open:"fa-plus",remove:"fa-times"},boxWidgetSelectors:{remove:'[data-widget="remove"]',collapse:'[data-widget="collapse"]'}},directChat:{enable:!0,contactToggleSelector:'[data-widget="chat-pane-toggle"]'},colors:{hotPink:"#cb1c64",electricBlue:"#2ea5bb",energyGreen:"#01ffc3",darkBlue:"#23568f",lavender:"#9d72ff",lightPink:"#ed71a7",robinsEgg:"#648faf",slate:"#0a3949",lightBlue:"#3c8dbc",red:"#f56954",green:"#00a65a",aqua:"#00c0ef",yellow:"#f39c12",blue:"#0073b7",navy:"#001F3F",teal:"#39CCCC"},screenSizes:{xs:480,sm:768,md:992,lg:1200}},$(function(){"undefined"!=typeof AdminLTEOptions&&$.extend(!0,$.AdminLTE.options,AdminLTEOptions);var e=$.AdminLTE.options;_init(),$.AdminLTE.layout.activate(),$.AdminLTE.tree(".sidebar"),e.enableControlSidebar&&$.AdminLTE.controlSidebar.activate(),e.navbarMenuSlimscroll&&void 0!==$.fn.slimscroll&&$(".navbar .menu").slimscroll({height:e.navbarMenuHeight,alwaysVisible:!1,size:e.navbarMenuSlimscrollWidth}).css("width","100%"),e.sidebarPushMenu&&$.AdminLTE.pushMenu.activate(e.sidebarToggleSelector),e.enableBSToppltip&&$("body").tooltip({selector:e.BSTooltipSelector}),e.enableBoxWidget&&$.AdminLTE.boxWidget.activate(),e.enableFastclick&&"undefined"!=typeof FastClick&&FastClick.attach(document.body),e.directChat.enable&&$(document).on("click",e.directChat.contactToggleSelector,function(){$(this).parents(".direct-chat").first().toggleClass("direct-chat-contacts-open")}),$('.btn-group[data-toggle="btn-toggle"]').each(function(){var e=$(this);$(this).find(".btn").on("click",function(i){e.find(".btn.active").removeClass("active"),$(this).addClass("active"),i.preventDefault()})})}),function(e){e.fn.boxRefresh=function(i){function o(e){e.append(s),t.onLoadStart.call(e)}function n(e){e.find(s).remove(),t.onLoadDone.call(e)}var t=e.extend({trigger:".refresh-btn",source:"",onLoadStart:function(e){},onLoadDone:function(e){}},i),s=e('');return this.each(function(){if(""!==t.source){var i=e(this);i.find(t.trigger).first().on("click",function(e){e.preventDefault(),o(i),i.find(".box-body").load(t.source,function(){n(i)})})}else console&&console.log("Please specify a source first - boxRefresh()")})}}(jQuery),function(e){e.fn.activateBox=function(){e.AdminLTE.boxWidget.activate(this)}}(jQuery),function(e){e.fn.todolist=function(i){var o=e.extend({onCheck:function(e){},onUncheck:function(e){}},i);return this.each(function(){void 0!==e.fn.iCheck?(e("input",this).on("ifChecked",function(i){var n=e(this).parents("li").first();n.toggleClass("done"),o.onCheck.call(n)}),e("input",this).on("ifUnchecked",function(i){var n=e(this).parents("li").first();n.toggleClass("done"),o.onUncheck.call(n)})):e("input",this).on("change",function(i){var n=e(this).parents("li").first();n.toggleClass("done"),o.onCheck.call(n)})})}}(jQuery);
\ No newline at end of file
diff --git a/skin-blue.css b/skin-blue.css
deleted file mode 100644
index 86aba16..0000000
--- a/skin-blue.css
+++ /dev/null
@@ -1,263 +0,0 @@
-/* pi-hole-midnight */
-/* Custom dark theme CSS for pi-hole to replace skin-blue AdminLTE theme */
-/* https://github.com/jacobbates/pi-hole-midnight/ */
-
-body {
-background:#000;
-}
-
-button,.btn-default {
-background-color:#080909;
-border:1px solid #666;
-color:#b2b2b2;
-}
-
-input,.form-control,pre,.box-footer,.datatables_processing {
-background:#080909;
-border:1px solid #666;
-color:#b2b2b2;
-}
-
-html {
-scrollbar-3dlight-color:#3c8dbc;
-scrollbar-arrow-color:#000;
-scrollbar-base-color:#3c8dbc;
-scrollbar-dark-shadow-color:#3c8dbc;
-scrollbar-face-color:#3c8dbc;
-scrollbar-highlight-color:#3c8dbc;
-scrollbar-shadow-color:#3c8dbc;
-scrollbar-track-color:#000;
-}
-
-::-webkit-scrollbar {
-height:3px;
-width:8px;
-}
-
-::-webkit-scrollbar-button,::-webkit-resizer {
-background-color:#666;
-}
-
-::-webkit-scrollbar-thumb {
-background-color:#3c8dbc;
-border-radius:3px;
-height:50px;
-}
-
-::-webkit-scrollbar-track,::-webkit-scrollbar-corner {
-background-color:#3c8dbc;
-}
-
-::-webkit-scrollbar-track-piece {
-background-color:#000;
-}
-
-.small-box.bg-aqua,.small-box.bg-green,.small-box.bg-red,.small-box.bg-yellow {
-background:#0a0d0f!important;
-border-top: 3px solid #3e4244;
-}
-
-.bg-red,.bg-yellow,.bg-aqua,.bg-blue,.bg-light-blue,.bg-green,.bg-navy,.bg-teal,.bg-olive,.bg-lime,.bg-orange,.bg-fuchsia,.bg-purple,.bg-maroon,.bg-black,.bg-red-active,.bg-yellow-active,.bg-aqua-active,.bg-blue-active,.bg-light-blue-active,.bg-green-active,.bg-navy-active,.bg-teal-active,.bg-olive-active,.bg-lime-active,.bg-orange-active,.bg-fuchsia-active,.bg-purple-active,.bg-maroon-active,.bg-black-active,.callout.callout-danger,.callout.callout-warning,.callout.callout-info,.callout.callout-success,.alert-success,.alert-danger,.alert-error,.alert-warning,.alert-info,.label-danger,.label-info,.label-warning,.label-primary,.label-success,.modal-primary .modal-body,.modal-primary .modal-header,.modal-primary .modal-footer,.modal-warning .modal-body,.modal-warning .modal-header,.modal-warning .modal-footer,.modal-info .modal-body,.modal-info .modal-header,.modal-info .modal-footer,.modal-success .modal-body,.modal-success .modal-header,.modal-success .modal-footer,.modal-danger .modal-body,.modal-danger .modal-header,.modal-danger .modal-footer {
-color:#fbfbfb!important;
-}
-
-.box,.tab-content,.nav-tabs-custom>.tab-content {
-background:#0a0d0f;
-border-color:#3e4244;
-}
-
-.box-header.with-border {
-background:#1c2021;
-border:none;
-color:#888;
-}
-
-.content-wrapper,.right-side {
-background:#12171a;
-color:#808b90;
-}
-
-.list-group-item,.input-group .input-group-addon,.panel,.panel-default>.panel-heading {
-background:#1c2021;
-border-color:#666;
-color:#b2b2b2;
-}
-
-.main-footer {
-background:#1a2226;
-border-color:#30383b;
-color:#b8c7ce;
-}
-
-.nav-tabs-custom,.nav-tabs-custom>.nav-tabs {
-background:#0e1010;
-border-color:#3e4244;
-color:#b2b2b2;
-}
-
-.nav-tabs-custom>.nav-tabs>li.active>a {
-border-color:#30383b;
-}
-
-.nav-tabs-custom>.nav-tabs>li.active>a,.nav-tabs-custom>.nav-tabs>li.active:hover>a {
-background-color:#0a0d0f;
-color:#b2b2b2;
-}
-
-.navbar-custom-menu>.navbar-nav>li>.dropdown-menu,.navbar-nav>.user-menu>.dropdown-menu>.user-footer,.navbar-nav>.user-menu>.dropdown-menu>.user-body a {
-background:#0c0d0e!important;
-color:#b2b2b2!important;
-}
-
-.pagination>li>a,.pagination>li>span,.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover {
-background-color:#292e30;
-border-color:#414547;
-color:#b2b2b2;
-}
-
-.progress {
-background:#12171a;
-}
-
-.skin-blue .list-group-item:hover {
-background:#292e30!important;
-}
-
-.skin-blue .main-header .navbar {
-background:#222d32;
-color:#b8c7ce;
-}
-
-.skin-blue .main-header .navbar .nav>li>a:hover,.skin-blue .main-header .navbar .nav>li>a:active,.skin-blue .main-header .navbar .nav>li>a:focus,.skin-blue .main-header .navbar .nav .open>a,.skin-blue .main-header .navbar .nav .open>a:hover,.skin-blue .main-header .navbar .nav .open>a:focus,.skin-blue .main-header .navbar .nav>.active>a,.skin-blue .main-header .navbar>.sidebar-toggle:hover,.skin-blue .main-header .navbar>.sidebar-toggle:active,.skin-blue .main-header>.logo:hover,.skin-blue .main-header>.logo:active {
-background:#3e4244;
-}
-
-.skin-blue .main-header .navbar .navbar-nav>li>a,.skin-blue .main-header .navbar .navbar-custom-menu .navbar-nav>li>a,.skin-blue .main-header .navbar .navbar-right>li>a {
-border:none;
-}
-
-.skin-blue .main-header .navbar>.sidebar-toggle {
-border:none;
-color:#b8c7ce;
-}
-
-.skin-blue .main-header>.logo {
-background:#1a2226;
-border:none;
-color:#b8c7ce;
-}
-
-.skin-blue .sidebar a,.skin-blue .main-header .navbar .nav>li>a {
-color:#b8c7ce;
-}
-
-.skin-blue .sidebar a:hover {
-text-decoration:none;
-}
-
-.skin-blue .sidebar-form {
-border:1px solid #374850;
-border-radius:3px;
-margin:10px;
-}
-
-.skin-blue .sidebar-form .btn {
-border-bottom-left-radius:0;
-border-bottom-right-radius:2px;
-border-top-left-radius:0;
-border-top-right-radius:2px;
-color:#999;
-}
-
-.skin-blue .sidebar-form input[type="text"] {
-border-bottom-left-radius:2px;
-border-bottom-right-radius:0;
-border-top-left-radius:2px;
-border-top-right-radius:0;
-color:#666;
-}
-
-.skin-blue .sidebar-form input[type="text"],.skin-blue .sidebar-form .btn {
-background-color:#374850;
-border:1px solid transparent;
-box-shadow:none;
-height:35px;
-}
-
-.skin-blue .sidebar-form input[type="text"]:focus+.input-group-btn .btn {
-border-left-color:#fff;
-}
-
-.skin-blue .sidebar-form input[type="text"]:focus,.skin-blue .sidebar-form input[type="text"]:focus+.input-group-btn .btn {
-background-color:#fff;
-color:#666;
-}
-
-.skin-blue .sidebar-menu .treeview-menu>li.active>a,.skin-blue .sidebar-menu .treeview-menu>li>a:hover {
-color:#fff;
-}
-
-.skin-blue .sidebar-menu .treeview-menu>li>a {
-color:#8aa4af;
-}
-
-.skin-blue .sidebar-menu>li.active>a {
-border-left-color:#3c8dbc;
-}
-
-.skin-blue .sidebar-menu>li.header {
-background:#1a2226;
-color:#4b646f;
-}
-
-.skin-blue .sidebar-menu>li:hover>a,.skin-blue .sidebar-menu>li.active>a,.skin-blue .sidebar-menu>li.menu-open>a {
-background:#1e282c;
-color:#fff;
-}
-
-.skin-blue .sidebar-menu>li>.treeview-menu {
-background:#2c3b41;
-margin:0 1px;
-}
-
-.skin-blue .sidebar-menu>li>a {
-border-left:3px solid transparent;
-}
-
-.skin-blue .user-panel>.info,.skin-blue .user-panel>.info>a,.nav-tabs-custom>.nav-tabs>li>a {
-color:#b2b2b2;
-}
-
-.skin-blue .wrapper,.skin-blue .main-sidebar,.skin-blue .left-side,.skin-blue .main-sidebar,.skin-blue .left-side {
-background-color:#222d32;
-}
-
-.table-bordered,.table-responsive,.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td {
-border-color:#12171a;
-}
-
-.table-striped>tbody>tr:nth-of-type(odd) {
-background-color:#101417;
-}
-
-.layout-boxed {
-background-color:#0a0d0f !important;
-background-image:none !important;
-}
-
-.chart svg, .chart canvas, .chartjs-render-monitor, .chart-legend {
--webkit-filter: invert(100%);
-filter: invert(100%);
-}
-
-#chartjs-tooltip {
-filter: invert(100%);
-}
-
-.small-box .icon{
-color: rgba(255,255,255,0.15);
-}
-
-#all-queries tr td:nth-child(6){
- color: #808b90 !important;
-}
diff --git a/skin-blue.min.css b/skin-blue.min.css
deleted file mode 100644
index df92c4b..0000000
--- a/skin-blue.min.css
+++ /dev/null
@@ -1,4 +0,0 @@
-/* pi-hole-midnight */
-/* Custom dark theme CSS for pi-hole to replace skin-blue AdminLTE theme */
-/* https://github.com/jacobbates/pi-hole-midnight/ */
-body{background:#000}.btn-default,button{background-color:#080909;border:1px solid #666;color:#b2b2b2}.box-footer,.datatables_processing,.form-control,input,pre{background:#080909;border:1px solid #666;color:#b2b2b2}html{scrollbar-3dlight-color:#3c8dbc;scrollbar-arrow-color:#000;scrollbar-base-color:#3c8dbc;scrollbar-dark-shadow-color:#3c8dbc;scrollbar-face-color:#3c8dbc;scrollbar-highlight-color:#3c8dbc;scrollbar-shadow-color:#3c8dbc;scrollbar-track-color:#000}::-webkit-scrollbar{height:3px;width:8px}::-webkit-resizer,::-webkit-scrollbar-button{background-color:#666}::-webkit-scrollbar-thumb{background-color:#3c8dbc;border-radius:3px;height:50px}::-webkit-scrollbar-corner,::-webkit-scrollbar-track{background-color:#3c8dbc}::-webkit-scrollbar-track-piece{background-color:#000}.small-box.bg-aqua,.small-box.bg-green,.small-box.bg-red,.small-box.bg-yellow{background:#0a0d0f!important;border-top:3px solid #3e4244}.alert-danger,.alert-error,.alert-info,.alert-success,.alert-warning,.bg-aqua,.bg-aqua-active,.bg-black,.bg-black-active,.bg-blue,.bg-blue-active,.bg-fuchsia,.bg-fuchsia-active,.bg-green,.bg-green-active,.bg-light-blue,.bg-light-blue-active,.bg-lime,.bg-lime-active,.bg-maroon,.bg-maroon-active,.bg-navy,.bg-navy-active,.bg-olive,.bg-olive-active,.bg-orange,.bg-orange-active,.bg-purple,.bg-purple-active,.bg-red,.bg-red-active,.bg-teal,.bg-teal-active,.bg-yellow,.bg-yellow-active,.callout.callout-danger,.callout.callout-info,.callout.callout-success,.callout.callout-warning,.label-danger,.label-info,.label-primary,.label-success,.label-warning,.modal-danger .modal-body,.modal-danger .modal-footer,.modal-danger .modal-header,.modal-info .modal-body,.modal-info .modal-footer,.modal-info .modal-header,.modal-primary .modal-body,.modal-primary .modal-footer,.modal-primary .modal-header,.modal-success .modal-body,.modal-success .modal-footer,.modal-success .modal-header,.modal-warning .modal-body,.modal-warning .modal-footer,.modal-warning .modal-header{color:#fbfbfb!important}.box,.nav-tabs-custom>.tab-content,.tab-content{background:#0a0d0f;border-color:#3e4244}.box-header.with-border{background:#1c2021;border:none;color:#888}.content-wrapper,.right-side{background:#12171a;color:#808b90}.input-group .input-group-addon,.list-group-item,.panel,.panel-default>.panel-heading{background:#1c2021;border-color:#666;color:#b2b2b2}.main-footer{background:#1a2226;border-color:#30383b;color:#b8c7ce}.nav-tabs-custom,.nav-tabs-custom>.nav-tabs{background:#0e1010;border-color:#3e4244;color:#b2b2b2}.nav-tabs-custom>.nav-tabs>li.active>a{border-color:#30383b}.nav-tabs-custom>.nav-tabs>li.active:hover>a,.nav-tabs-custom>.nav-tabs>li.active>a{background-color:#0a0d0f;color:#b2b2b2}.navbar-custom-menu>.navbar-nav>li>.dropdown-menu,.navbar-nav>.user-menu>.dropdown-menu>.user-body a,.navbar-nav>.user-menu>.dropdown-menu>.user-footer{background:#0c0d0e!important;color:#b2b2b2!important}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover,.pagination>li>a,.pagination>li>span{background-color:#292e30;border-color:#414547;color:#b2b2b2}.progress{background:#12171a}.skin-blue .list-group-item:hover{background:#292e30!important}.skin-blue .main-header .navbar{background:#222d32;color:#b8c7ce}.skin-blue .main-header .navbar .nav .open>a,.skin-blue .main-header .navbar .nav .open>a:focus,.skin-blue .main-header .navbar .nav .open>a:hover,.skin-blue .main-header .navbar .nav>.active>a,.skin-blue .main-header .navbar .nav>li>a:active,.skin-blue .main-header .navbar .nav>li>a:focus,.skin-blue .main-header .navbar .nav>li>a:hover,.skin-blue .main-header .navbar>.sidebar-toggle:active,.skin-blue .main-header .navbar>.sidebar-toggle:hover,.skin-blue .main-header>.logo:active,.skin-blue .main-header>.logo:hover{background:#3e4244}.skin-blue .main-header .navbar .navbar-custom-menu .navbar-nav>li>a,.skin-blue .main-header .navbar .navbar-nav>li>a,.skin-blue .main-header .navbar .navbar-right>li>a{border:none}.skin-blue .main-header .navbar>.sidebar-toggle{border:none;color:#b8c7ce}.skin-blue .main-header>.logo{background:#1a2226;border:none;color:#b8c7ce}.skin-blue .main-header .navbar .nav>li>a,.skin-blue .sidebar a{color:#b8c7ce}.skin-blue .sidebar a:hover{text-decoration:none}.skin-blue .sidebar-form{border:1px solid #374850;border-radius:3px;margin:10px}.skin-blue .sidebar-form .btn{color:#999;border-radius:0 2px 2px 0}.skin-blue .sidebar-form input[type=text]{color:#666;border-radius:2px 0 0 2px}.skin-blue .sidebar-form .btn,.skin-blue .sidebar-form input[type=text]{background-color:#374850;border:1px solid transparent;box-shadow:none;height:35px}.skin-blue .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-blue .sidebar-form input[type=text]:focus,.skin-blue .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-blue .sidebar-menu .treeview-menu>li.active>a,.skin-blue .sidebar-menu .treeview-menu>li>a:hover{color:#fff}.skin-blue .sidebar-menu .treeview-menu>li>a{color:#8aa4af}.skin-blue .sidebar-menu>li.active>a{border-left-color:#3c8dbc}.skin-blue .sidebar-menu>li.header{background:#1a2226;color:#4b646f}.skin-blue .sidebar-menu>li.active>a,.skin-blue .sidebar-menu>li.menu-open>a,.skin-blue .sidebar-menu>li:hover>a{background:#1e282c;color:#fff}.skin-blue .sidebar-menu>li>.treeview-menu{background:#2c3b41;margin:0 1px}.skin-blue .sidebar-menu>li>a{border-left:3px solid transparent}.nav-tabs-custom>.nav-tabs>li>a,.skin-blue .user-panel>.info,.skin-blue .user-panel>.info>a{color:#b2b2b2}.skin-blue .left-side,.skin-blue .main-sidebar,.skin-blue .wrapper{background-color:#222d32}.table-bordered,.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th,.table-responsive{border-color:#12171a}.table-striped>tbody>tr:nth-of-type(odd){background-color:#101417}.layout-boxed{background-color:#0a0d0f!important;background-image:none!important}.chart canvas,.chart svg{-webkit-filter:invert(100%);filter:invert(100%)}.small-box .icon{color:rgba(255,255,255,.15)}#all-queries tr td:nth-child(6){color: #808b90 !important;}.chartjs-render-monitor{filter:invert(100%)}.chart-legend{filter:invert(100%)}#chartjs-tooltip{filter:invert(100%)}
diff --git a/style/vendor/daterangepicker.css b/style/vendor/daterangepicker.css
new file mode 100644
index 0000000..7032397
--- /dev/null
+++ b/style/vendor/daterangepicker.css
@@ -0,0 +1,242 @@
+.daterangepicker {
+ position: absolute;
+ color: inherit;
+ background: #000000;
+ border-radius: 4px;
+ width: 278px;
+ padding: 4px;
+ margin-top: 1px;
+ top: 100px;
+ left: 20px;
+ /* Calendars */ }
+ .daterangepicker:before, .daterangepicker:after {
+ position: absolute;
+ display: inline-block;
+ border-bottom-color: rgba(0, 0, 0, 0.2);
+ content: ''; }
+ .daterangepicker th{
+ color: #e0e0e0;
+ }
+ .daterangepicker:before {
+ top: -7px;
+ border-right: 7px solid transparent;
+ border-left: 7px solid transparent;
+ border-bottom: 7px solid #ccc; }
+ .daterangepicker:after {
+ top: -6px;
+ border-right: 6px solid transparent;
+ border-bottom: 6px solid #fff;
+ border-left: 6px solid transparent; }
+ .daterangepicker.opensleft:before {
+ right: 9px; }
+ .daterangepicker.opensleft:after {
+ right: 10px; }
+ .daterangepicker.openscenter:before {
+ left: 0;
+ right: 0;
+ width: 0;
+ margin-left: auto;
+ margin-right: auto; }
+ .daterangepicker.openscenter:after {
+ left: 0;
+ right: 0;
+ width: 0;
+ margin-left: auto;
+ margin-right: auto; }
+ .daterangepicker.opensright:before {
+ left: 9px; }
+ .daterangepicker.opensright:after {
+ left: 10px; }
+ .daterangepicker.dropup {
+ margin-top: -5px; }
+ .daterangepicker.dropup:before {
+ top: initial;
+ bottom: -7px;
+ border-bottom: initial;
+ border-top: 7px solid #ccc; }
+ .daterangepicker.dropup:after {
+ top: initial;
+ bottom: -6px;
+ border-bottom: initial;
+ border-top: 6px solid #fff; }
+ .daterangepicker.dropdown-menu {
+ max-width: none;
+ z-index: 3001; }
+ .daterangepicker.single .ranges, .daterangepicker.single .calendar {
+ float: none; }
+ .daterangepicker.show-calendar .calendar {
+ display: block; }
+ .daterangepicker .calendar {
+ display: none;
+ max-width: 270px;
+ margin: 4px; }
+ .daterangepicker .calendar.single .calendar-table {
+ border: none; }
+ .daterangepicker .calendar th, .daterangepicker .calendar td {
+ white-space: nowrap;
+ text-align: center;
+ min-width: 32px; }
+ .daterangepicker .calendar-table {
+ border: 1px solid #212121;
+ padding: 4px;
+ border-radius: 4px;
+ background: #212121; }
+ .daterangepicker table {
+ width: 100%;
+ margin: 0; }
+ .daterangepicker td, .daterangepicker th {
+ text-align: center;
+ width: 20px;
+ height: 20px;
+ border-radius: 4px;
+ border: 1px solid transparent;
+ white-space: nowrap;
+ cursor: pointer; }
+ .daterangepicker td.available{
+ color: #e0e0e0 }
+ .daterangepicker td.available:hover, .daterangepicker th.available:hover {
+ background: #414141; }
+ .daterangepicker td.week, .daterangepicker th.week {
+ font-size: 80%;
+ color: #ccc; }
+ .daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date {
+ background-color: #212121;
+ border-color: transparent;
+ color: #626262; }
+ .daterangepicker td.in-range {
+ background-color: #f5f5f5;
+ border-color: transparent;
+ color: #000;
+ border-radius: 0; }
+ .daterangepicker td.start-date {
+ border-radius: 4px 0 0 4px; }
+ .daterangepicker td.end-date {
+ border-radius: 0 4px 4px 0; }
+ .daterangepicker td.start-date.end-date {
+ border-radius: 4px; }
+ .daterangepicker td.active, .daterangepicker td.active:hover {
+ background-color: #999;
+ border-color: transparent;
+ color: #212121; }
+ .daterangepicker th.month {
+
+ width: auto; }
+ .daterangepicker td.disabled, .daterangepicker option.disabled {
+ color: #999;
+ cursor: not-allowed;
+ text-decoration: line-through; }
+ .daterangepicker select.monthselect, .daterangepicker select.yearselect {
+ font-size: 12px;
+ padding: 1px;
+ height: auto;
+ margin: 0;
+ cursor: default;
+ color: #f5f5f5;
+ background-color:#424242}
+ .daterangepicker select.monthselect {
+ margin-right: 2%;
+ width: 56%; }
+ .daterangepicker select.yearselect {
+ width: 40%; }
+ .daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect {
+ width: 50px;
+ margin-bottom: 0;
+ color: #f5f5f5;
+ background-color:#424242}
+ .daterangepicker .input-mini {
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ color: #f5f5f5;
+ height: 30px;
+ line-height: 30px;
+ display: block;
+ vertical-align: middle;
+ margin: 0 0 5px 0;
+ padding: 0 6px 0 28px;
+ width: 100%; }
+ .daterangepicker .input-mini.active {
+ border: 1px solid #cccc;
+ border-radius: 4px; }
+ .daterangepicker .daterangepicker_input {
+ position: relative; }
+ .daterangepicker .daterangepicker_input i {
+ position: absolute;
+ left: 8px;
+ top: 8px; }
+ .daterangepicker .calendar-time {
+ text-align: center;
+ margin: 5px auto;
+ line-height: 30px;
+ position: relative;
+ padding-left: 28px; }
+ .daterangepicker .calendar-time select.disabled {
+ color: #ccc;
+ cursor: not-allowed; }
+
+.ranges {
+ font-size: 11px;
+ float: none;
+ margin: 4px;
+ text-align: left; }
+ .ranges ul {
+ list-style: none;
+ margin: 0 auto;
+ padding: 0;
+ width: 100%; }
+ .ranges li {
+ font-size: 13px;
+ background: #424242;
+ border: 1px solid #000;
+ border-radius: 4px;
+ color: #fff;
+ padding: 3px 12px;
+ margin-bottom: 8px;
+ cursor: pointer; }
+ .ranges li:hover {
+ background: #08c;
+ border: 1px solid #08c;
+ color: #fff; }
+ .ranges li.active {
+ background: #08c;
+ border: 1px solid #08c;
+ color: #fff; }
+
+/* Larger Screen Styling */
+@media (min-width: 564px) {
+ .daterangepicker {
+ width: auto; }
+ .daterangepicker .ranges ul {
+ width: 160px; }
+ .daterangepicker.single .ranges ul {
+ width: 100%; }
+ .daterangepicker.single .calendar.left {
+ clear: none; }
+ .daterangepicker.single .ranges, .daterangepicker.single .calendar {
+ float: left; }
+ .daterangepicker .calendar.left {
+ clear: left;
+ margin-right: 0; }
+ .daterangepicker .calendar.left .calendar-table {
+ border-right: none;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0; }
+ .daterangepicker .calendar.right {
+ margin-left: 0; }
+ .daterangepicker .calendar.right .calendar-table {
+ border-left: none;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0; }
+ .daterangepicker .left .daterangepicker_input {
+ padding-right: 12px; }
+ .daterangepicker .calendar.left .calendar-table {
+ padding-right: 12px; }
+ .daterangepicker .ranges, .daterangepicker .calendar {
+ float: left; } }
+
+@media (min-width: 730px) {
+ .daterangepicker .ranges {
+ width: auto;
+ float: left; }
+ .daterangepicker .calendar.left {
+ clear: none; } }
+
diff --git a/style/vendor/skin-blue.css b/style/vendor/skin-blue.css
new file mode 100644
index 0000000..992d7d6
--- /dev/null
+++ b/style/vendor/skin-blue.css
@@ -0,0 +1,300 @@
+/* pi-hole-midnight */
+/* Custom dark theme CSS for pi-hole to replace skin-blue AdminLTE theme */
+/* https://github.com/MBarrows20/pi-hole-material-dark/ */
+body {
+ background: #000
+}
+
+.btn-default,button {
+ background-color: #080909;
+ border: 1px solid #666;
+ color: #b2b2b2
+}
+
+.box-footer,.datatables_processing,.form-control,input,pre {
+ background: #080909;
+ border: 1px solid #666;
+ color: #b2b2b2
+}
+
+html {
+ scrollbar-3dlight-color: #3c8dbc;
+ scrollbar-arrow-color:#000;scrollbar-base-color:#3c8dbc;scrollbar-dark-shadow-color:#3c8dbc;scrollbar-face-color:#3c8dbc;scrollbar-highlight-color:#3c8dbc;scrollbar-shadow-color:#3c8dbc;scrollbar-track-color:#000}
+
+::-webkit-scrollbar {
+ height: 3px;
+ width: 8px
+}
+
+::-webkit-resizer,::-webkit-scrollbar-button {
+ background-color: #666
+}
+
+::-webkit-scrollbar-thumb {
+ background-color: #3c8dbc;
+ border-radius: 3px;
+ height: 50px
+}
+
+::-webkit-scrollbar-corner,::-webkit-scrollbar-track {
+ background-color: #3c8dbc
+}
+
+::-webkit-scrollbar-track-piece {
+ background-color: #000
+}
+
+.small-box.bg-aqua {
+ background: #212121!important;
+ border-top: 3px solid rgb(0,192,239,.8)
+}
+
+.small-box.bg-green {
+ background: #212121!important;
+ border-top: 3px solid rgb(0,166,90,.8)
+}
+
+.small-box.bg-red {
+ background: #212121!important;
+ border-top: 3px solid rgb(221,75,57,.8)
+}
+
+.small-box.bg-yellow {
+ background: #212121!important;
+ border-top: 3px solid rgb(243,156,18,.8)
+}
+
+.small-box.bg-red .icon {
+ color: rgb(221,75,57,.8)
+}
+
+.small-box.bg-green .icon {
+ color: rgb(0,166,90,.8)
+}
+
+.small-box.bg-yellow .icon {
+ color: rgb(243,156,18,.8)
+}
+
+.small-box.bg-aqua .icon {
+ color: rgb(0,192,239,.8)
+}
+
+.alert-danger,.alert-error,.alert-info,.alert-success,.alert-warning,.bg-aqua,.bg-aqua-active,.bg-black,.bg-black-active,.bg-blue,.bg-blue-active,.bg-fuchsia,.bg-fuchsia-active,.bg-green,.bg-green-active,.bg-light-blue,.bg-light-blue-active,.bg-lime,.bg-lime-active,.bg-maroon,.bg-maroon-active,.bg-navy,.bg-navy-active,.bg-olive,.bg-olive-active,.bg-orange,.bg-orange-active,.bg-purple,.bg-purple-active,.bg-red,.bg-red-active,.bg-teal,.bg-teal-active,.bg-yellow,.bg-yellow-active,.callout.callout-danger,.callout.callout-info,.callout.callout-success,.callout.callout-warning,.label-danger,.label-info,.label-primary,.label-success,.label-warning,.modal-danger .modal-body,.modal-danger .modal-footer,.modal-danger .modal-header,.modal-info .modal-body,.modal-info .modal-footer,.modal-info .modal-header,.modal-primary .modal-body,.modal-primary .modal-footer,.modal-primary .modal-header,.modal-success .modal-body,.modal-success .modal-footer,.modal-success .modal-header,.modal-warning .modal-body,.modal-warning .modal-footer,.modal-warning .modal-header {
+ color: #fbfbfb!important
+}
+
+.box,.nav-tabs-custom>.tab-content,.tab-content {
+ background: #212121;
+ border-color: #424242
+}
+
+.box-header.with-border {
+ background: #303030;
+ border: none;
+ color: #f5f5f5
+}
+
+.content-wrapper,.right-side {
+ background: #000;
+ color: #f5f5f5
+}
+
+#DHCPLeasesTable_wrapper td,#DHCPStaticLeasesTable_wrapper td,#dns td,#sysadmin td,#sysadmin th {
+ color: #c7c7c7;
+ border-color: #414141
+}
+
+.input-group .input-group-addon,.list-group-item,.panel,.panel-default>.panel-heading {
+ background: #303030;
+ border-color: #666;
+ color: #b2b2b2
+}
+
+.main-footer {
+ background: #212121;
+ border-color: #30383b;
+ color: #b8c7ce
+}
+
+.nav-tabs-custom,.nav-tabs-custom>.nav-tabs {
+ background: #0e1010;
+ border-color: #3e4244;
+ color: #b2b2b2
+}
+
+.nav-tabs-custom>.nav-tabs>li.active>a {
+ border-color: #30383b
+}
+
+.nav-tabs-custom>.nav-tabs>li.active:hover>a,.nav-tabs-custom>.nav-tabs>li.active>a {
+ background-color: #0a0d0f;
+ color: #b2b2b2
+}
+
+.navbar-custom-menu>.navbar-nav>li>.dropdown-menu,.navbar-nav>.user-menu>.dropdown-menu>.user-body a,.navbar-nav>.user-menu>.dropdown-menu>.user-footer {
+ background: #0c0d0e!important;
+ color: #b2b2b2!important
+}
+
+.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover,.pagination>li>a,.pagination>li>span {
+ background-color: #292e30;
+ border-color: #414547;
+ color: #b2b2b2
+}
+
+.progress {
+ background: #424242
+}
+
+.skin-blue .list-group-item:hover {
+ background: #292e30!important
+}
+
+.skin-blue .main-header .navbar {
+ background: #303030;
+ color: #f5f5f5
+}
+
+.skin-blue .main-header .navbar .nav .open>a,.skin-blue .main-header .navbar .nav .open>a:focus,.skin-blue .main-header .navbar .nav .open>a:hover,.skin-blue .main-header .navbar .nav>.active>a,.skin-blue .main-header .navbar .nav>li>a:active,.skin-blue .main-header .navbar .nav>li>a:focus,.skin-blue .main-header .navbar .nav>li>a:hover,.skin-blue .main-header .navbar>.sidebar-toggle:active,.skin-blue .main-header .navbar>.sidebar-toggle:hover,.skin-blue .main-header>.logo:active,.skin-blue .main-header>.logo:hover {
+ background: #3e4244
+}
+
+.skin-blue .main-header .navbar .navbar-custom-menu .navbar-nav>li>a,.skin-blue .main-header .navbar .navbar-nav>li>a,.skin-blue .main-header .navbar .navbar-right>li>a {
+ border: none
+}
+
+.skin-blue .main-header .navbar>.sidebar-toggle {
+ border: none;
+ color: #b8c7ce
+}
+
+.skin-blue .main-header>.logo {
+ background: #212121;
+ border: none;
+ color: #f5f5f5
+}
+
+.skin-blue .main-header .navbar .nav>li>a,.skin-blue .sidebar a {
+ color: #f5f5f5
+}
+
+.skin-blue .sidebar a:hover {
+ text-decoration: none
+}
+
+.skin-blue .sidebar-form {
+ border: 1px solid #374850;
+ border-radius: 3px;
+ margin: 10px
+}
+
+.skin-blue .sidebar-form .btn {
+ color: #999;
+ border-radius: 0 2px 2px 0
+}
+
+.skin-blue .sidebar-form input[type=text] {
+ color: #666;
+ border-radius: 2px 0 0 2px
+}
+
+.skin-blue .sidebar-form .btn,.skin-blue .sidebar-form input[type=text] {
+ background-color: #374850;
+ border: 1px solid transparent;
+ box-shadow: none;
+ height: 35px
+}
+
+.skin-blue .sidebar-form input[type=text]:focus+.input-group-btn .btn {
+ border-left-color: #fff
+}
+
+.skin-blue .sidebar-form input[type=text]:focus,.skin-blue .sidebar-form input[type=text]:focus+.input-group-btn .btn {
+ background-color: #fff;
+ color: #666
+}
+
+.skin-blue .sidebar-menu .treeview-menu>li.active>a,.skin-blue .sidebar-menu .treeview-menu>li>a:hover {
+ color: #fff
+}
+
+.skin-blue .sidebar-menu .treeview-menu>li>a {
+ color: #a9a9a9
+}
+
+.skin-blue .sidebar-menu>li.active>a {
+ border-left-color: #3c8dbc
+}
+
+.skin-blue .sidebar-menu>li.header {
+ background: #424242;
+ color: #f5f5f5
+}
+
+.skin-blue .sidebar-menu>li.active>a,.skin-blue .sidebar-menu>li.menu-open>a,.skin-blue .sidebar-menu>li:hover>a {
+ background: #424242;
+ color: #fff
+}
+
+.skin-blue .sidebar-menu>li>.treeview-menu {
+ background: #212121;
+ margin: 0 1px
+}
+
+.skin-blue .sidebar-menu>li>a {
+ border-left: 3px solid transparent
+}
+
+.nav-tabs-custom>.nav-tabs>li>a,.skin-blue .user-panel>.info,.skin-blue .user-panel>.info>a {
+ color: #f5f5f5
+}
+
+#ad-frequency td,#ad-frequency th,#all-queries,#all-queries th,#all-queries tr td,#all-queries tr td:nth-child(6),#client-frequency td,#client-frequency th,#client-frequency-blocked td,#client-frequency-blocked th,#domain-frequency td,#domain-frequency th,#network-entries_filter,#network-entries_length,.table-striped>tbody>tr>td,.table-striped>thead>tr>th {
+ color: #c7c7c7!important;
+ border-color: #414141
+}
+
+#ad-frequency a,#client-frequency a,#client-frequency-blocked a,#domain-frequency a {
+ color: #42a5f5
+}
+
+#forward-destinations-legend.chart-legend,#query-types-legend.chart-legend {
+ color: #f5f5f5
+}
+
+.skin-blue .left-side,.skin-blue .main-sidebar,.skin-blue .wrapper {
+ background-color: #303030
+}
+
+.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>thead>tr>td,.table-responsive,.table-responsive tbody>tr>td {
+ border-color: #414141;
+}
+
+#network-entries tbody{
+ border-color: #303030;
+ filter: invert(85%);
+}
+#network-details td{
+ filter: invert(85%);
+ color: #666;
+}
+#network-details thead, #network-details tfoot>tr>th{
+ background-color: #303030;
+ border-color: #414141;
+ color: #f5f5f5
+}
+
+
+.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>th {
+ color: #e0e0e0
+}
+
+.table-striped>tbody>tr:nth-of-type(odd) {
+ background-color: #101417
+}
+
+.layout-boxed {
+ background-color: #0a0d0f!important;
+ background-image: none!important
+}
diff --git a/style/vendor/skin-blue.min.css b/style/vendor/skin-blue.min.css
new file mode 100644
index 0000000..f11acbd
--- /dev/null
+++ b/style/vendor/skin-blue.min.css
@@ -0,0 +1,5 @@
+/* pi-hole-midnight */
+/* Custom dark theme CSS for pi-hole to replace skin-blue AdminLTE theme */
+/* https://github.com/MBarrows20/pi-hole-material-dark/ */
+
+body{background:#000}.btn-default,button{background-color:#080909;border:1px solid #666;color:#b2b2b2}.box-footer,.datatables_processing,.form-control,input,pre{background:#080909;border:1px solid #666;color:#b2b2b2}html{scrollbar-3dlight-color:#3c8dbc;scrollbar-arrow-color:#000;scrollbar-base-color:#3c8dbc;scrollbar-dark-shadow-color:#3c8dbc;scrollbar-face-color:#3c8dbc;scrollbar-highlight-color:#3c8dbc;scrollbar-shadow-color:#3c8dbc;scrollbar-track-color:#000}::-webkit-scrollbar{height:3px;width:8px}::-webkit-resizer,::-webkit-scrollbar-button{background-color:#666}::-webkit-scrollbar-thumb{background-color:#3c8dbc;border-radius:3px;height:50px}::-webkit-scrollbar-corner,::-webkit-scrollbar-track{background-color:#3c8dbc}::-webkit-scrollbar-track-piece{background-color:#000}.small-box.bg-aqua{background:#212121!important;border-top:3px solid rgb(0,192,239,.8)}.small-box.bg-green{background:#212121!important;border-top:3px solid rgb(0,166,90,.8)}.small-box.bg-red{background:#212121!important;border-top:3px solid rgb(221,75,57,.8)}.small-box.bg-yellow{background:#212121!important;border-top:3px solid rgb(243,156,18,.8)}.small-box.bg-red .icon{color:rgb(221,75,57,.8)}.small-box.bg-green .icon{color:rgb(0,166,90,.8)}.small-box.bg-yellow .icon{color:rgb(243,156,18,.8)}.small-box.bg-aqua .icon{color:rgb(0,192,239,.8)}.alert-danger,.alert-error,.alert-info,.alert-success,.alert-warning,.bg-aqua,.bg-aqua-active,.bg-black,.bg-black-active,.bg-blue,.bg-blue-active,.bg-fuchsia,.bg-fuchsia-active,.bg-green,.bg-green-active,.bg-light-blue,.bg-light-blue-active,.bg-lime,.bg-lime-active,.bg-maroon,.bg-maroon-active,.bg-navy,.bg-navy-active,.bg-olive,.bg-olive-active,.bg-orange,.bg-orange-active,.bg-purple,.bg-purple-active,.bg-red,.bg-red-active,.bg-teal,.bg-teal-active,.bg-yellow,.bg-yellow-active,.callout.callout-danger,.callout.callout-info,.callout.callout-success,.callout.callout-warning,.label-danger,.label-info,.label-primary,.label-success,.label-warning,.modal-danger .modal-body,.modal-danger .modal-footer,.modal-danger .modal-header,.modal-info .modal-body,.modal-info .modal-footer,.modal-info .modal-header,.modal-primary .modal-body,.modal-primary .modal-footer,.modal-primary .modal-header,.modal-success .modal-body,.modal-success .modal-footer,.modal-success .modal-header,.modal-warning .modal-body,.modal-warning .modal-footer,.modal-warning .modal-header{color:#fbfbfb!important}.box,.nav-tabs-custom>.tab-content,.tab-content{background:#212121;border-color:#424242}.box-header.with-border{background:#303030;border:none;color:#f5f5f5}.content-wrapper,.right-side{background:#000;color:#f5f5f5}#DHCPLeasesTable_wrapper td,#DHCPStaticLeasesTable_wrapper td,#dns td,#sysadmin td,#sysadmin th{color:#c7c7c7;border-color:#414141}.input-group .input-group-addon,.list-group-item,.panel,.panel-default>.panel-heading{background:#303030;border-color:#666;color:#b2b2b2}.main-footer{background:#212121;border-color:#30383b;color:#b8c7ce}.nav-tabs-custom,.nav-tabs-custom>.nav-tabs{background:#0e1010;border-color:#3e4244;color:#b2b2b2}.nav-tabs-custom>.nav-tabs>li.active>a{border-color:#30383b}.nav-tabs-custom>.nav-tabs>li.active:hover>a,.nav-tabs-custom>.nav-tabs>li.active>a{background-color:#0a0d0f;color:#b2b2b2}.navbar-custom-menu>.navbar-nav>li>.dropdown-menu,.navbar-nav>.user-menu>.dropdown-menu>.user-body a,.navbar-nav>.user-menu>.dropdown-menu>.user-footer{background:#0c0d0e!important;color:#b2b2b2!important}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover,.pagination>li>a,.pagination>li>span{background-color:#292e30;border-color:#414547;color:#b2b2b2}.progress{background:#424242}.skin-blue .list-group-item:hover{background:#292e30!important}.skin-blue .main-header .navbar{background:#303030;color:#f5f5f5}.skin-blue .main-header .navbar .nav .open>a,.skin-blue .main-header .navbar .nav .open>a:focus,.skin-blue .main-header .navbar .nav .open>a:hover,.skin-blue .main-header .navbar .nav>.active>a,.skin-blue .main-header .navbar .nav>li>a:active,.skin-blue .main-header .navbar .nav>li>a:focus,.skin-blue .main-header .navbar .nav>li>a:hover,.skin-blue .main-header .navbar>.sidebar-toggle:active,.skin-blue .main-header .navbar>.sidebar-toggle:hover,.skin-blue .main-header>.logo:active,.skin-blue .main-header>.logo:hover{background:#3e4244}.skin-blue .main-header .navbar .navbar-custom-menu .navbar-nav>li>a,.skin-blue .main-header .navbar .navbar-nav>li>a,.skin-blue .main-header .navbar .navbar-right>li>a{border:none}.skin-blue .main-header .navbar>.sidebar-toggle{border:none;color:#b8c7ce}.skin-blue .main-header>.logo{background:#212121;border:none;color:#f5f5f5}.skin-blue .main-header .navbar .nav>li>a,.skin-blue .sidebar a{color:#f5f5f5}.skin-blue .sidebar a:hover{text-decoration:none}.skin-blue .sidebar-form{border:1px solid #374850;border-radius:3px;margin:10px}.skin-blue .sidebar-form .btn{color:#999;border-radius:0 2px 2px 0}.skin-blue .sidebar-form input[type=text]{color:#666;border-radius:2px 0 0 2px}.skin-blue .sidebar-form .btn,.skin-blue .sidebar-form input[type=text]{background-color:#374850;border:1px solid transparent;box-shadow:none;height:35px}.skin-blue .sidebar-form input[type=text]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-blue .sidebar-form input[type=text]:focus,.skin-blue .sidebar-form input[type=text]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-blue .sidebar-menu .treeview-menu>li.active>a,.skin-blue .sidebar-menu .treeview-menu>li>a:hover{color:#fff}.skin-blue .sidebar-menu .treeview-menu>li>a{color:#a9a9a9}.skin-blue .sidebar-menu>li.active>a{border-left-color:#3c8dbc}.skin-blue .sidebar-menu>li.header{background:#424242;color:#f5f5f5}.skin-blue .sidebar-menu>li.active>a,.skin-blue .sidebar-menu>li.menu-open>a,.skin-blue .sidebar-menu>li:hover>a{background:#424242;color:#fff}.skin-blue .sidebar-menu>li>.treeview-menu{background:#212121;margin:0 1px}.skin-blue .sidebar-menu>li>a{border-left:3px solid transparent}.nav-tabs-custom>.nav-tabs>li>a,.skin-blue .user-panel>.info,.skin-blue .user-panel>.info>a{color:#f5f5f5}#ad-frequency td,#ad-frequency th,#all-queries,#all-queries th,#all-queries tr td,#all-queries tr td:nth-child(6),#client-frequency td,#client-frequency th,#client-frequency-blocked td,#client-frequency-blocked th,#domain-frequency td,#domain-frequency th,#network-entries_filter,#network-entries_length,.table-striped>tbody>tr>td,.table-striped>thead>tr>th{color:#c7c7c7!important;border-color:#414141}#ad-frequency a,#client-frequency a,#client-frequency-blocked a,#domain-frequency a{color:#42a5f5}#forward-destinations-legend.chart-legend,#query-types-legend.chart-legend{color:#f5f5f5}.skin-blue .left-side,.skin-blue .main-sidebar,.skin-blue .wrapper{background-color:#303030}.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>thead>tr>td,.table-responsive,.table-responsive tbody>tr>td{border-color:#414141}#network-entries tbody{border-color:#303030;filter:invert(85%)}#network-details td{filter:invert(85%);color:#666}#network-details tfoot>tr>th,#network-details thead{background-color:#303030;border-color:#414141;color:#f5f5f5}.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>th{color:#e0e0e0}.table-striped>tbody>tr:nth-of-type(odd){background-color:#101417}.layout-boxed{background-color:#0a0d0f!important;background-image:none!important}