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;
}
2024-05-15 12:31:36 +02:00
div#togglebuttons>button {
margin: 0 0.1em;
}
2024-05-26 15:12:32 +02:00
div>label {
padding-right: 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');
2024-05-26 16:53:34 +02:00
function create_element_link(element, href) {
element.addEventListener("click", () => {
var lnk = document.createElement('a');
if (typeof href === "function") {
lnk.href = href();
} else {
lnk.href = href;
}
if (document.createEvent) {
let e = document.createEvent("MouseEvents");
e.initMouseEvent("click", true, true, window,
0, 0, 0, 0, 0, false, false, false,
false, 0, null);
lnk.dispatchEvent(e);
} else if (lnk.fireEvent) {
lnk.fireEvent("onclick");
}
});
}
2024-05-14 21:10:53 +02:00
document.addEventListener('DOMContentLoaded', () => {
2024-05-26 12:50:29 +02:00
location.search.substr(1).split("& ").forEach((param) => {
let p = param.split("=");
let key = p[0];
if (key === "base") {
document.getElementById("base_plan").value = decodeURIComponent(p[1]);
}
2024-05-26 16:59:13 +02:00
if (key === "ext") {
2024-05-26 15:12:32 +02:00
document.getElementById("extension_plan").value = decodeURIComponent(p[1]);
2024-05-26 12:50:29 +02:00
}
});
2024-05-14 21:10:53 +02:00
update_plan();
2024-05-15 21:00:20 +02:00
2024-05-26 17:05:45 +02:00
["base", "extension"].forEach((id) => {
create_element_link(document.getElementById("download_" + id), document.getElementById(id + "_plan").value);
document.getElementById("download_" + id).addEventListener("keydown", (event) => {
if (event.key === 'Enter') {
update_plan()
}
});
2024-05-15 21:00:20 +02:00
});
2024-05-26 16:53:34 +02:00
create_element_link(document.getElementById("create_link"), function() {
return window.location.href.split('?')[0] + '?'
+ "base=" + document.getElementById("base_plan").value
+ "& ext=" + document.getElementById("extension_plan").value;
});
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;
}
2024-05-23 17:47:11 +02:00
function isInRange(number, min, max) {
2024-05-26 17:08:14 +02:00
return Number(number) >= Number(min) & & Number(number) < = Number(max);
2024-05-23 17:47:11 +02:00
}
2024-05-14 21:10:53 +02:00
2024-05-26 16:59:13 +02:00
function add_power(ext, row) {
for (const [key, value] of Object.entries(ext.power)) {
row["power_" + key] = value;
2024-05-23 17:47:11 +02:00
}
}
2024-05-26 15:12:32 +02:00
function update_table(bases, extensions) {
2024-05-23 17:47:11 +02:00
var tdata = [];
2024-05-24 19:55:54 +02:00
var base_header = {};
2024-05-26 15:12:32 +02:00
var ext_header = {};
2024-05-24 19:23:31 +02:00
2024-05-24 19:55:54 +02:00
// first, fill base_header
2024-05-26 15:12:32 +02:00
bases.forEach((base) => {
if ((typeof base.header !== "undefined") & & base.header) {
base_header = structuredClone(base);
2024-05-24 19:23:31 +02:00
}
});
// then fill tdata
2024-05-26 15:12:32 +02:00
bases.forEach((base) => {
if ((typeof base.header === "undefined") || (base.header == false)) {
2024-05-26 16:59:13 +02:00
let row = {};
2024-05-24 19:23:31 +02:00
2024-05-26 15:12:32 +02:00
// first, fill ext_header
extensions.forEach((ext) => {
if ((typeof ext.header !== "undefined") & & ext.header) {
ext_header = structuredClone(ext);
}
});
2024-05-24 19:55:54 +02:00
2024-05-26 15:12:32 +02:00
// then fill tdata
let already_added = false;
extensions.forEach((ext) => {
if ((typeof ext.header === "undefined") || (ext.header == false)) {
base_header.columns.forEach((col) => {
if (base_header.columns.includes(col)) {
2024-05-26 16:59:13 +02:00
row[col] = base[col];
2024-05-26 15:12:32 +02:00
}
});
let [ext_start, ext_end] = ext.frequency.split('-');
if (typeof base.frequency === "number") {
// at single frequency
if (isInRange(base.frequency, ext_start, ext_end)) {
2024-05-26 16:59:13 +02:00
add_power(ext, row);
2024-05-26 15:12:32 +02:00
} else {
return;
}
} else {
// frequeny range
let [base_start, base_end] = base.frequency.split('-');
2024-05-26 16:59:13 +02:00
if (isInRange(base_start, ext_start, ext_end) & & isInRange(base_end, ext_start, ext_end)) {
// base range is inside of ext range or the same
add_power(ext, row);
2024-05-24 19:23:31 +02:00
} else {
2024-05-26 15:12:32 +02:00
// base range is split by ext range
if (isInRange(ext_start, base_start, base_end)) {
// base range starts below ext range
2024-05-26 16:59:13 +02:00
let r = structuredClone(row);
2024-05-26 15:12:32 +02:00
let start = ext_start;
let end = base_end < ext_end ? base_end : ext_end ;
2024-05-26 16:59:13 +02:00
r["frequency"] = start + '-' + end;
add_power(ext, r);
tdata.push(r);
2024-05-26 15:12:32 +02:00
already_added = true;
} else if (isInRange(ext_end, base_start, base_end)) {
// ext range starts below base range
2024-05-26 16:59:13 +02:00
let r = structuredClone(row);
2024-05-26 15:12:32 +02:00
let start = base_start;
let end = ext_end;
2024-05-26 16:59:13 +02:00
r["frequency"] = start + '-' + end;
add_power(ext, r);
tdata.push(r);
2024-05-26 15:12:32 +02:00
already_added = true;
2024-05-24 19:23:31 +02:00
}
2024-05-23 17:47:11 +02:00
}
}
2024-05-14 21:10:53 +02:00
}
});
2024-05-26 15:12:32 +02:00
if (!already_added) {
2024-05-26 16:59:13 +02:00
tdata.push(row);
2024-05-26 15:12:32 +02:00
}
already_added = false;
2024-05-24 19:23:31 +02:00
}
2024-05-23 17:47:11 +02:00
});
2024-05-15 17:54:17 +02:00
2024-05-23 17:47:11 +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;
2024-05-24 19:55:54 +02:00
if (typeof base_header.titles[column.field] !== "undefined") {
column.title = base_header.titles[column.field];
}
2024-05-26 15:12:32 +02:00
if (typeof ext_header.titles[column.field] !== "undefined") {
column.title = ext_header.titles[column.field];
2024-05-24 19:55:54 +02:00
}
2024-05-23 18:00:11 +02:00
});
return definitions;
},
2024-05-23 17:47:11 +02:00
});
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' }),
2024-05-26 15:12:32 +02:00
fetch(document.getElementById('extension_plan').value, { mode: 'cors' }),
2024-05-23 17:47:11 +02:00
]).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);
}
2024-05-23 17:47:11 +02:00
}).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(
2024-05-26 17:12:23 +02:00
'Base plan and Extension plan: use your own yml file for a band plan, URLs are possible, CORS needs to be allowed for the files, download standard files for examples\n'
2024-05-15 17:54:17 +02:00
+ 'Filter rows with text in column headings, use filter starting with "!" as exclusion\n'
2024-05-26 17:12:23 +02:00
+ 'Download list: saves .pdf of the current list (selection of rows apply)\n'
+ 'Selection of rows with mouse possible\n'
+ 'Download plans: get the yml plans\n'
+ 'Create link: get link for currently selected plans for bookmarking\n'
2024-05-15 17:54:17 +02:00
);
}
2024-05-14 21:10:53 +02:00
< / script >
< div >
2024-05-26 15:12:32 +02:00
< label > Base Plan:
< input name = "plan" id = "base_plan" type = "text" value = "band-plan-iaru_r1_hf.yml" >
< / label >
< label > Extension:
< input name = "plan" id = "extension_plan" type = "text" value = "band-plan-de.yml" >
< / label >
< button onclick = "update_plan()" > Change plans< / button >
2024-05-14 21:10:53 +02:00
< / div >
2024-05-15 12:31:36 +02:00
< 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 >
2024-05-26 17:05:45 +02:00
< button id = "download_base" > Download base plan< / button >
< button id = "download_extension" > Download extension plan< / button >
2024-05-26 16:53:34 +02:00
< button id = "create_link" > Create link< / button >
2024-05-15 17:54:17 +02:00
< button onclick = "help()" > Help< / button >
2024-05-14 21:10:53 +02:00
< / div >
< div id = "data-table" > < / div >
< div id = "error" > < / div >
2024-05-26 17:12:23 +02:00
< div >
< p > This information is supplied without liability.< / p >
< p > Source is at < a href = "https://src.dm5wk.de/dm5wk/band-plan-web/" > https://src.dm5wk.de/dm5wk/band-plan-web/< / a > < / p >
< / div >
2024-05-14 21:10:53 +02:00
< / body >
< / html >