band-plan-web/band-plan.html

278 lines
8 KiB
HTML
Raw Normal View History

2024-05-14 21:10:53 +02:00
<!-- SPDX-License-Identifier: Beerware -->
<!-- Wolfgang Kroener wrote this file. As long as you retain this notice -->
<!-- you can do whatever you want with this stuff. If we meet some day, -->
<!-- and you think this stuff is worth it, you can buy me a beer in return -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>band plan</title>
<style>
body>div {
padding-bottom: 1em;
}
div#togglebuttons>button {
margin: 0 0.1em;
}
2024-05-14 21:10:53 +02:00
</style>
</head>
<body>
<link href="https://unpkg.com/tabulator-tables/dist/css/tabulator.min.css" rel="stylesheet">
<script src="https://unpkg.com/browser-cjs/require.min.js"></script>
<script src="https://unpkg.com/tabulator-tables/dist/js/tabulator.min.js"></script>
<script src="https://unpkg.com/jspdf/dist/jspdf.umd.min.js"></script>
<script src="https://unpkg.com/jspdf-autotable/dist/jspdf.plugin.autotable.min.js"></script>
<script>
// require in browser-cjs
// eslint-disable-next-line no-undef
const yaml = require('https://unpkg.com/js-yaml/dist/js-yaml.min.js');
// eslint-disable-next-line no-undef
const Measures = require('https://unpkg.com/measures/dist/measures.cjs.js');
// eslint-disable-next-line no-undef
const path = require('https://unpkg.com/path-browserify/index.js');
document.addEventListener('DOMContentLoaded', () => {
update_plan();
2024-05-15 21:00:20 +02:00
document.getElementById("base_plan").addEventListener("keydown", (event) => {
2024-05-15 21:00:20 +02:00
if (event.key === 'Enter') {
update_plan()
}
});
2024-05-14 21:10:53 +02:00
});
// eslint-disable-next-line no-unused-vars
function filter_with_not(headerValue, rowValue, rowData, filterParams){
if (headerValue.startsWith('!')) {
// filter not
const str = headerValue.slice(1);
if ((str.length > 0) && (typeof rowValue !== "undefined")) {
return !rowValue.toString().includes(str);
} else {
return true;
}
} else {
// filter normal
if (typeof rowValue !== "undefined") {
return rowValue.toString().includes(headerValue);
} else {
return false;
}
}
}
2024-05-15 17:54:17 +02:00
function display_error(e) {
let t = document.getElementById("data-table");
t.innerHTML = "";
t.removeAttribute("class");
console.log(e);
document.getElementById("error").innerText = e;
}
function isInRange(number, min, max) {
return number >= min && number <= max;
}
2024-05-14 21:10:53 +02:00
function add_power(add, p) {
if (typeof add.power_a !== "undefined") {
p["power_a"] = add.power_a;
}
if (typeof add.power_e !== "undefined") {
p["power_e"] = add.power_e;
}
if (typeof add.power_n !== "undefined") {
p["power_n"] = add.power_n;
}
}
function update_table(bands, additions) {
var tdata = [];
var header = {};
// first, fill header
bands.forEach((band) => {
if (band.band === "header") {
header = structuredClone(band);
}
});
// then fill tdata
bands.forEach((band) => {
if (band.band !== "header") {
band.parts.forEach((part) => {
let p = {};
header.columns.forEach((col) => {
p[col] = band[col];
if (col == "description") {
p[col] = part[col];
}
});
p["frequency"] = typeof part.at !== "undefined" ? part.at : part.start + '\u2013' + part.end;
let parts_already_added = false;
additions.forEach((add) => {
if (typeof part.at !== "undefined") {
if (isInRange(part.at, add.start, add.end)) {
// at single frequency
add_power(add, p);
}
} else {
// frequeny range
if ((add.start == part.start) && (add.end == part.end)) {
// ranges are the same
add_power(add, p);
} else if (isInRange(part.start, add.start, add.end) && isInRange(part.end, add.start, add.end)) {
// range of part is inside of range of add
add_power(add, p);
} else {
// part range is split by add range
if (isInRange(add.start, part.start, part.end)) {
// part range starts below add range
let p_clone = structuredClone(p);
let start = add.start;
let end = part.end < add.end ? part.end : add.end;
p_clone["frequency"] = start + '\u2013' + end;
add_power(add, p_clone);
tdata.push(p_clone);
parts_already_added = true;
} else if (isInRange(add.end, part.start, part.end)) {
// add range starts below part range
let p_clone = structuredClone(p);
let start = part.start;
let end = add.end;
p_clone["frequency"] = start + '\u2013' + end;
add_power(add, p_clone);
tdata.push(p_clone);
parts_already_added = true;
}
}
}
});
if (!parts_already_added) {
tdata.push(p);
2024-05-14 21:10:53 +02:00
}
parts_already_added = false;
2024-05-14 21:10:53 +02:00
});
}
});
2024-05-15 17:54:17 +02:00
// require in tabular-tables
// eslint-disable-next-line no-undef
var table = new Tabulator("#data-table", {
layout: "fitDataTable",
initialSort: [
{column: "frequency", dir: "asc"},
{column: "band", dir: "desc"},
],
selectableRows: true,
data: tdata,
2024-05-23 18:00:11 +02:00
autoColumns:"full",
// add filter for all columns
autoColumnsDefinitions: function(definitions){
definitions.forEach((column) => {
if (column.field == "band") {
2024-05-24 18:47:12 +02:00
column.sorter = function(a, b) {
2024-05-23 18:00:11 +02:00
return parseFloat(new Measures().from(a).to('m')) - parseFloat(new Measures().from(b).to('m'));
}
}
if (column.field == "frequency") {
column.sorter = "number";
}
column.headerFilter = "input";
column.headerFilterFunc = filter_with_not;
});
return definitions;
},
});
document.getElementById("download").addEventListener("click", () => {
let download_range = "all";
if (table.getSelectedRows().length > 0) {
download_range = "selected";
}
table.download(
"pdf",
path.basename(document.getElementById('base_plan').value, '.yml') + '.pdf',
{ orientation:"portrait" },
download_range);
});
table.on("tableBuilt", () => {
var div_togglebuttons = document.getElementById("togglebuttons");
div_togglebuttons.innerHTML = "<span>Toggle column:</span>";
table.getColumns().forEach((col) => {
let col_name = col.getField();
let b = document.createElement("button");
b.innerText = col_name;
b.addEventListener("click", () => {
table.toggleColumn(col_name);
});
div_togglebuttons.appendChild(b);
});
});
document.getElementById("error").innerText = "";
}
function update_plan() {
Promise.all([
fetch(document.getElementById('base_plan').value, { mode: 'cors' }),
fetch(document.getElementById('additions_plan').value, { mode: 'cors' }),
]).then((res) => {
res.forEach((r) => {
if (!r.ok) {
throw new Error(r.status);
}
});
Promise.all([res[0].text(), res[1].text()]).then((d) => {
try {
update_table(yaml.load(d[0]),yaml.load(d[1]));
2024-05-15 17:54:17 +02:00
} catch (e) {
display_error(e);
}
}).catch((e) => {
display_error(e);
});
2024-05-15 17:54:17 +02:00
}).catch((e) => {
display_error(e);
2024-05-14 21:10:53 +02:00
});
}
2024-05-15 17:54:17 +02:00
// used in button
// eslint-disable-next-line no-unused-vars
function help() {
alert(
'Plan: use your own yml file for a band plan, URLs are possible, CORS needs to be allowed for the file\n'
+ 'Filter rows with text in column headings, use filter starting with "!" as exclusion\n'
+ 'Download list: saves .pdf of the current list\n'
+ 'Selection of rows with mouse possible'
);
}
2024-05-14 21:10:53 +02:00
</script>
<div>
<label>Base Plan:
<input name="plan" id="base_plan" type="text" value="band-plan-iaru_r1_hf.yml">
</label>
<button onclick="update_plan()">Change plan</button>
</div>
<div>
<label>Additions:
<input name="plan" id="additions_plan" type="text" value="band-plan-de.yml">
2024-05-15 17:54:17 +02:00
</label>
<button onclick="update_plan()">Change plan</button>
2024-05-14 21:10:53 +02:00
</div>
<div id="togglebuttons"></div>
2024-05-14 21:10:53 +02:00
<div>
2024-05-15 17:54:17 +02:00
<button id="download">Download list</button>
<button onclick="help()">Help</button>
2024-05-14 21:10:53 +02:00
</div>
<div id="data-table"></div>
<div id="error"></div>
</body>
</html>