SenShaMart/ui/demo-logic.js
2023-07-13 11:32:02 +10:00

802 lines
No EOL
27 KiB
JavaScript

function startSenshamartDemoUI() {
//shared
const clearTable = (obj) => {
while (obj.rows.length !== 0) {
obj.deleteRow(-1);
}
};
//init
const publicKeySpan = document.getElementById("publicKey");
const coinCountSpan = document.getElementById("coinCount");
const status = document.getElementById("status");
let currentTab = document.getElementById("sensorsTab");
const initTab = function (baseName, style) {
const buttonName = baseName + "Button";
const button = document.getElementById(buttonName);
if (button === null) {
console.log("Couldn't find: " + buttonName);
return;
}
const tabName = baseName + "Tab";
const tab = document.getElementById(tabName);
if (tab === null) {
console.log("Couldn't find: " + tabName);
return;
}
tab.style.display = "none";
button.onclick = function (_) {
currentTab.style.display = "none";
tab.style.display = style;
currentTab = tab;
};
};
initTab("sensors", "grid");
initTab("registerSensor", "block");
initTab("integrate", "block");
initTab("freeformQuery", "block");
currentTab.style.display = "grid";
const refreshInfo = {
balance: {
onNew: [],
onDel: [],
onChange: [],
vals: {}
},
sensor: {
onNew: [],
onDel: [],
onChange: [],
vals: {}
},
broker: {
onNew: [],
onDel: [],
onChange: [],
vals: {}
},
integration: {
onNew: [],
onDel: [],
onChange: [],
vals: {}
}
};
let ourPubKey = null;
const refreshButton = document.getElementById("refresh");
const chainDepth = document.getElementById("chainDepth");
let refreshCounter = 0;
let refreshFailed = false;
let loaded = false;
const statusOK = function (str) {
status.innerHTML = str;
status.style.backgroundColor = 'lightgreen';
};
const statusWorking = function (str) {
status.innerHTML = str;
status.style.backgroundColor = 'yellow';
};
const statusError = function (str) {
status.innerHTML = str;
status.style.backgroundColor = 'red';
};
const refresh = function () {
refreshButton.disabled = true;
if (loaded !== true) {
return;
}
if (refreshCounter !== 0) {
status.innerHTML = "Couldn't refresh, already currently refreshing";
return;
}
const updateInfo = (type, newData) => {
const oldData = type.vals;
type.vals = newData;
for (const [key, value] of Object.entries(newData)) {
if (!(key in oldData)) {
for (const handler of type.onNew) {
handler(key, value);
}
} else {
for (const handler of type.onChange) {
handler(key, value);
}
}
}
for (const [key, value] of Object.entries(oldData)) {
if (!(key in newData)) {
for (const handler of type.onDel) {
handler(key);
}
}
}
};
const fetchFinal = () => {
refreshCounter--;
if (refreshCounter === 0) {
refreshButton.disabled = false;
if (!refreshFailed) {
statusOK("Refresh finished");
}
}
};
const refreshFetch = function (type, path) {
fetch(path).then((res) => {
return res.json();
}).then((data) => {
updateInfo(type, data);
}).catch((err) => {
console.log(err);
statusError("Error: " + err.message);
refreshFailed = true;
}).finally(fetchFinal);
};
refreshCounter = 4;
refreshFailed = false;
statusWorking("Refreshing");
refreshFetch(refreshInfo.sensor, "/Sensors");
refreshFetch(refreshInfo.broker, "/Brokers");
refreshFetch(refreshInfo.balance, "/Balances");
fetch('/chain-length').then((res) => {
return res.json();
}).then((data) => {
chainDepth.innerHTML = data;
}).catch((err) => {
console.log(err);
statusError("Error: " + err.message);
refreshFailed = true;
}).finally(fetchFinal);
};
refreshButton.onclick = function (_) {
refresh();
};
fetch("/public-key").then(function (res) {
return res.json();
}).then(function (pubKey) {
ourPubKey = pubKey;
publicKeySpan.innerHTML = pubKey;
loaded = true;
refresh();
}).catch(function (err) {
console.log(err);
});
//our balance header
refreshInfo.balance.onNew.push(function (key, data) {
if (key === ourPubKey) {
coinCountSpan.innerHTML = data.balance;
}
});
refreshInfo.balance.onChange.push(function (key, data) {
if (key === ourPubKey) {
coinCountSpan.innerHTML = data.balance;
}
});
//sensors
const sensorSensors = document.getElementById("sensorSensors");
const sensorInfo = document.getElementById("sensorInfo");
const sensorInfoName = document.getElementById("sensorInfoName");
const sensorInfoCPM = document.getElementById("sensorInfoCPM");
const sensorInfoCPKB = document.getElementById("sensorInfoCPKB");
const sensorInfoBroker = document.getElementById("sensorInfoBroker");
const sensorInfoRDFBody = document.getElementById("sensorInfoRDFBody");
const sensorSetInfo = function (sensor) {
sensorInfo.style.display = "block";
sensorInfoName.value = sensor.metadata.name;
sensorInfoCPM.value = sensor.metadata.costPerMinute;
sensorInfoCPKB.value = sensor.metadata.costPerKB;
sensorInfoBroker.value = sensor.metadata.integrationBroker;
clearTable(sensorInfoRDFBody);
if ("extraNodes" in sensor.metadata) {
for (const tuple of sensor.metadata.extraNodes) {
const dataRow = sensorInfoRDFBody.insertRow();
const sCell = dataRow.insertCell();
sCell.style.border = "1px solid black";
sCell.innerHTML = tuple.s;
const pCell = dataRow.insertCell();
pCell.style.border = "1px solid black";
pCell.innerHTML = tuple.p;
const oCell = dataRow.insertCell();
oCell.style.border = "1px solid black";
oCell.innerHTML = tuple.o;
}
}
if ("extraLiterals" in sensor.metadata) {
for (const tuple of sensor.metadata.extraLiterals) {
const dataRow = sensorInfoRDFBody.insertRow();
const sCell = dataRow.insertCell();
sCell.style.border = "1px solid black";
sCell.innerHTML = tuple.s;
const pCell = dataRow.insertCell();
pCell.style.border = "1px solid black";
pCell.innerHTML = tuple.p;
const oCell = dataRow.insertCell();
oCell.style.border = "1px solid black";
oCell.innerHTML = tuple.o;
}
}
}
refreshInfo.sensor.onNew.push(function (key, data) {
sensorSensors.append(new Option(key, key));
});
refreshInfo.sensor.onDel.push(function (key, data) {
const child = sensorSensors.namedItem(key);
if (child !== null) {
sensorSensors.removeChild(child);
}
});
refreshInfo.sensor.onChange.push(function (key, data) {
const child = sensorSensors.namedItem(key);
if (child === null) {
return;
}
if (child.selected) {
sensorSetInfo(data);
}
});
sensorSensors.oninput = function (_) {
if (sensorSensors.selectedIndex === -1) {
brokerInfo.style.display = "none";
return;
}
const selectedIndex = sensorSensors.selectedIndex;
const selectedOption = sensorSensors.item(selectedIndex);
const selectedSensor = refreshInfo.sensor.vals[selectedOption.value];
sensorSetInfo(selectedSensor);
};
//register sensor
const registerName = document.getElementById("registerName");
const registerCPM = document.getElementById("registerCPM");
const registerCPKB = document.getElementById("registerCPKB");
const registerBroker = document.getElementById("registerBroker");
const registerClearMetadata = document.getElementById("registerClearMetadata");
const registerMetadata = document.getElementById("registerMetadata");
registerMetadata.value = "";
const registerReward = document.getElementById("registerReward");
const registerGo = document.getElementById("registerGo");
const registerResult = document.getElementById("registerResult");
let registerParsedNodeMetadata = [];
let registerParsedLiteralMetadata = [];
registerCPM.addEventListener("change", () => {
const parsed = Number.parseInt(registerCPM.value, 10);
if (Number.isNaN(parsed) || parsed < 0) {
registerCPM.value = 1;
}
});
registerCPKB.addEventListener("change", () => {
const parsed = Number.parseInt(registerCPKB.value, 10);
if (Number.isNaN(parsed) || parsed < 0) {
registerCPKB.value = 1;
}
});
registerReward.addEventListener("change", () => {
const parsed = Number.parseInt(registerReward.value, 10);
if (Number.isNaN(parsed) || parsed < 0) {
registerReward.value = 0;
}
});
refreshInfo.broker.onNew.push(function (key, data) {
registerBroker.append(new Option(key, key));
});
refreshInfo.broker.onDel.push(function (key, data) {
const child = registerBroker.namedItem(key);
if (child !== null) {
registerBroker.removeChild(child);
}
});
registerClearMetadata.addEventListener("click", (_) => {
registerParsedNodeMetadata = [];
registerParsedLiteralMetadata = [];
registerMetadata.value = "";
});
registerMetadata.addEventListener('change', (event) => {
if (event.target.files.length !== 1) {
statusError("No file was selected");
return;
}
registerMetadata.disabled = true;
registerClearMetadata.disabled = true;
const reader = new FileReader();
reader.onload = (_) => {
const parser = new N3.Parser();
try {
const tuples = parser.parse(reader.result);
registerParsedLiteralMetadata = [];
registerParsedNodeMetadata = [];
for (const tuple of tuples) {
const adding = {
s: tuple._subject.value,
p: tuple._predicate.value,
o: tuple._object.value
};
if (tuple._object.termType === "Literal") {
registerParsedLiteralMetadata.push(adding);
} else {
registerParsedNodeMetadata.push(adding);
}
}
statusOK(`File was read sucessfully for ${registerParsedLiteralMetadata.length + registerParsedNodeMetadata.length} tuples`);
registerMetadata.disabled = false;
registerClearMetadata.disabled = false;
} catch (ex) {
statusError("Couldn't read file: " + ex.message);
console.log(ex);
registerMetadata.value = "";
registerMetadata.disabled = false;
registerClearMetadata.disabled = false;
}
};
reader.readAsText(event.target.files[0]);
});
registerGo.addEventListener("click", (_) => {
if (registerBroker.selectedIndex === -1) {
statusError("No broker selected");
return;
}
registerGo.disabled = true;
const input = {
sensorName: registerName.value,
costPerMinute: Number.parseInt(registerCPM.value),
costPerKB: Number.parseInt(registerCPKB.value),
integrationBroker: registerBroker.item(registerBroker.selectedIndex).value,
rewardAmount: Number.parseInt(registerReward.value),
};
if (registerParsedLiteralMetadata.length !== 0) {
input.extraLiteralMetadata = registerParsedLiteralMetadata;
}
if (registerParsedNodeMetadata.length !== 0) {
input.extraNodeMetadata = registerParsedNodeMetadata;
}
fetch("/sensorregistration", {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(input)
}).then((res) => {
return res.json();
}).then((res) => {
if (!res.result) {
statusError("Error while creating register sensor transaction: " + res.reason);
return;
}
registerResult.innerHTML = JSON.stringify(res.tx,null,2);
}).finally(() => {
registerGo.disabled = false;
})
});
//integrate
const integrateAdd = document.getElementById("integrateAdd");
const integrateAddSensors = document.getElementById("integrateAddSensors");
const integrateRemove = document.getElementById("integrateRemove");
const integrateRemoveSensors = document.getElementById("integrateRemoveSensors");
const integrateInfo = document.getElementById("integrateInfo");
const integrateInfoName = document.getElementById("integrateInfoName");
const integrateInfoCPM = document.getElementById("integrateInfoCPM");
const integrateInfoCPKB = document.getElementById("integrateInfoCPKB");
const integrateInfoBroker = document.getElementById("integrateInfoBroker");
const integrateInfoAmountLabel = document.getElementById("integrateInfoAmountLabel");
const integrateInfoAmount = document.getElementById("integrateInfoAmount");
const integrateAcceptModifications = document.getElementById("integrateAcceptModifications");
const integrateReward = document.getElementById("integrateReward");
const integrateGo = document.getElementById("integrateGo");
const integrateResult = document.getElementById("integrateResult");
const integrateConnectInfoBody = document.getElementById("integrateConnectInfoBody");
let integrateSelectedSensor = null;
let integrateModifiedCount = 0;
const integrateCreatedOutputs = new Map();
const integrateSetInfo = (sensorInfo) => {
const sensor = refreshInfo.sensor.vals[sensorInfo.sensor];
integrateSelectedSensor = sensor.metadata.name;
integrateInfoName.value = sensor.metadata.name;
integrateInfoCPM.value = sensor.metadata.costPerMinute;
integrateInfoCPKB.value = sensor.metadata.costPerKB;
integrateInfoBroker.value = sensor.metadata.integrationBroker;
if ("amount" in sensorInfo) {
integrateInfoAmountLabel.style.display = "block";
integrateInfoAmount.style.display = "block";
integrateInfoAmount.value = sensorInfo.amount;
} else {
integrateInfoAmountLabel.style.display = "none";
integrateInfoAmount.style.display = "none";
}
if ("modified" in sensorInfo && sensorInfo.modified) {
integrateAcceptModifications.style.display = "block";
} else {
integrateAcceptModifications.style.display = "none";
}
integrateInfo.style.display = "block";
};
refreshInfo.sensor.onNew.push((key, data) => {
if (!integrateCreatedOutputs.has(key)) {
integrateAddSensors.append(new Option(key, key));
}
});
refreshInfo.sensor.onDel.push((key, data) => {
const child = integrateAddSensors.namedItem(key);
if (child !== null) {
integrateAddSensors.removeChild(child);
}
if (integrateCreatedOutputs.has(key)) {
const found = integrateCreatedOutputs.get(key);
found.option.style.color = "red";
if (!found.modified) {
integrateModifiedCount++;
integrateGo.disabled = true;
found.modified = true;
}
if (integrateSelectedSensor === key) {
integrateAcceptModifications.style.display = "block";
}
}
});
refreshInfo.sensor.onChange.push((key, data) => {
if (integrateCreatedOutputs.has(key)) {
const found = integrateCreatedOutputs.get(key);
if (found.hash !== data.hash) {
found.hash = data.hash;
if (!found.modified) {
found.option.style.color = "orange";
integrateModifiedCount++;
integrateGo.disabled = true;
found.modified = true;
}
if (integrateSelectedSensor === key) {
integrateSetInfo(found);
}
}
} else {
if (integrateSelectedSensor === key) {
integrateSetInfo({ name: key });
}
}
});
integrateAdd.addEventListener("click", (_) => {
if (integrateAddSensors.selectedIndex === -1) {
statusError("No sensor selected");
return -1;
}
const sensorName = integrateAddSensors.item(integrateAddSensors.selectedIndex).value;
if (integrateCreatedOutputs.has(sensorName)) {
return;
}
const created = {
option: new Option(sensorName, sensorName),
amount: 1,
sensor: sensorName,
hash: refreshInfo.sensor.vals[sensorName].hash
};
integrateCreatedOutputs.set(sensorName, created);
integrateAddSensors.remove(integrateAddSensors.selectedIndex);
integrateRemoveSensors.append(created.option);
integrateRemoveSensors.selectedIndex = integrateRemoveSensors.options.length - 1;
integrateRemoveSensors.focus();
integrateSetInfo(created);
});
integrateAddSensors.addEventListener("input", (_) => {
if (integrateAddSensors.selectedIndex === -1) {
integrateInfo.style.display = "none";
integrateSelectedSensor = null;
return;
}
integrateSetInfo({
sensor: integrateAddSensors.item(integrateAddSensors.selectedIndex).value
});
});
integrateAddSensors.addEventListener("focus", (_) => {
if (integrateAddSensors.selectedIndex !== -1) {
integrateSetInfo({
sensor: integrateAddSensors.item(integrateAddSensors.selectedIndex).value
});
}
});
integrateRemove.addEventListener("click", (_) => {
if (integrateRemoveSensors.selectedIndex === -1) {
statusError("No sensor selected");
return -1;
}
const sensorName = integrateRemoveSensors.item(integrateRemoveSensors.selectedIndex).value;
const found = integrateCreatedOutputs.get(sensorName);
integrateRemoveSensors.removeChild(found.option);
integrateAddSensors.append(new Option(sensorName, sensorName));
integrateAddSensors.selectedIndex = integrateAddSensors.options.length - 1;
integrateAddSensors.focus();
integrateCreatedOutputs.delete(sensorName);
integrateSetInfo({ sensor: sensorName });
});
integrateRemoveSensors.addEventListener("input", (_) => {
if (integrateRemoveSensors.selectedIndex === -1) {
integrateInfo.style.display = "none";
integrateSelectedSensor = null;
return;
}
const found = integrateCreatedOutputs.get(integrateRemoveSensors.item(integrateRemoveSensors.selectedIndex).value);
integrateSetInfo(found);
});
integrateRemoveSensors.addEventListener("focus", (_) => {
if (integrateRemoveSensors.selectedIndex !== -1) {
const found = integrateCreatedOutputs.get(integrateRemoveSensors.item(integrateRemoveSensors.selectedIndex).value);
integrateSetInfo(found);
}
});
integrateInfoAmount.addEventListener("change", (_) => {
const parsed = Number.parseInt(integrateInfoAmount.value, 10);
const found = integrateCreatedOutputs.get(integrateSelectedSensor);
if (Number.isNaN(parsed) || parsed < 1) {
integrateInfoAmount.value = 1;
found.amount = 1;
} else {
found.amount = parsed;
}
});
integrateAcceptModifications.addEventListener("click", (_) => {
if (!integrateCreatedOutputs.has(integrateSelectedSensor)) {
return;
}
const found = integrateCreatedOutputs.get(integrateSelectedSensor);
found.modified = false;
found.option.style.color = "black";
integrateModifiedCount--;
integrateAcceptModifications.style.display = "none";
if (integrateModifiedCount === 0) {
integrateGo.disabled = false;
}
});
integrateReward.addEventListener("change", () => {
const parsed = Number.parseInt(integrateReward.value, 10);
if (Number.isNaN(parsed) || parsed < 0) {
integrateReward.value = 0;
}
});
integrateGo.addEventListener("click", (_) => {
if (integrateModifiedCount !== 0) {
return;
}
const input = {
rewardAmount: Number.parseInt(integrateReward.value),
witnessCount: 0,
outputs: []
};
for (const [name, output] of integrateCreatedOutputs.entries()) {
const sensor = refreshInfo.sensor.vals[name];
input.outputs.push({
amount: output.amount,
sensorName: name,
sensorHash: sensor.hash,
brokerHash: refreshInfo.broker.vals[sensor.metadata.integrationBroker].hash
});
}
const forDisplayLater = [];
for (let i = 0; i < input.outputs.length; i++) {
const sensor = refreshInfo.sensor.vals[input.outputs[i].sensorName];
forDisplayLater.push({
sensor: sensor.metadata.name,
brokerIp: refreshInfo.broker.vals[sensor.metadata.integrationBroker].metadata.endpoint,
index: i
});
}
integrateGo.disabled = true;
fetch("/integration", {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(input)
}).then((res) => {
return res.json();
}).then((res) => {
if (!res.result) {
statusError("Error while creating integration transaction: " + res.reason);
return;
}
clearTable(integrateConnectInfoBody);
for (const display of forDisplayLater) {
const dataRow = integrateConnectInfoBody.insertRow();
const sensorNameCell = dataRow.insertCell();
sensorNameCell.border = "1px solid black";
sensorNameCell.innerHTML = display.sensor;
const brokerIpCell = dataRow.insertCell();
brokerIpCell.border = "1px solid black";
brokerIpCell.innerHTML = display.brokerIp;
const topicCell = dataRow.insertCell();
topicCell.border = "1px solid black";
topicCell.innerHTML = 'out/' + res.hash + '/' +display.index;
}
integrateResult.innerHTML = JSON.stringify(res.tx, null, 2);
}).finally(() => {
integrateGo.disabled = false;
})
});
//freeform query
const freeformSelect = document.getElementById("freeformSelect");
const freeformQuery = document.getElementById("freeformQuery");
const freeformGo = document.getElementById("freeformGo");
const freeformHead = document.getElementById("freeformHead");
const freeformBody = document.getElementById("freeformBody");
const freeformEscaper = document.createElement('textarea');
const freeformEscape = (html) => {
freeformEscaper.textContent = html;
return freeformEscaper.innerHTML;
}
const freeformQueries = {
"Get all camera sensors": "SELECT ?sensor ?lat ?long ?measures WHERE { ?sensor <http://www.w3.org/ns/sosa/observes> ?observes. ?sensor <http://www.w3.org/ns/sosa/hasFeatureOfInterest> ?location. ?observes <http://www.w3.org/2000/01/rdf-schema#label> ?measures . ?location <http://www.w3.org/2003/01/geo/wgs84_pos#lat> ?lat . ?location <http://www.w3.org/2003/01/geo/wgs84_pos#long> ?long . ?observes <http://www.w3.org/2000/01/rdf-schema#label> \"video\"}",
"Get all milk pressure sensors": "SELECT ?sensor ?lat ?long ?measures WHERE { ?sensor <http://www.w3.org/ns/sosa/observes> ?observes. ?sensor <http://www.w3.org/ns/sosa/hasFeatureOfInterest> ?location. ?observes <http://www.w3.org/2000/01/rdf-schema#label> ?measures . ?location <http://www.w3.org/2003/01/geo/wgs84_pos#lat> ?lat . ?location <http://www.w3.org/2003/01/geo/wgs84_pos#long> ?long . ?observes <http://www.w3.org/2000/01/rdf-schema#label> \"Milk Pressure\"}",
"Get all air temperature sensors": "SELECT ?sensor ?lat ?long ?measures WHERE { ?sensor <http://www.w3.org/ns/sosa/observes> ?observes. ?sensor <http://www.w3.org/ns/sosa/hasFeatureOfInterest> ?location. ?observes <http://www.w3.org/2000/01/rdf-schema#label> ?measures . ?location <http://www.w3.org/2003/01/geo/wgs84_pos#lat> ?lat . ?location <http://www.w3.org/2003/01/geo/wgs84_pos#long> ?long . ?observes <http://www.w3.org/2000/01/rdf-schema#label> \"Air Temperature\"}",
"Get all air humidity sensors": "SELECT ?sensor ?lat ?long ?measures WHERE { ?sensor <http://www.w3.org/ns/sosa/observes> ?observes. ?sensor <http://www.w3.org/ns/sosa/hasFeatureOfInterest> ?location. ?observes <http://www.w3.org/2000/01/rdf-schema#label> ?measures . ?location <http://www.w3.org/2003/01/geo/wgs84_pos#lat> ?lat . ?location <http://www.w3.org/2003/01/geo/wgs84_pos#long> ?long . ?observes <http://www.w3.org/2000/01/rdf-schema#label> \"Relative air Humidity\"}",
"Get all milk temperature sensors": "SELECT ?sensor ?lat ?long ?measures WHERE { ?sensor <http://www.w3.org/ns/sosa/observes> ?observes. ?sensor <http://www.w3.org/ns/sosa/hasFeatureOfInterest> ?location. ?observes <http://www.w3.org/2000/01/rdf-schema#label> ?measures . ?location <http://www.w3.org/2003/01/geo/wgs84_pos#lat> ?lat . ?location <http://www.w3.org/2003/01/geo/wgs84_pos#long> ?long . ?observes <http://www.w3.org/2000/01/rdf-schema#label> \"Milk Temperature\"}",
"Get all sensors in Australia": "SELECT ?sensor ?lat ?long ?measures WHERE { ?sensor <http://www.w3.org/ns/sosa/observes> ?observes. ?sensor <http://www.w3.org/ns/sosa/hasFeatureOfInterest> ?location. ?observes <http://www.w3.org/2000/01/rdf-schema#label> ?measures . ?location <http://www.w3.org/2003/01/geo/wgs84_pos#lat> ?lat . ?location <http://www.w3.org/2003/01/geo/wgs84_pos#long> ?long . FILTER(xsd:decimal(?long) > 113.338953078 && xsd:decimal(?long) < 153.569469029 && xsd:decimal(?lat) > -43.6345972634 && xsd:decimal(?lat) < -10.6681857235)}"
};
const freeformOnInput = () => {
if (freeformSelect.selectedIndex === -1) {
return;
}
const selected = freeformSelect.item(freeformSelect.selectedIndex);
freeformQuery.innerHTML = freeformEscape(freeformQueries[selected.value]);
};
freeformSelect.addEventListener("input", freeformOnInput);
for (const [key, value] of Object.entries(freeformQueries)) {
freeformSelect.append(new Option(key, key));
}
freeformOnInput();
freeformGo.onclick = (_) => {
if (freeformSelect.selectedIndex === -1) {
statusError("No query selected");
return;
}
const input = freeformQueries[freeformSelect.item(freeformSelect.selectedIndex).value];
freeformGo.disabled = true;
clearTable(freeformHead);
clearTable(freeformBody);
fetch("/sparql", {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
query: input
})
}).then((res) => {
return res.json();
}).then((res) => {
if (!res.result) {
statusError("Error when querying: " + res.reason);
return;
}
const headersSet = new Set();
for (const obj of res.values) {
for (const [key, value] of Object.entries(obj)) {
if (!headersSet.has(key)) {
headersSet.add(key);
}
}
}
const headers = new Map();
for (const header of [...headersSet].sort()) {
headers.set(header, headers.size);
}
const headerRow = freeformHead.insertRow(-1);
const headerCells = [];
for (var i = 0; i < headers.size; ++i) {
const created = document.createElement('th');
headerRow.appendChild(created);
headerCells.push(created);
}
for (const [key, value] of headers) {
headerCells[value].innerHTML = key;
}
for (const obj of res.values) {
const dataRow = freeformBody.insertRow();
const cells = [];
for (var i = 0; i < headers.size; ++i) {
const newCell = dataRow.insertCell();
newCell.style.border = "1px solid black";
cells.push(newCell);
}
for (const [key, value] of Object.entries(obj)) {
cells[headers.get(key)].innerHTML = value.value;
}
statusOK("Finished query");
}
}).finally(() => {
freeformGo.disabled = false;
});
};
}