Add linting rules

This commit is contained in:
Zaid Abdulla committed 2018-12-27 01:21:16 -08:00
1 parent 75a5a42296
commit c702003c32
43 files changed
+705 -576

No files matched your search

+2 -2
View File
@@ -51,14 +51,14 @@ function pagify(pageName, page) {
if (!page.init) {
state = null;
fulfill();
return;
return null;
}
return new Promise(resolve => {
page
.init(args)
.then(st => {
if (!st) return m.route.set("/");
if (!st) return void m.route.set("/");
state = st;
resolve();
fulfill();
+8 -6
View File
@@ -20,7 +20,7 @@ class Autocomplete {
el.addEventListener("focus", () => {
this.element = el;
let domRect = el.getBoundingClientRect();
const domRect = el.getBoundingClientRect();
this.container.style.left = `${domRect.left}px`;
this.container.style.width = `${domRect.width}px`;
this.container.style.top = `${domRect.bottom}px`;
@@ -36,13 +36,15 @@ class Autocomplete {
el.addEventListener("keydown", e => {
if (this.element !== el) return;
if (e.key === "Escape") this.hide();
else if (e.key === "Enter")
if (e.key === "Escape") {
this.hide();
} else if (e.key === "Enter") {
if (this.default != null) {
el.value = this.default;
e.preventDefault();
this.update();
}
}
});
el.addEventListener("input", () => {
@@ -94,10 +96,10 @@ class Autocomplete {
this.default = suggestions[0];
for (let suggestion of suggestions) {
let e = document.createElement("div");
for (const suggestion of suggestions) {
const e = document.createElement("div");
e.className = "suggestion";
let t = document.createTextNode(suggestion);
const t = document.createTextNode(suggestion);
e.appendChild(t);
e.addEventListener("mousedown", ev => {
ev.preventDefault();
+10 -9
View File
@@ -12,7 +12,7 @@ const component = {
view: vnode => {
const device = vnode.attrs.device;
let search = m("input", {
const search = m("input", {
type: "text",
placeholder: "Search parameters",
oninput: e => {
@@ -20,10 +20,10 @@ const component = {
}
});
let instanceRegex = /\.[0-9]+$/;
const instanceRegex = /\.[0-9]+$/;
let re;
if (vnode.state.searchString) {
let keywords = vnode.state.searchString.split(" ").filter(s => s);
const keywords = vnode.state.searchString.split(" ").filter(s => s);
if (keywords.length)
re = new RegExp(keywords.map(s => escapeRegExp(s)).join(".*"), "i");
}
@@ -31,22 +31,22 @@ const component = {
const rows = Object.keys(device)
.sort()
.map(k => {
let p = device[k];
let val = [];
let attrs = {};
const p = device[k];
const val = [];
const attrs = {};
if (re) {
let str = p.value && p.value[0] ? `${k} ${p.value[0]}` : k;
const str = p.value && p.value[0] ? `${k} ${p.value[0]}` : k;
if (!re.test(str)) attrs.style = "display: none;";
}
if (p.object === false)
if (p.object === false) {
val.push(
m(
components.get("parameter"),
Object.assign({ device: device, parameter: k })
)
);
else if (p.object && p.writable)
} else if (p.object && p.writable) {
if (instanceRegex.test(k)) {
val.push(
m(
@@ -82,6 +82,7 @@ const component = {
)
);
}
}
val.push(
m(
+2 -2
View File
@@ -12,10 +12,10 @@ const component = {
view: vnode => {
if (vnode.attrs.filter) {
const filter = memoizedParse(vnode.attrs.filter);
if (!store.evaluateExpression(filter, vnode.attrs.device)) return;
if (!store.evaluateExpression(filter, vnode.attrs.device)) return null;
}
let children = Object.values(vnode.attrs.components).map(c => {
const children = Object.values(vnode.attrs.components).map(c => {
if (typeof c !== "object") return `${c}`;
const attrs = Object.assign({}, vnode.attrs, c);
return m(components.get(attrs.type), attrs);
+6 -3
View File
@@ -20,7 +20,7 @@ const component = {
const thead = m("thead", m("tr", headers));
const rows = [];
for (let f of faults.value)
for (const f of faults.value) {
rows.push([
m("td", f["channel"]),
m("td", f["code"]),
@@ -51,14 +51,17 @@ const component = {
)
)
]);
}
let tbody;
if (rows.length) tbody = m("tbody", rows.map(r => m("tr", r)));
else
if (rows.length) {
tbody = m("tbody", rows.map(r => m("tr", r)));
} else {
tbody = m(
"tbody",
m("tr.empty", m("td", { colspan: headers.length }, "No faults"))
);
}
return m("table.table", thead, tbody);
}
+2 -1
View File
@@ -14,7 +14,7 @@ const component = {
view: vnode => {
const device = vnode.attrs.device;
const chart = CHARTS[vnode.attrs.chart];
for (let slice of Object.values(chart.slices)) {
for (const slice of Object.values(chart.slices)) {
const filter = memoizedParse(slice.filter);
if (store.evaluateExpression(filter, device)) {
const dot = m(
@@ -35,6 +35,7 @@ const component = {
return m("span.overview-dot", dot, `${slice.label}`);
}
}
return null;
}
};
+11 -7
View File
@@ -9,7 +9,7 @@ import * as expressionParser from "../../common/expression-parser";
const component = {
oninit: vnode => {
let obj = expression.parse(vnode.attrs.parameter);
const obj = expression.parse(vnode.attrs.parameter);
if (!Array.isArray(obj) || !obj[0] === "PARAM")
throw new Error("Object must be a parameter path");
vnode.state.object = obj[1];
@@ -30,19 +30,20 @@ const component = {
const instances = new Set();
const prefix = `${object}.`;
for (let p in device)
for (const p in device) {
if (p.startsWith(prefix)) {
let i = p.indexOf(".", prefix.length);
const i = p.indexOf(".", prefix.length);
if (i === -1) instances.add(p);
else instances.add(p.slice(0, i));
}
}
const headers = Object.values(parameters).map(p => m("th", p.label));
const thead = m("thead", m("tr", headers));
const rows = [];
for (let i of instances) {
for (const i of instances) {
const row = parameters.map(p => {
const param = expressionParser.map(p.parameter, e => {
if (Array.isArray(e) && e[0] === "PARAM")
@@ -62,7 +63,7 @@ const component = {
);
});
if (device[i].writable === true)
if (device[i].writable === true) {
row.push(
m(
"td",
@@ -82,15 +83,17 @@ const component = {
)
)
);
}
rows.push(m("tr", row));
}
if (!rows.length)
if (!rows.length) {
rows.push(
m("tr.empty", m("td", { colspan: headers.length }, "No instances"))
);
}
if (device[object].writable === true)
if (device[object].writable === true) {
rows.push(
m(
"tr",
@@ -114,6 +117,7 @@ const component = {
)
)
);
}
let label;
if (vnode.attrs.label) label = m("h2", vnode.attrs.label);
+16 -20
View File
@@ -11,27 +11,22 @@ const memoizedParse = memoize(expression.parse);
const evaluateParam = memoize((exp, obj, now) => {
let timestamp = now;
let params = new Set();
let value = expression.evaluate(
memoizedParse(exp),
obj,
now,
e => {
if (Array.isArray(e)) {
if (e[0] === "PARAM") {
params.add(e[1]);
if (!Array.isArray(e[1])) {
let p = obj[e[1]];
if (p && p.valueTimestamp)
timestamp = Math.min(timestamp, p.valueTimestamp);
}
const params = new Set();
const value = expression.evaluate(memoizedParse(exp), obj, now, e => {
if (Array.isArray(e)) {
if (e[0] === "PARAM") {
params.add(e[1]);
if (!Array.isArray(e[1])) {
const p = obj[e[1]];
if (p && p.valueTimestamp)
timestamp = Math.min(timestamp, p.valueTimestamp);
}
if (e[0] === "FUNC" && e[1] === "DATE_STRING" && !Array.isArray(e[2]))
return new Date(e[2]).toLocaleString();
}
return e;
if (e[0] === "FUNC" && e[1] === "DATE_STRING" && !Array.isArray(e[2]))
return new Date(e[2]).toLocaleString();
}
);
return e;
});
let parameter = params.size === 1 ? params.values().next().value : null;
if (Array.isArray(parameter)) parameter = null;
@@ -42,7 +37,7 @@ const component = {
view: vnode => {
const device = vnode.attrs.device;
let { value, timestamp, parameter } = evaluateParam(
const { value, timestamp, parameter } = evaluateParam(
vnode.attrs.parameter,
vnode.attrs.device,
store.getTimestamp()
@@ -51,7 +46,7 @@ const component = {
if (value == null) return null;
let edit;
if (device[parameter] && device[parameter].writable)
if (device[parameter] && device[parameter].writable) {
edit = m(
"button",
{
@@ -72,6 +67,7 @@ const component = {
},
"✎"
);
}
return m(
"span.parameter-value",
+5 -3
View File
@@ -13,7 +13,7 @@ const component = {
vnode.state.timeout = null;
},
view: vnode => {
let refresh = () => {
const refresh = () => {
const device = vnode.attrs.device;
let param =
device["InternetGatewayDevice.ManagementServer.ConnectionRequestURL"];
@@ -34,20 +34,22 @@ const component = {
});
};
let t = vnode.state.timestamp + REFRESH_INTERVAL - Date.now();
const t = vnode.state.timestamp + REFRESH_INTERVAL - Date.now();
clearTimeout(vnode.state.timeout);
if (t <= 0) refresh();
else vnode.state.timeout = setTimeout(refresh, t);
if (vnode.state.host) {
let status = "";
if (vnode.state.ping)
if (vnode.state.ping) {
if (vnode.state.ping.avg != null)
status = `${Math.trunc(vnode.state.ping.avg)} ms`;
else status = "Unreachable";
}
return m("div", `Pinging ${vnode.state.host}: ${status}`);
}
return null;
}
};
+17 -15
View File
@@ -12,8 +12,8 @@ const singular = {
function createField(current, attr, focus) {
if (attr.type === "combo") {
let options = [m("option", { value: "" }, "--Please choose--")];
for (let op of attr.options) options.push(m("option", { value: op }, op));
const options = [m("option", { value: "" }, "--Please choose--")];
for (const op of attr.options) options.push(m("option", { value: op }, op));
let selected = "";
if (attr.options.includes(current.object[attr.id]))
@@ -40,7 +40,7 @@ function createField(current, attr, focus) {
name: attr.id,
value: current.object[attr.id],
oncreate: _vnode => {
let editor = codeMirror.fromTextArea(_vnode.dom, {
const editor = codeMirror.fromTextArea(_vnode.dom, {
mode: "javascript",
lineNumbers: true
});
@@ -87,21 +87,22 @@ function createField(current, attr, focus) {
const component = {
view: vnode => {
let actionHandler = vnode.attrs.actionHandler;
let attributes = vnode.attrs.attributes;
let resource = vnode.attrs.resource;
let base = vnode.attrs.base || {};
if (!vnode.state.current)
const actionHandler = vnode.attrs.actionHandler;
const attributes = vnode.attrs.attributes;
const resource = vnode.attrs.resource;
const base = vnode.attrs.base || {};
if (!vnode.state.current) {
vnode.state.current = {
isNew: !base["_id"],
object: Object.assign({}, base)
};
}
let current = vnode.state.current;
const current = vnode.state.current;
let form = [];
const form = [];
let focused = false;
for (let attr of attributes) {
for (const attr of attributes) {
let focus = false;
if (!focused && (current.isNew || attr.id !== "_id"))
focus = focused = true;
@@ -117,9 +118,9 @@ const component = {
}
const submit = m("button.primary", { type: "submit" }, "Save");
let buttons = [submit];
const buttons = [submit];
if (!current.isNew)
if (!current.isNew) {
buttons.push(
m(
"button.primary",
@@ -135,10 +136,11 @@ const component = {
"Delete"
)
);
}
form.push(m(".actions-bar", buttons));
let children = [
const children = [
m("h1", `${current.isNew ? "New" : "Editing"} ${singular[resource]}`),
m(
"form",
@@ -147,7 +149,7 @@ const component = {
e.redraw = false;
e.target.onsubmit = null;
e.preventDefault();
for (let elem of e.target.elements) elem.disabled = true;
for (const elem of e.target.elements) elem.disabled = true;
submit.dom.textContent = "Loading ...";
actionHandler("save", current.object);
}
+11 -10
View File
@@ -37,28 +37,29 @@ const component = {
.commit(
[task],
(deviceId, err, connectionRequestStatus, tasks2) => {
if (err)
return notifications.push(
"error",
`${deviceId}: ${err.message}`
);
if (err) {
notifications.push("error", `${deviceId}: ${err.message}`);
return;
}
for (let t of tasks2)
for (const t of tasks2)
if (t.status === "stale") taskQueue.deleteTask(t);
if (connectionRequestStatus !== "OK")
if (connectionRequestStatus !== "OK") {
notifications.push(
"error",
`${deviceId}: ${connectionRequestStatus}`
);
else if (tasks2[0].status === "stale")
} else if (tasks2[0].status === "stale") {
notifications.push(
"error",
`${deviceId}: No contact from device`
);
else if (tasks2[0].status === "fault")
} else if (tasks2[0].status === "fault") {
notifications.push("error", `${deviceId}: Refresh faulted`);
else notifications.push("success", `${deviceId}: Summoned`);
} else {
notifications.push("success", `${deviceId}: Summoned`);
}
}
)
.then(() => {
+1 -1
View File
@@ -11,7 +11,7 @@ const component = {
if (vnode.attrs.writable != null) writable = vnode.attrs.writable;
const tags = [];
for (let p of Object.keys(device))
for (const p of Object.keys(device))
if (p.startsWith("Tags.")) tags.push(p.slice(5));
tags.sort();
+5 -3
View File
@@ -7,10 +7,11 @@ import * as store from "./store";
import * as components from "./components";
function init(args) {
if (!window.authorizer.hasAccess("devices", 2))
if (!window.authorizer.hasAccess("devices", 2)) {
return Promise.reject(
new Error("You are not authorized to view this page")
);
}
return Promise.resolve({
deviceId: args.id,
@@ -22,15 +23,16 @@ const component = {
view: vnode => {
document.title = `${vnode.attrs.deviceId} - Devices - GenieACS`;
let dev = store.fetch("devices", vnode.attrs.deviceFilter).value;
const dev = store.fetch("devices", vnode.attrs.deviceFilter).value;
if (!dev.length) return "Loading";
const conf = config.ui.device;
const cmps = [];
for (let c of Object.values(conf))
for (const c of Object.values(conf)) {
cmps.push(
m(components.get(c["type"]), Object.assign({ device: dev[0] }, c))
);
}
return [
m("h1", vnode.attrs.deviceId),
+34 -29
View File
@@ -18,11 +18,9 @@ const PAGE_SIZE = config.ui.pageSize || 10;
const memoizedParse = memoize(expression.parse);
const memoizedJsonParse = memoize(JSON.parse);
const memoizedGetSortable = memoize(str => {
let expressionParams = expression.extractParams(
expression.parse(str)
);
const expressionParams = expression.extractParams(expression.parse(str));
if (expressionParams.length === 1) {
let param = expression.evaluate(expressionParams[0]);
const param = expression.evaluate(expressionParams[0]);
if (typeof param === "string") return param;
}
return null;
@@ -52,7 +50,7 @@ const unpackSmartQuery = memoize(query => {
function init(args) {
return new Promise((resolve, reject) => {
if (!window.authorizer.hasAccess("devices", 2))
return reject(new Error("You are not authorized to view this page"));
return void reject(new Error("You are not authorized to view this page"));
const filter = args.filter;
const sort = args.sort;
@@ -76,16 +74,17 @@ function renderTable(
type: "checkbox",
checked: devices.length && selected.size === devices.length,
onchange: e => {
for (let d of devices)
for (const d of devices) {
if (e.target.checked) selected.add(d["DeviceID.ID"].value[0]);
else selected.delete(d["DeviceID.ID"].value[0]);
}
},
disabled: !total
});
let labels = [m("th", selectAll)];
for (let param of parameters) {
let label = param.label;
const labels = [m("th", selectAll)];
for (const param of parameters) {
const label = param.label;
let _param;
if (!param.unsortable && (_param = memoizedGetSortable(param.parameter))) {
let direction = 1;
@@ -94,7 +93,7 @@ function renderTable(
if (sort[_param] > 0) symbol = "\u2bc6";
else if (sort[_param] < 0) symbol = "\u2bc5";
let sortable = m(
const sortable = m(
"button",
{
onclick: () => {
@@ -111,9 +110,9 @@ function renderTable(
}
}
let rows = [];
for (let device of devices) {
let checkbox = m("input", {
const rows = [];
for (const device of devices) {
const checkbox = m("input", {
type: "checkbox",
checked: selected.has(device["DeviceID.ID"].value[0]),
onchange: e => {
@@ -158,12 +157,13 @@ function renderTable(
);
}
if (!rows.length)
if (!rows.length) {
rows.push(
m("tr.empty", m("td", { colspan: parameters.length + 1 }, "No devices"))
);
}
let footerElements = [];
const footerElements = [];
if (total != null) footerElements.push(`${devices.length}/${total}`);
else footerElements.push(`${devices.length}`);
@@ -179,12 +179,13 @@ function renderTable(
)
);
if (downloadUrl)
if (downloadUrl) {
footerElements.push(
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
);
}
let tfoot = m(
const tfoot = m(
"tfoot",
m("tr", m("td", { colspan: labels.length }, footerElements))
);
@@ -206,11 +207,12 @@ function renderActions(selected) {
title: "Reboot selected devices",
disabled: !selected.size,
onclick: () => {
for (let d of selected)
for (const d of selected) {
taskQueue.queueTask({
name: "reboot",
device: d
});
}
}
},
"Reboot"
@@ -224,11 +226,12 @@ function renderActions(selected) {
title: "Factory reset selected devices",
disabled: !selected.size,
onclick: () => {
for (let d of selected)
for (const d of selected) {
taskQueue.queueTask({
name: "factoryReset",
device: d
});
}
}
},
"Reset"
@@ -246,7 +249,7 @@ function renderActions(selected) {
if (!confirm(`Deleting ${ids.length} devices. Are you sure?`)) return;
let counter = 1;
for (let id of ids) {
for (const id of ids) {
++counter;
store
.deleteResource("devices", id)
@@ -278,7 +281,7 @@ function renderActions(selected) {
if (!tag) return;
let counter = 1;
for (let id of ids) {
for (const id of ids) {
++counter;
store
.updateTags(id, { [tag]: true })
@@ -312,7 +315,7 @@ function renderActions(selected) {
if (!tag) return;
let counter = 1;
for (let id of ids) {
for (const id of ids) {
++counter;
store
.updateTags(id, { [tag]: false })
@@ -345,13 +348,13 @@ const component = {
}
function onFilterChanged(filter) {
let ops = { filter };
const ops = { filter };
if (vnode.attrs.sort) ops.sort = vnode.attrs.sort;
m.route.set("/devices", ops);
}
function onSortChange(sort) {
let ops = { sort };
const ops = { sort };
if (vnode.attrs.filter) ops.filter = vnode.attrs.filter;
m.route.set("/devices", ops);
}
@@ -360,17 +363,19 @@ const component = {
let filter = vnode.attrs.filter ? memoizedParse(vnode.attrs.filter) : true;
filter = unpackSmartQuery(filter);
let devs = store.fetch("devices", filter, {
const devs = store.fetch("devices", filter, {
limit: vnode.state.showCount || PAGE_SIZE,
sort: sort
});
let count = store.count("devices", filter);
const count = store.count("devices", filter);
let selected = new Set();
if (vnode.state.selected)
for (let d of devs.value)
const selected = new Set();
if (vnode.state.selected) {
for (const d of devs.value) {
if (vnode.state.selected.has(d["DeviceID.ID"].value[0]))
selected.add(d["DeviceID.ID"].value[0]);
}
}
vnode.state.selected = selected;
const downloadUrl = getDownloadUrl(
+42 -34
View File
@@ -89,7 +89,7 @@ function renderStagingDownload(task) {
onclick: () => {
task.fileName = f;
task.fileType = "";
for (let file of files.value)
for (const file of files.value)
if (file._id === f) task.fileType = file["metadata.fileType"];
}
},
@@ -110,8 +110,8 @@ function renderStagingDownload(task) {
}
function renderStaging(staging) {
let elements = [];
for (let s of staging) {
const elements = [];
for (const s of staging) {
const queueFunc = () => {
staging.delete(s);
taskQueue.queueTask(s);
@@ -143,19 +143,19 @@ function renderStaging(staging) {
}
function renderQueue(queue) {
let details = [];
let devices = {};
for (let t of queue) {
const details = [];
const devices = {};
for (const t of queue) {
devices[t.device] = devices[t.device] || [];
devices[t.device].push(t);
}
for (let [k, v] of Object.entries(devices)) {
for (const [k, v] of Object.entries(devices)) {
details.push(m("strong", k));
for (let t of v) {
let actions = [];
for (const t of v) {
const actions = [];
if (t.status === "fault" || t.status === "stale")
if (t.status === "fault" || t.status === "stale") {
actions.push(
m(
"button",
@@ -168,6 +168,7 @@ function renderQueue(queue) {
"↺"
)
);
}
actions.push(
m(
@@ -182,7 +183,7 @@ function renderQueue(queue) {
)
);
if (t.name === "setParameterValues")
if (t.name === "setParameterValues") {
details.push(
m(
`div.${t.status}`,
@@ -197,7 +198,7 @@ function renderQueue(queue) {
m(".actions", actions)
)
);
else if (t.name === "refreshObject")
} else if (t.name === "refreshObject") {
details.push(
m(
`div.${t.status}`,
@@ -205,13 +206,13 @@ function renderQueue(queue) {
m(".actions", actions)
)
);
else if (t.name === "reboot")
} else if (t.name === "reboot") {
details.push(m(`div.${t.status}`, "Reboot", m(".actions", actions)));
else if (t.name === "factoryReset")
} else if (t.name === "factoryReset") {
details.push(
m(`div.${t.status}`, "Factory reset", m(".actions", actions))
);
else if (t.name === "addObject")
} else if (t.name === "addObject") {
details.push(
m(
`div.${t.status}`,
@@ -219,7 +220,7 @@ function renderQueue(queue) {
m(".actions", actions)
)
);
else if (t.name === "deleteObject")
} else if (t.name === "deleteObject") {
details.push(
m(
`div.${t.status}`,
@@ -227,7 +228,7 @@ function renderQueue(queue) {
m(".actions", actions)
)
);
else if (t.name === "getParameterValues")
} else if (t.name === "getParameterValues") {
details.push(
m(
`div.${t.status}`,
@@ -235,7 +236,7 @@ function renderQueue(queue) {
m(".actions", actions)
)
);
else if (t.name === "download")
} else if (t.name === "download") {
details.push(
m(
`div.${t.status}`,
@@ -243,7 +244,9 @@ function renderQueue(queue) {
m(".actions", actions)
)
);
else details.push(m(`div.${t.status}`, t.name, m(".actions", actions)));
} else {
details.push(m(`div.${t.status}`, t.name, m(".actions", actions)));
}
}
}
@@ -253,7 +256,7 @@ function renderQueue(queue) {
function renderNotifications(notifs) {
const notificationElements = [];
for (let n of notifs)
for (const n of notifs) {
notificationElements.push(
m(
"div.notification",
@@ -276,6 +279,7 @@ function renderNotifications(notifs) {
n.message
)
);
}
return notificationElements;
}
@@ -286,13 +290,13 @@ const component = {
const notifs = notifications.getNotifications();
let drawerElement, statusElement;
let notificationElements = renderNotifications(notifs);
let stagingElements = renderStaging(staging);
let queueElements = renderQueue(queue);
const notificationElements = renderNotifications(notifs);
const stagingElements = renderStaging(staging);
const queueElements = renderQueue(queue);
function repositionNotifications() {
let top = 10;
for (let c of notificationElements) {
for (const c of notificationElements) {
c.dom.style.top = top;
top += c.dom.offsetHeight + 10;
}
@@ -300,20 +304,21 @@ const component = {
function resizeDrawer() {
let height = statusElement.dom.offsetTop + statusElement.dom.offsetHeight;
if (stagingElements.length)
for (let s of stagingElements)
if (stagingElements.length) {
for (const s of stagingElements)
height = Math.max(height, s.dom.offsetTop + s.dom.offsetHeight);
else if (vnode.state.mouseIn)
for (let c of drawerElement.children)
} else if (vnode.state.mouseIn) {
for (const c of drawerElement.children)
height = Math.max(height, c.dom.offsetTop + c.dom.offsetHeight);
}
drawerElement.dom.style.height = height;
}
if (stagingElements.length + queueElements.length) {
const statusCount = { queued: 0, pending: 0, fault: 0, stale: 0 };
for (let t of queue) statusCount[t.status] += 1;
for (const t of queue) statusCount[t.status] += 1;
let actions = m(
const actions = m(
".actions",
m(
"button.primary",
@@ -321,18 +326,20 @@ const component = {
title: "Commit queued tasks",
disabled: !statusCount.queued,
onclick: () => {
let tasks = Array.from(taskQueue.getQueue()).filter(
const tasks = Array.from(taskQueue.getQueue()).filter(
t => t.status === "queued"
);
taskQueue
.commit(
tasks,
(deviceId, err, connectionRequestStatus, tasks2) => {
if (err)
return notifications.push(
if (err) {
notifications.push(
"error",
`${deviceId}: ${err.message}`
);
return;
}
if (connectionRequestStatus !== "OK") {
notifications.push(
@@ -342,7 +349,7 @@ const component = {
return;
}
for (let t of tasks2)
for (const t of tasks2) {
if (t.status === "stale") {
notifications.push(
"error",
@@ -356,6 +363,7 @@ const component = {
);
return;
}
}
notifications.push(
"success",
+28 -22
View File
@@ -24,10 +24,11 @@ const attributes = [
];
const getDownloadUrl = memoize(filter => {
let cols = {};
for (let attr of attributes)
const cols = {};
for (const attr of attributes) {
cols[attr.label] =
attr.id === "timestamp" ? `DATE_STRING(${attr.id})` : attr.id;
}
return `/api/faults.csv?${m.buildQueryString({
filter: filter,
@@ -44,10 +45,11 @@ const unpackSmartQuery = memoize(query => {
});
function init(args) {
if (!window.authorizer.hasAccess("faults", 2))
if (!window.authorizer.hasAccess("faults", 2)) {
return Promise.reject(
new Error("You are not authorized to view this page")
);
}
const sort = args.sort;
const filter = args.filter;
@@ -68,16 +70,17 @@ function renderTable(
type: "checkbox",
checked: faults.length && selected.size === faults.length,
onchange: e => {
for (let f of faults)
for (const f of faults) {
if (e.target.checked) selected.add(f["_id"]);
else selected.delete(f["_id"]);
}
},
disabled: !total
});
const labels = [m("th", selectAll)];
for (let attr of attributes) {
let label = attr.label;
for (const attr of attributes) {
const label = attr.label;
let direction = 1;
@@ -85,7 +88,7 @@ function renderTable(
if (sort[attr.id] > 0) symbol = "\u2bc6";
else if (sort[attr.id] < 0) symbol = "\u2bc5";
let sortable = m(
const sortable = m(
"button",
{
onclick: () => {
@@ -99,9 +102,9 @@ function renderTable(
labels.push(m("th", [label, sortable]));
}
let rows = [];
for (let f of faults) {
let checkbox = m("input", {
const rows = [];
for (const f of faults) {
const checkbox = m("input", {
type: "checkbox",
checked: selected.has(f["_id"]),
onchange: e => {
@@ -116,13 +119,14 @@ function renderTable(
const deviceHref = `#!/devices/${encodeURIComponent(f["device"])}`;
let tds = [m("td", checkbox)];
for (let attr of attributes)
const tds = [m("td", checkbox)];
for (const attr of attributes) {
if (attr.id === "device")
tds.push(m("a", { href: deviceHref }, f[attr.id]));
else if (attr.id === "timestamp")
tds.push(m("td", new Date(f[attr.id]).toLocaleString()));
else tds.push(m("td", f[attr.id]));
}
rows.push(
m(
@@ -145,7 +149,7 @@ function renderTable(
if (!rows.length)
rows.push(m("tr.empty", m("td", { colspan: 7 }, "No faults")));
let footerElements = [];
const footerElements = [];
if (total != null) footerElements.push(`${faults.length}/${total}`);
else footerElements.push(`${faults.length}`);
@@ -161,12 +165,13 @@ function renderTable(
)
);
if (downloadUrl)
if (downloadUrl) {
footerElements.push(
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
);
}
let tfoot = m(
const tfoot = m(
"tfoot",
m("tr", m("td", { colspan: labels.length }, footerElements))
);
@@ -217,13 +222,13 @@ const component = {
}
function onFilterChanged(filter) {
let ops = { filter };
const ops = { filter };
if (vnode.attrs.sort) ops.sort = vnode.attrs.sort;
m.route.set("/faults", ops);
}
function onSortChange(sort) {
let ops = { sort };
const ops = { sort };
if (vnode.attrs.filter) ops.filter = vnode.attrs.filter;
m.route.set("/faults", ops);
}
@@ -232,16 +237,17 @@ const component = {
let filter = vnode.attrs.filter ? memoizedParse(vnode.attrs.filter) : true;
filter = unpackSmartQuery(filter);
let faults = store.fetch("faults", filter, {
const faults = store.fetch("faults", filter, {
limit: vnode.state.showCount || PAGE_SIZE,
sort: sort
});
let count = store.count("faults", filter);
const count = store.count("faults", filter);
let selected = new Set();
if (vnode.state.selected)
for (let f of faults.value)
const selected = new Set();
if (vnode.state.selected) {
for (const f of faults.value)
if (vnode.state.selected.has(f["_id"])) selected.add(f["_id"]);
}
vnode.state.selected = selected;
const downloadUrl = getDownloadUrl(vnode.attrs.filter);
+37 -33
View File
@@ -37,7 +37,7 @@ const attributes = [
{ id: "metadata.version", label: "Version" }
];
let formData = {
const formData = {
resource: "files",
attributes: attributes
.slice(1) // remove _id from new object form
@@ -55,21 +55,21 @@ const unpackSmartQuery = memoize(query => {
function putActionHandler(action, object) {
return new Promise((resolve, reject) => {
if (action === "save") {
let file = object["file"] ? object["file"][0] : null;
const file = object["file"] ? object["file"][0] : null;
delete object["file"];
if (!file) return reject(new Error("File not selected"));
if (!file) return void reject(new Error("File not selected"));
let id = file.name;
const id = file.name;
store
.resourceExists("files", id)
.then(exists => {
if (exists) {
store.fulfill(0, Date.now());
return reject(new Error("File already exists"));
return void reject(new Error("File already exists"));
}
let headers = Object.assign(
const headers = Object.assign(
{
"Content-Type": "application/octet-stream",
Accept: "application/octet-stream"
@@ -81,9 +81,7 @@ function putActionHandler(action, object) {
method: "PUT",
headers: headers,
url: `/api/files/${encodeURIComponent(id)}`,
serialize: body => {
return body; // Identity function to prevent JSON.parse on blob data
},
serialize: body => body, // Identity function to prevent JSON.parse on blob data
data: file
})
.then(() => {
@@ -104,8 +102,8 @@ function putActionHandler(action, object) {
}
const getDownloadUrl = memoize(filter => {
let cols = {};
for (let attr of attributes) cols[attr.label] = attr.id;
const cols = {};
for (const attr of attributes) cols[attr.label] = attr.id;
return `/api/files.csv?${m.buildQueryString({
filter: filter,
columns: JSON.stringify(cols)
@@ -113,10 +111,11 @@ const getDownloadUrl = memoize(filter => {
});
function init(args) {
if (!window.authorizer.hasAccess("files", 2))
if (!window.authorizer.hasAccess("files", 2)) {
return Promise.reject(
new Error("You are not authorized to view this page")
);
}
const sort = args.sort;
const filter = args.filter;
@@ -137,16 +136,17 @@ function renderTable(
type: "checkbox",
checked: files.length && selected.size === files.length,
onchange: e => {
for (let file of files)
for (const file of files) {
if (e.target.checked) selected.add(file["_id"]);
else selected.delete(file["_id"]);
}
},
disabled: !total
});
const labels = [m("th", selectAll)];
for (let attr of attributes) {
let label = attr.label;
for (const attr of attributes) {
const label = attr.label;
let direction = 1;
@@ -154,7 +154,7 @@ function renderTable(
if (sort[attr.id] > 0) symbol = "\u2bc6";
else if (sort[attr.id] < 0) symbol = "\u2bc5";
let sortable = m(
const sortable = m(
"button",
{
onclick: () => {
@@ -168,9 +168,9 @@ function renderTable(
labels.push(m("th", [label, sortable]));
}
let rows = [];
for (let file of files) {
let checkbox = m("input", {
const rows = [];
for (const file of files) {
const checkbox = m("input", {
type: "checkbox",
checked: selected.has(file["_id"]),
onchange: e => {
@@ -183,11 +183,12 @@ function renderTable(
}
});
let tds = [m("td", checkbox)];
for (let attr of attributes)
const tds = [m("td", checkbox)];
for (const attr of attributes) {
if (attr.id == "script")
tds.push(m("td", { title: file[attr.id] }, file[attr.id]));
else tds.push(m("td", file[attr.id]));
}
rows.push(
m(
@@ -210,7 +211,7 @@ function renderTable(
if (!rows.length)
rows.push(m("tr.empty", m("td", { colspan: labels.length }, "No files")));
let footerElements = [];
const footerElements = [];
if (total != null) footerElements.push(`${files.length}/${total}`);
else footerElements.push(`${files.length}`);
@@ -226,12 +227,13 @@ function renderTable(
)
);
if (downloadUrl)
if (downloadUrl) {
footerElements.push(
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
);
}
let tfoot = m(
const tfoot = m(
"tfoot",
m("tr", m("td", { colspan: labels.length }, footerElements))
);
@@ -261,14 +263,14 @@ function renderTable(
)
];
if (window.authorizer.hasAccess("files", 3))
if (window.authorizer.hasAccess("files", 3)) {
buttons.push(
m(
"button.primary",
{
title: "Create new file",
onclick: () => {
let cb = () => {
const cb = () => {
return m(
putForm,
Object.assign(
@@ -295,6 +297,7 @@ function renderTable(
"New"
)
);
}
return [
m(
@@ -317,13 +320,13 @@ const component = {
}
function onFilterChanged(filter) {
let ops = { filter };
const ops = { filter };
if (vnode.attrs.sort) ops.sort = vnode.attrs.sort;
m.route.set("/files", ops);
}
function onSortChange(sort) {
let ops = { sort };
const ops = { sort };
if (vnode.attrs.filter) ops.filter = vnode.attrs.filter;
m.route.set("/files", ops);
}
@@ -332,17 +335,18 @@ const component = {
let filter = vnode.attrs.filter ? memoizedParse(vnode.attrs.filter) : true;
filter = unpackSmartQuery(filter);
let files = store.fetch("files", filter, {
const files = store.fetch("files", filter, {
limit: vnode.state.showCount || PAGE_SIZE,
sort: sort
});
let count = store.count("files", filter);
const count = store.count("files", filter);
let selected = new Set();
if (vnode.state.selected)
for (let file of files.value)
const selected = new Set();
if (vnode.state.selected) {
for (const file of files.value)
if (vnode.state.selected.has(file["_id"])) selected.add(file["_id"]);
}
vnode.state.selected = selected;
const downloadUrl = getDownloadUrl(vnode.attrs.filter);
+4 -3
View File
@@ -17,8 +17,8 @@ const getAutocomplete = memoize(resource => {
function parseFilter(f) {
if (/^[\s0-9a-zA-Z]+:/.test(f)) {
let k = f.split(":", 1)[0];
let v = f.slice(k.length + 1).trim();
const k = f.split(":", 1)[0];
const v = f.slice(k.length + 1).trim();
return ["FUNC", "Q", k.trim(), v];
}
return expression.parse(f);
@@ -35,7 +35,7 @@ const splitFilter = memoize(filter => {
const list = [];
const f = expression.parse(filter);
if (Array.isArray(f) && f[0] === "AND")
for (let ff of f.slice(1)) list.push(stringifyFilter(ff));
for (const ff of f.slice(1)) list.push(stringifyFilter(ff));
else list.push(stringifyFilter(f));
list.push("");
@@ -59,6 +59,7 @@ const component = {
} catch (err) {
vnode.state.filterInvalid |= 1 << idx;
}
return null;
});
vnode.state.filterList.push("");
+15 -8
View File
@@ -6,8 +6,8 @@ const menu = {
view: vnode => {
const active = { [vnode.attrs.page]: "active" };
let tabs = [];
if (window.authorizer.hasAccess("devices", 1))
const tabs = [];
if (window.authorizer.hasAccess("devices", 1)) {
tabs.push(
m(
"li",
@@ -15,8 +15,9 @@ const menu = {
m("a", { href: "#!/overview" }, "Overview")
)
);
}
if (window.authorizer.hasAccess("devices", 2))
if (window.authorizer.hasAccess("devices", 2)) {
tabs.push(
m(
"li",
@@ -24,8 +25,9 @@ const menu = {
m("a", { href: "#!/devices" }, "Devices")
)
);
}
if (window.authorizer.hasAccess("faults", 2))
if (window.authorizer.hasAccess("faults", 2)) {
tabs.push(
m(
"li",
@@ -33,8 +35,9 @@ const menu = {
m("a", { href: "#!/faults" }, "Faults")
)
);
}
if (window.authorizer.hasAccess("presets", 2))
if (window.authorizer.hasAccess("presets", 2)) {
tabs.push(
m(
"li",
@@ -42,8 +45,9 @@ const menu = {
m("a", { href: "#!/presets" }, "Presets")
)
);
}
if (window.authorizer.hasAccess("provisions", 2))
if (window.authorizer.hasAccess("provisions", 2)) {
tabs.push(
m(
"li",
@@ -51,8 +55,9 @@ const menu = {
m("a", { href: "#!/provisions" }, "Provisions")
)
);
}
if (window.authorizer.hasAccess("virtualParameters", 2))
if (window.authorizer.hasAccess("virtualParameters", 2)) {
tabs.push(
m(
"li",
@@ -60,8 +65,9 @@ const menu = {
m("a", { href: "#!/virtualParameters" }, "Virtual Parameters")
)
);
}
if (window.authorizer.hasAccess("files", 2))
if (window.authorizer.hasAccess("files", 2)) {
tabs.push(
m(
"li",
@@ -69,6 +75,7 @@ const menu = {
m("a", { href: "#!/files" }, "Files")
)
);
}
return m("nav", m("ul", tabs));
}
+2 -1
View File
@@ -18,7 +18,7 @@ function close(callback) {
}
function render() {
if (overlayCallback)
if (overlayCallback) {
return m(
".overlay-wrapper",
{
@@ -50,6 +50,7 @@ function render() {
overlayCallback()
)
);
}
return null;
}
+10 -8
View File
@@ -11,9 +11,10 @@ const memoizedParse = memoize(expression.parse);
const GROUPS = config.ui.overview.groups;
const CHARTS = {};
for (let group of Object.values(GROUPS))
for (let chartName of Object.values(group["charts"]))
for (const group of Object.values(GROUPS)) {
for (const chartName of Object.values(group["charts"]))
CHARTS[chartName] = config.ui.overview.charts[chartName];
}
function queryCharts(charts) {
charts = Object.assign({}, charts);
@@ -30,10 +31,11 @@ function queryCharts(charts) {
}
const init = function() {
if (!window.authorizer.hasAccess("devices", 1))
if (!window.authorizer.hasAccess("devices", 1)) {
return Promise.reject(
new Error("You are not authorized to view this page")
);
}
return Promise.resolve({ charts: queryCharts(CHARTS) });
};
@@ -41,14 +43,14 @@ const init = function() {
const component = {
view: vnode => {
document.title = "Overview - GenieACS";
let children = [];
for (let group of Object.values(GROUPS)) {
const children = [];
for (const group of Object.values(GROUPS)) {
if (group.label) children.push(m("h1", group.label));
let groupChildren = [];
for (let chartName of Object.values(group.charts)) {
const groupChildren = [];
for (const chartName of Object.values(group.charts)) {
const chart = vnode.attrs.charts[chartName];
let chartChildren = [];
const chartChildren = [];
if (chart.label) chartChildren.push(m("h2", chart.label));
chartChildren.push(m(pieChartComponent, { chart: chart }));
+11 -11
View File
@@ -3,21 +3,21 @@
import m from "mithril";
const drawChart = function(chartData) {
let slices = chartData.slices;
let total = Array.from(Object.values(chartData.slices)).reduce(
const slices = chartData.slices;
const total = Array.from(Object.values(chartData.slices)).reduce(
(a, s) => a + (s.count.value || 0),
0
);
let legend = [];
let paths = [];
let links = [];
const legend = [];
const paths = [];
const links = [];
let currentProgressPercentage = 0;
let startX = Math.cos(2 * Math.PI * currentProgressPercentage) * 100;
let startY = Math.sin(2 * Math.PI * currentProgressPercentage) * 100;
let endX, endY;
for (let slice of Object.values(slices)) {
let percent = total > 0 ? (slice.count.value || 0) / total : 0;
for (const slice of Object.values(slices)) {
const percent = total > 0 ? (slice.count.value || 0) / total : 0;
legend.push(
m(".legend-line", [
m("span.color", {
@@ -41,9 +41,9 @@ const drawChart = function(chartData) {
currentProgressPercentage += percent;
endX = Math.cos(2 * Math.PI * currentProgressPercentage) * 100;
endY = Math.sin(2 * Math.PI * currentProgressPercentage) * 100;
let isBigArc = percent > 0.5 ? 1 : 0;
const isBigArc = percent > 0.5 ? 1 : 0;
let sketch =
const sketch =
`M ${startX} ${startY} ` + // Move to the starting point
`A 100 100 0 ${isBigArc} 1 ${endX} ${endY} ` + // Draw an Arc from starting point to ending point
`L 0 0 z`; // complete the shape by drawing a line to the center of circle
@@ -58,9 +58,9 @@ const drawChart = function(chartData) {
})
);
let percentageX =
const percentageX =
Math.cos(2 * Math.PI * (currentProgressPercentage - percent / 2)) * 50;
let percentageY =
const percentageY =
Math.sin(2 * Math.PI * (currentProgressPercentage - percent / 2)) * 50;
links.push(
+42 -32
View File
@@ -39,10 +39,10 @@ const unpackSmartQuery = memoize(query => {
function putActionHandler(action, object, isNew) {
if (action === "save") {
let id = object["_id"];
const id = object["_id"];
delete object["_id"];
if (!id) return notifications.push("error", "ID can not be empty");
if (!id) return void notifications.push("error", "ID can not be empty");
store
.resourceExists("presets", id)
@@ -90,14 +90,14 @@ function putActionHandler(action, object, isNew) {
}
}
let formData = {
const formData = {
resource: "presets",
attributes: attributes
};
const getDownloadUrl = memoize(filter => {
let cols = {};
for (let attr of attributes) cols[attr.label] = attr.id;
const cols = {};
for (const attr of attributes) cols[attr.label] = attr.id;
return `/api/presets.csv?${m.buildQueryString({
filter: filter,
columns: JSON.stringify(cols)
@@ -105,10 +105,11 @@ const getDownloadUrl = memoize(filter => {
});
function init(args) {
if (!window.authorizer.hasAccess("presets", 2))
if (!window.authorizer.hasAccess("presets", 2)) {
return Promise.reject(
new Error("You are not authorized to view this page")
);
}
const sort = args.sort;
const filter = args.filter;
@@ -129,17 +130,18 @@ function renderTable(
type: "checkbox",
checked: presets.length && selected.size === presets.length,
onchange: e => {
for (let preset of presets)
for (const preset of presets) {
if (e.target.checked) selected.add(preset["_id"]);
else selected.delete(preset["_id"]);
}
},
disabled: !total
});
const labels = [m("th", selectAll)];
for (let attr of attributes) {
let label = attr.label;
for (const attr of attributes) {
const label = attr.label;
if (attr.unsortable) {
labels.push(m("th", label));
@@ -152,7 +154,7 @@ function renderTable(
if (sort[attr.id] > 0) symbol = "\u2bc6";
else if (sort[attr.id] < 0) symbol = "\u2bc5";
let sortable = m(
const sortable = m(
"button",
{
onclick: () => {
@@ -166,9 +168,9 @@ function renderTable(
labels.push(m("th", [label, sortable]));
}
let rows = [];
for (let preset of presets) {
let checkbox = m("input", {
const rows = [];
for (const preset of presets) {
const checkbox = m("input", {
type: "checkbox",
checked: selected.has(preset["_id"]),
onchange: e => {
@@ -182,14 +184,15 @@ function renderTable(
});
let devicesUrl = "/#!/devices";
if (preset["precondition"].length)
if (preset["precondition"].length) {
devicesUrl += `?${m.buildQueryString({
filter: preset["precondition"]
})}`;
}
let tds = [m("td", checkbox)];
for (let attr of attributes)
if (attr.id === "precondition")
const tds = [m("td", checkbox)];
for (const attr of attributes) {
if (attr.id === "precondition") {
tds.push(
m(
"td",
@@ -197,7 +200,10 @@ function renderTable(
m("a", { href: devicesUrl }, preset[attr.id])
)
);
else tds.push(m("td", preset[attr.id]));
} else {
tds.push(m("td", preset[attr.id]));
}
}
tds.push(
m(
@@ -206,7 +212,7 @@ function renderTable(
"a",
{
onclick: () => {
let cb = () => {
const cb = () => {
return m(
putForm,
Object.assign(
@@ -250,7 +256,7 @@ function renderTable(
if (!rows.length)
rows.push(m("tr.empty", m("td", { colspan: labels.length }, "No presets")));
let footerElements = [];
const footerElements = [];
if (total != null) footerElements.push(`${presets.length}/${total}`);
else footerElements.push(`${presets.length}`);
@@ -266,12 +272,13 @@ function renderTable(
)
);
if (downloadUrl)
if (downloadUrl) {
footerElements.push(
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
);
}
let tfoot = m(
const tfoot = m(
"tfoot",
m("tr", m("td", { colspan: labels.length }, footerElements))
);
@@ -301,14 +308,14 @@ function renderTable(
)
];
if (window.authorizer.hasAccess("presets", 3))
if (window.authorizer.hasAccess("presets", 3)) {
buttons.push(
m(
"button.primary",
{
title: "Create new preset",
onclick: () => {
let cb = () => {
const cb = () => {
return m(
putForm,
Object.assign(
@@ -328,6 +335,7 @@ function renderTable(
"New"
)
);
}
return [
m(
@@ -350,13 +358,13 @@ const component = {
}
function onFilterChanged(filter) {
let ops = { filter };
const ops = { filter };
if (vnode.attrs.sort) ops.sort = vnode.attrs.sort;
m.route.set("/presets", ops);
}
function onSortChange(sort) {
let ops = { sort };
const ops = { sort };
if (vnode.attrs.filter) ops.filter = vnode.attrs.filter;
m.route.set("/presets", ops);
}
@@ -365,15 +373,15 @@ const component = {
let filter = vnode.attrs.filter ? memoizedParse(vnode.attrs.filter) : true;
filter = unpackSmartQuery(filter);
let presets = store.fetch("presets", filter, {
const presets = store.fetch("presets", filter, {
limit: vnode.state.showCount || PAGE_SIZE,
sort: sort
});
let count = store.count("presets", filter);
const count = store.count("presets", filter);
const provisions = store.fetch("provisions", true);
if (provisions.fulfilled) {
let provisionAttr = attributes.find(attr => {
const provisionAttr = attributes.find(attr => {
return attr.id === "provision";
});
provisionAttr.options = provisions.value.map(v => {
@@ -381,11 +389,13 @@ const component = {
});
}
let selected = new Set();
if (vnode.state.selected)
for (let preset of presets.value)
const selected = new Set();
if (vnode.state.selected) {
for (const preset of presets.value) {
if (vnode.state.selected.has(preset["_id"]))
selected.add(preset["_id"]);
}
}
vnode.state.selected = selected;
const downloadUrl = getDownloadUrl(vnode.attrs.filter);
+37 -29
View File
@@ -34,10 +34,10 @@ const unpackSmartQuery = memoize(query => {
function putActionHandler(action, object, isNew) {
if (action === "save") {
let id = object["_id"];
const id = object["_id"];
delete object["_id"];
if (!id) return notifications.push("error", "ID can not be empty");
if (!id) return void notifications.push("error", "ID can not be empty");
store
.resourceExists("provisions", id)
@@ -85,14 +85,14 @@ function putActionHandler(action, object, isNew) {
}
}
let formData = {
const formData = {
resource: "provisions",
attributes: attributes
};
const getDownloadUrl = memoize(filter => {
let cols = {};
for (let attr of attributes) cols[attr.label] = attr.id;
const cols = {};
for (const attr of attributes) cols[attr.label] = attr.id;
return `/api/provisions.csv?${m.buildQueryString({
filter: filter,
columns: JSON.stringify(cols)
@@ -100,10 +100,11 @@ const getDownloadUrl = memoize(filter => {
});
function init(args) {
if (!window.authorizer.hasAccess("provisions", 2))
if (!window.authorizer.hasAccess("provisions", 2)) {
return Promise.reject(
new Error("You are not authorized to view this page")
);
}
const sort = args.sort;
const filter = args.filter;
@@ -131,16 +132,17 @@ function renderTable(
type: "checkbox",
checked: provisions.length && selected.size === provisions.length,
onchange: e => {
for (let provision of provisions)
for (const provision of provisions) {
if (e.target.checked) selected.add(provision["_id"]);
else selected.delete(provision["_id"]);
}
},
disabled: !total
});
const labels = [m("th", selectAll)];
for (let attr of attributes) {
let label = attr.label;
for (const attr of attributes) {
const label = attr.label;
let direction = 1;
@@ -148,7 +150,7 @@ function renderTable(
if (sort[attr.id] > 0) symbol = "\u2bc6";
else if (sort[attr.id] < 0) symbol = "\u2bc5";
let sortable = m(
const sortable = m(
"button",
{
onclick: () => {
@@ -162,9 +164,9 @@ function renderTable(
labels.push(m("th", [label, sortable]));
}
let rows = [];
for (let provision of provisions) {
let checkbox = m("input", {
const rows = [];
for (const provision of provisions) {
const checkbox = m("input", {
type: "checkbox",
checked: selected.has(provision["_id"]),
onchange: e => {
@@ -177,11 +179,12 @@ function renderTable(
}
});
let tds = [m("td", checkbox)];
for (let attr of attributes)
const tds = [m("td", checkbox)];
for (const attr of attributes) {
if (attr.id == "script")
tds.push(m("td", { title: provision[attr.id] }, provision[attr.id]));
else tds.push(m("td", provision[attr.id]));
}
tds.push(
m(
@@ -190,7 +193,7 @@ function renderTable(
"a",
{
onclick: () => {
let cb = () => {
const cb = () => {
return m(
putForm,
Object.assign(
@@ -232,12 +235,13 @@ function renderTable(
);
}
if (!rows.length)
if (!rows.length) {
rows.push(
m("tr.empty", m("td", { colspan: labels.length }, "No provisions"))
);
}
let footerElements = [];
const footerElements = [];
if (total != null) footerElements.push(`${provisions.length}/${total}`);
else footerElements.push(`${provisions.length}`);
@@ -253,12 +257,13 @@ function renderTable(
)
);
if (downloadUrl)
if (downloadUrl) {
footerElements.push(
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
);
}
let tfoot = m(
const tfoot = m(
"tfoot",
m("tr", m("td", { colspan: labels.length }, footerElements))
);
@@ -290,14 +295,14 @@ function renderTable(
)
];
if (window.authorizer.hasAccess("provisions", 3))
if (window.authorizer.hasAccess("provisions", 3)) {
buttons.push(
m(
"button.primary",
{
title: "Create new provision",
onclick: () => {
let cb = () => {
const cb = () => {
return m(
putForm,
Object.assign(
@@ -317,6 +322,7 @@ function renderTable(
"New"
)
);
}
return [
m(
@@ -339,13 +345,13 @@ const component = {
}
function onFilterChanged(filter) {
let ops = { filter };
const ops = { filter };
if (vnode.attrs.sort) ops.sort = vnode.attrs.sort;
m.route.set("/provisions", ops);
}
function onSortChange(sort) {
let ops = { sort };
const ops = { sort };
if (vnode.attrs.filter) ops.filter = vnode.attrs.filter;
m.route.set("/provisions", ops);
}
@@ -354,18 +360,20 @@ const component = {
let filter = vnode.attrs.filter ? memoizedParse(vnode.attrs.filter) : true;
filter = unpackSmartQuery(filter);
let provisions = store.fetch("provisions", filter, {
const provisions = store.fetch("provisions", filter, {
limit: vnode.state.showCount || PAGE_SIZE,
sort: sort
});
let count = store.count("provisions", filter);
const count = store.count("provisions", filter);
let selected = new Set();
if (vnode.state.selected)
for (let provision of provisions.value)
const selected = new Set();
if (vnode.state.selected) {
for (const provision of provisions.value) {
if (vnode.state.selected.has(provision["_id"]))
selected.add(provision["_id"]);
}
}
vnode.state.selected = selected;
const downloadUrl = getDownloadUrl(vnode.attrs.filter);
+12 -9
View File
@@ -3,7 +3,7 @@
import config from "./config";
import * as expression from "../common/expression";
let resources = {
const resources = {
devices: {},
faults: {
Device: { parameter: ["PARAM", "device"], type: "string" },
@@ -35,11 +35,12 @@ let resources = {
}
};
for (let v of Object.values(config.ui.filters))
for (const v of Object.values(config.ui.filters)) {
resources.devices[v.label] = {
parameter: expression.parse(v.parameter),
type: (v.type || "").split(",").map(s => s.trim())
};
}
function getLabels(resource) {
if (!resources[resource]) return [];
@@ -48,14 +49,15 @@ function getLabels(resource) {
function queryNumber(param, value) {
let op = "=";
for (let o of ["<>", "=", "<=", "<", ">=", ">"])
for (const o of ["<>", "=", "<=", "<", ">=", ">"]) {
if (value.startsWith(o)) {
op = o;
value = value.slice(o.length).trim();
break;
}
}
let v = parseInt(value);
const v = parseInt(value);
if (v !== +value) return null;
return [op, param, v];
@@ -68,12 +70,13 @@ function queryString(param, value) {
function queryMac(param, value) {
value = value.replace(/[^a-f0-9]/gi, "").toLowerCase();
if (!value) return null;
if (value.length === 12)
if (value.length === 12) {
return [
"LIKE",
["FUNC", "LOWER", param],
value.replace(/(..)(?!$)/g, "$1:")
];
}
return [
"OR",
@@ -90,20 +93,20 @@ function unpack(resource, label, value) {
if (!resources[resource]) return null;
const type = resources[resource][label].type;
value = value.trim();
let res = ["OR"];
const res = ["OR"];
if (type.length === 0 || type.includes("number")) {
let q = queryNumber(resources[resource][label].parameter, value);
const q = queryNumber(resources[resource][label].parameter, value);
if (q) res.push(q);
}
if (type.length === 0 || type.includes("string")) {
let q = queryString(resources[resource][label].parameter, value);
const q = queryString(resources[resource][label].parameter, value);
if (q) res.push(q);
}
if (type.includes("mac")) {
let q = queryMac(resources[resource][label].parameter, value);
const q = queryMac(resources[resource][label].parameter, value);
if (q) res.push(q);
}
+43 -32
View File
@@ -1,7 +1,6 @@
"use strict";
import m from "mithril";
import config from "./config";
import * as expressionParser from "../common/expression-parser";
import * as expression from "../common/expression";
import memoize from "../common/memoize";
@@ -23,7 +22,7 @@ const queries = {
};
const resources = {};
for (let r of [
for (const r of [
"devices",
"faults",
"files",
@@ -31,13 +30,14 @@ for (let r of [
"provisions",
"virtualParameters",
"files"
])
]) {
resources[r] = {
objects: new Map(),
count: new Map(),
fetch: new Map(),
combinedFilter: null
};
}
class QueryResponse {
get fulfilled() {
@@ -79,11 +79,12 @@ function limitFilter(filter, sort, bookmark) {
arr.reduce((cur, kv) => {
const [param, asc] = kv;
if (asc <= 0) {
if (bookmark[param] == null)
if (bookmark[param] == null) {
return expression.or(
["IS NOT NULL", ["PARAM", param]],
expression.and(["IS NULL", ["PARAM", param]], cur)
);
}
let f = null;
@@ -124,13 +125,15 @@ function compareFunction(sort) {
for (const [param, asc] of sortEntries) {
let v1 = a[param];
let v2 = b[param];
if (v1 != null && typeof v1 === "object")
if (v1 != null && typeof v1 === "object") {
if (v1.value) v1 = v1.value[0];
else v1 = null;
}
if (v2 != null && typeof v2 === "object")
if (v2 != null && typeof v2 === "object") {
if (v2.value) v2 = v2.value[0];
else v2 = null;
}
if (v1 > v2) {
return asc;
@@ -153,23 +156,24 @@ function compareFunction(sort) {
function findMatches(resourceType, filter, sort, limit) {
// Handle "tag =" and "tag <>" special cases
if (resourceType === "devices")
if (resourceType === "devices") {
filter = expressionParser.map(filter, e => {
if (
Array.isArray(e) &&
Array.isArray(e[1]) &&
e[1][0] === "PARAM" &&
e[1][1] === "tag"
)
) {
if (e[0] === "=") return ["IS NOT NULL", ["PARAM", `Tags.${e[2]}`]];
else if (e[0] === "<>") return ["IS NULL", ["PARAM", `Tags.${e[2]}`]];
}
return e;
});
}
let value = [];
for (let obj of resources[resourceType].objects.values())
if (expression.evaluate(filter, obj, fulfillTimestamp))
value.push(obj);
for (const obj of resources[resourceType].objects.values())
if (expression.evaluate(filter, obj, fulfillTimestamp)) value.push(obj);
value = value.sort(compareFunction(sort));
if (limit) value = value.slice(0, limit);
@@ -181,7 +185,7 @@ function inferQuery(resourceType, queryResponse) {
const limit = queries.limit.get(queryResponse);
let filter = queries.filter.get(queryResponse);
filter = unpackExpression(filter);
let bookmark = queries.bookmark.get(queryResponse);
const bookmark = queries.bookmark.get(queryResponse);
const sort = queries.sort.get(queryResponse);
if (bookmark || !limit) {
if (bookmark) filter = limitFilter(filter, sort, bookmark);
@@ -208,7 +212,7 @@ function fetch(resourceType, filter, options = {}) {
sort["DeviceID.ID"] = sort["DeviceID.ID"] || 1;
else sort["_id"] = sort["_id"] || 1;
let key = `${filterStr}:${limit}:${JSON.stringify(sort)}`;
const key = `${filterStr}:${limit}:${JSON.stringify(sort)}`;
let queryResponse = resources[resourceType].fetch.get(key);
if (queryResponse) return queryResponse;
@@ -225,15 +229,15 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
let updated = false;
if (_fulfillTimestamp > fulfillTimestamp) {
for (let resource of Object.values(resources))
for (const resource of Object.values(resources))
resource.combinedFilter = null;
fulfillTimestamp = _fulfillTimestamp;
}
const allPromises = [];
for (let [resourceType, resource] of Object.entries(resources))
for (let [queryResponseKey, queryResponse] of resource.count) {
for (const [resourceType, resource] of Object.entries(resources)) {
for (const [queryResponseKey, queryResponse] of resource.count) {
if (!(queries.accessed.get(queryResponse) >= accessTimestamp)) {
resource.count.delete(queryResponseKey);
continue;
@@ -269,11 +273,12 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
);
}
}
}
let toFetchAll = {};
const toFetchAll = {};
for (let [resourceType, resource] of Object.entries(resources))
for (let [queryResponseKey, queryResponse] of resource.fetch) {
for (const [resourceType, resource] of Object.entries(resources)) {
for (const [queryResponseKey, queryResponse] of resource.fetch) {
if (!(queries.accessed.get(queryResponse) >= accessTimestamp)) {
resource.fetch.delete(queryResponseKey);
continue;
@@ -287,7 +292,7 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
toFetchAll[resourceType].push(queryResponse);
const limit = queries.limit.get(queryResponse);
const sort = queries.sort.get(queryResponse);
if (limit)
if (limit) {
allPromises.push(
new Promise((resolve, reject) => {
updated = true;
@@ -308,12 +313,15 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
.then(res => {
if (res.length) {
// Generate bookmark object
let bm = Object.keys(sort).reduce((b, k) => {
if (res[0][k] != null)
const bm = Object.keys(sort).reduce((b, k) => {
if (res[0][k] != null) {
if (typeof res[0][k] === "object") {
if (res[0][k].value != null)
b[k] = res[0][k].value[0];
} else b[k] = res[0][k];
} else {
b[k] = res[0][k];
}
}
return b;
}, {});
@@ -326,8 +334,10 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
.catch(reject);
})
);
}
}
}
}
return new Promise((resolve, reject) => {
Promise.all(allPromises)
@@ -368,11 +378,12 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
if (!resources[resourceType].combinedFilter)
deleted = new Set(resources[resourceType].objects.keys());
let combinedFilterDiff = combinedFilter;
if (resources[resourceType].combinedFilter)
if (resources[resourceType].combinedFilter) {
combinedFilterDiff = expression.and(
combinedFilterDiff,
expression.not(resources[resourceType].combinedFilter)
);
}
resources[resourceType].combinedFilter = expression.or(
combinedFilter,
resources[resourceType].combinedFilter
@@ -389,7 +400,7 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
})
})
.then(res => {
for (let r of res) {
for (const r of res) {
const id =
resourceType === "devices"
? r["DeviceID.ID"].value[0]
@@ -398,7 +409,7 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
deleted.delete(id);
}
for (let d of deleted) {
for (const d of deleted) {
const obj = resources[resourceType].objects.get(d);
if (
expression.evaluate(
@@ -410,7 +421,7 @@ function fulfill(accessTimestamp, _fulfillTimestamp) {
resources[resourceType].objects.delete(d);
}
for (let queryResponse of toFetch) {
for (const queryResponse of toFetch) {
let filter = queries.filter.get(queryResponse);
filter = unpackExpression(filter);
const limit = queries.limit.get(queryResponse);
@@ -444,7 +455,7 @@ function getTimestamp() {
}
function postTasks(deviceId, tasks) {
for (let t of tasks) {
for (const t of tasks) {
t.status = "pending";
t.device = deviceId;
}
@@ -459,8 +470,8 @@ function postTasks(deviceId, tasks) {
const connectionRequestStatus = xhr.getResponseHeader(
"Connection-Request"
);
let st = JSON.parse(xhr.response);
for (let [i, t] of st.entries()) {
const st = JSON.parse(xhr.response);
for (const [i, t] of st.entries()) {
tasks[i]._id = t._id;
tasks[i].status = t.status;
tasks[i].fault = t.fault;
@@ -487,7 +498,7 @@ function deleteResource(resourceType, id) {
}
function putResource(resourceType, id, object) {
for (let k in object) if (object[k] === undefined) object[k] = null;
for (const k in object) if (object[k] === undefined) object[k] = null;
return m.request({
method: "PUT",
@@ -498,7 +509,7 @@ function putResource(resourceType, id, object) {
function resourceExists(resource, id) {
const param = resource === "devices" ? "DeviceID.ID" : "_id";
let filter = ["=", ["PARAM", param], id];
const filter = ["=", ["PARAM", param], id];
return m.request({
method: "HEAD",
url:
+6 -5
View File
@@ -41,8 +41,8 @@ function stageDownload(task) {
}
function commit(tasks, callback) {
let devices = {};
for (let t of tasks) {
const devices = {};
for (const t of tasks) {
devices[t.device] = devices[t.device] || [];
devices[t.device].push(t);
queueTask(t);
@@ -50,19 +50,20 @@ function commit(tasks, callback) {
return new Promise(resolve => {
let counter = 1;
for (let [deviceId, tasks2] of Object.entries(devices)) {
for (const [deviceId, tasks2] of Object.entries(devices)) {
++counter;
store
.postTasks(deviceId, tasks)
.then(connectionRequestStatus => {
for (let t of tasks2)
for (const t of tasks2) {
if (t.status === "pending") t.status = "stale";
else if (t.status === "done") queue.delete(t);
}
callback(deviceId, null, connectionRequestStatus, tasks2);
if (--counter === 0) resolve();
})
.catch(err => {
for (let t of tasks2) t.status = "stale";
for (const t of tasks2) t.status = "stale";
callback(deviceId, err, null, tasks2);
if (--counter === 0) resolve();
});
+2 -1
View File
@@ -13,7 +13,7 @@ function timeAgo(dtime) {
let res = "";
let level = 2;
for (const [u, t] of Object.entries(UNITS))
for (const [u, t] of Object.entries(UNITS)) {
if (dtime >= t) {
let n;
if (level > 1) {
@@ -26,6 +26,7 @@ function timeAgo(dtime) {
else res += `${n} ${u} `;
if (!--level) break;
}
}
return res + "ago";
}
+3 -2
View File
@@ -7,7 +7,7 @@ import * as notifications from "./notifications";
const userMenu = {
view: () => {
if (window.username)
if (window.username) {
return m(
"div.user-menu",
window.username,
@@ -32,7 +32,7 @@ const userMenu = {
"Log out"
)
);
else
} else {
return m(
"div.user-menu",
m(
@@ -43,6 +43,7 @@ const userMenu = {
"Log in"
)
);
}
}
};
+41 -31
View File
@@ -34,10 +34,10 @@ const unpackSmartQuery = memoize(query => {
function putActionHandler(action, object, isNew) {
if (action === "save") {
let id = object["_id"];
const id = object["_id"];
delete object["_id"];
if (!id) return notifications.push("error", "ID can not be empty");
if (!id) return void notifications.push("error", "ID can not be empty");
store
.resourceExists("virtualParameters", id)
@@ -85,14 +85,14 @@ function putActionHandler(action, object, isNew) {
}
}
let formData = {
const formData = {
resource: "virtualParameters",
attributes: attributes
};
const getDownloadUrl = memoize(filter => {
let cols = {};
for (let attr of attributes) cols[attr.label] = attr.id;
const cols = {};
for (const attr of attributes) cols[attr.label] = attr.id;
return `/api/virtualParameters.csv?${m.buildQueryString({
filter: filter,
columns: JSON.stringify(cols)
@@ -100,10 +100,11 @@ const getDownloadUrl = memoize(filter => {
});
function init(args) {
if (!window.authorizer.hasAccess("virtualParameters", 2))
if (!window.authorizer.hasAccess("virtualParameters", 2)) {
return Promise.reject(
new Error("You are not authorized to view this page")
);
}
const sort = args.sort;
const filter = args.filter;
@@ -132,16 +133,17 @@ function renderTable(
checked:
virtualParameters.length && selected.size === virtualParameters.length,
onchange: e => {
for (let virtualParameter of virtualParameters)
for (const virtualParameter of virtualParameters) {
if (e.target.checked) selected.add(virtualParameter["_id"]);
else selected.delete(virtualParameter["_id"]);
}
},
disabled: !total
});
const labels = [m("th", selectAll)];
for (let attr of attributes) {
let label = attr.label;
for (const attr of attributes) {
const label = attr.label;
let direction = 1;
@@ -149,7 +151,7 @@ function renderTable(
if (sort[attr.id] > 0) symbol = "\u2bc6";
else if (sort[attr.id] < 0) symbol = "\u2bc5";
let sortable = m(
const sortable = m(
"button",
{
onclick: () => {
@@ -163,9 +165,9 @@ function renderTable(
labels.push(m("th", [label, sortable]));
}
let rows = [];
for (let virtualParameter of virtualParameters) {
let checkbox = m("input", {
const rows = [];
for (const virtualParameter of virtualParameters) {
const checkbox = m("input", {
type: "checkbox",
checked: selected.has(virtualParameter["_id"]),
onchange: e => {
@@ -178,9 +180,9 @@ function renderTable(
}
});
let tds = [m("td", checkbox)];
for (let attr of attributes)
if (attr.id == "script")
const tds = [m("td", checkbox)];
for (const attr of attributes) {
if (attr.id == "script") {
tds.push(
m(
"td",
@@ -188,7 +190,10 @@ function renderTable(
virtualParameter[attr.id]
)
);
else tds.push(m("td", virtualParameter[attr.id]));
} else {
tds.push(m("td", virtualParameter[attr.id]));
}
}
tds.push(
m(
@@ -197,7 +202,7 @@ function renderTable(
"a",
{
onclick: () => {
let cb = () => {
const cb = () => {
return m(
putForm,
Object.assign(
@@ -239,15 +244,16 @@ function renderTable(
);
}
if (!rows.length)
if (!rows.length) {
rows.push(
m(
"tr.empty",
m("td", { colspan: labels.length }, "No virtual parameters")
)
);
}
let footerElements = [];
const footerElements = [];
if (total != null)
footerElements.push(`${virtualParameters.length}/${total}`);
else footerElements.push(`${virtualParameters.length}`);
@@ -266,12 +272,13 @@ function renderTable(
)
);
if (downloadUrl)
if (downloadUrl) {
footerElements.push(
m("a.download-csv", { href: downloadUrl, download: "" }, "Download")
);
}
let tfoot = m(
const tfoot = m(
"tfoot",
m("tr", m("td", { colspan: labels.length }, footerElements))
);
@@ -306,14 +313,14 @@ function renderTable(
)
];
if (window.authorizer.hasAccess("virtualParameters", 3))
if (window.authorizer.hasAccess("virtualParameters", 3)) {
buttons.push(
m(
"button.primary",
{
title: "Create new virtual parameter",
onclick: () => {
let cb = () => {
const cb = () => {
return m(
putForm,
Object.assign(
@@ -333,6 +340,7 @@ function renderTable(
"New"
)
);
}
return [
m(
@@ -355,13 +363,13 @@ const component = {
}
function onFilterChanged(filter) {
let ops = { filter };
const ops = { filter };
if (vnode.attrs.sort) ops.sort = vnode.attrs.sort;
m.route.set("/virtualParameters", ops);
}
function onSortChange(sort) {
let ops = { sort };
const ops = { sort };
if (vnode.attrs.filter) ops.filter = vnode.attrs.filter;
m.route.set("/virtualParameters", ops);
}
@@ -370,18 +378,20 @@ const component = {
let filter = vnode.attrs.filter ? memoizedParse(vnode.attrs.filter) : true;
filter = unpackSmartQuery(filter);
let virtualParameters = store.fetch("virtualParameters", filter, {
const virtualParameters = store.fetch("virtualParameters", filter, {
limit: vnode.state.showCount || PAGE_SIZE,
sort: sort
});
let count = store.count("virtualParameters", filter);
const count = store.count("virtualParameters", filter);
let selected = new Set();
if (vnode.state.selected)
for (let virtualParameter of virtualParameters.value)
const selected = new Set();
if (vnode.state.selected) {
for (const virtualParameter of virtualParameters.value) {
if (vnode.state.selected.has(virtualParameter["_id"]))
selected.add(virtualParameter["_id"]);
}
}
vnode.state.selected = selected;
const downloadUrl = getDownloadUrl(vnode.attrs.filter);