App-Netdisco
view release on metacpan or search on metacpan
share/public/javascripts/netdisco-tables.js view on Meta::CPAN
// CodeQL see all of ours.
//
// The data-nd-table attribute is a DataTables options object: columns,
// columnDefs, targets, order, paging and the rest keep DataTables' own
// meaning and are passed through unchanged, so the DataTables option
// reference is their specification. Two keys are consumed here and never
// reach DataTables: "defaults": false skips the shared options, which are
// the DataTables options every table starts from and live in defaults()
// below, and "customReport" keeps a custom report's column order out of the
// saved table state. Three sibling attributes complete the contract:
// data-nd-urls holds the URL prefixes the renderers need, data-nd-data names
// the id of the application/json block holding the rows, and data-nd-admin
// tells the subnet renderer whether to show the admin tools.
//
// JSON cannot carry a function, so a column's render and a table's
// drawCallback or initComplete are named here and referenced by name. Each
// entry is a factory: it takes the arguments the JSON gives it and the
// table's URL prefixes, and returns the function DataTables calls.
const ndTables = (function () {
const esc = function (s) {
return DataTable.util.escapeHtml(s == null ? '' : String(s));
};
const enc = encodeURIComponent;
const get = function (row, dotted) {
return String(dotted)
.split('.')
.reduce(function (o, k) {
return o == null ? o : o[k];
}, row);
};
const link = function (href, text) {
return '<a href="' + href + '">' + text + '</a>';
};
// DataTable.render.text() and .number() return an object keyed by render
// type (display, filter, ...), falling back to the raw value for a type
// they don't name, rather than a single function; this makes that shape
// callable like every other entry in RENDERERS.
const byType = function (spec) {
return function (data, type) {
return (
spec[type] ||
function (d) {
return d;
}
)(data);
};
};
// Device label as netdisco shows it everywhere: dns, else name, else ip.
// args.dns, args.name and args.ip name the row keys, each optional. A
// caller that itself needs a "name" key for something other than a row
// key (deviceLink's dispatch key is "name" too) cannot also write a row
// key literally called "name" in the same JSON object: JSON.parse keeps
// only the last of two identical keys. Such a caller nests dns/name/ip
// under args.label instead, a second object with no such collision.
//
// The closures this replaces each read a fixed, different subset of
// dns/name/ip (some never looked at name at all), so naming any one of the
// three restricts the cascade to only the named tiers, still tried in dns,
// name, ip order. Naming none is a caller with no such history to match,
// so it gets the full three-tier fallback.
const deviceLabel = function (args, row, fallback) {
const a = args.label || args;
const tiers = a.dns || a.name || a.ip ? [a.dns, a.name, a.ip] : ['dns', 'name', 'ip'];
let value;
tiers.forEach(function (key) {
if (!value && key) value = get(row, key);
});
return value || fallback;
};
const withQuery = function (base, q) {
return base + (base.indexOf('?') === -1 ? '?' : '&') + q;
};
// Every renderer needing a URL prefix reads it through here, so a fragment
// missing a data-nd-urls key fails loudly, naming the key and the
// renderer, instead of building an href with the literal text "undefined".
const urlFor = function (urls, name, rendererName) {
const url = urls[name];
if (url == null) throw new Error('renderer ' + rendererName + ' needs data-nd-urls key "' + name + '"');
return url;
};
// The book marker an inactive node carries: inside the link text with
// args.mark, beside it with args.archived, absent for an active row.
const archivedMark = function (args, row) {
if (!((args.archived || args.mark) && !row.active)) return '';
if (args.mark) return ' <i class="fas fa-book nd_icon-archived"></i> ';
return ' <i class="fas fa-book nd_icon-archived"></i> ';
};
const RENDERERS = {
/**
* Returns the cell value unchanged, or an empty string when the value is null or undefined.
* @returns {Function} the DataTables render function
*/
raw: function () {
return function (data) {
return data == null ? '' : data;
};
},
/**
* Escapes the cell value as HTML text for every render type, via the vendored text renderer.
* @returns {Function} the DataTables render function
*/
escape: function () {
return byType(DataTable.render.text());
},
/**
* Formats a numeric cell with thousands separators. The vendored formatter bakes in
* one fixed decimal count per instance, but a column such as devicepoestatus's
* wattage readings mixes whole watts with one-decimal values row by row, so the
* precision is read off each value's own string form instead of fixed at build
* time. Non-numeric and null values pass through unformatted either way, by the
* vendored formatter's own guard.
* @param {object} args args.precision, when given, forces that precision for every row instead of reading it per value
* @returns {Function} the DataTables render function
*/
number: function (args) {
( run in 1.130 second using v1.01-cache-2.11-cpan-e7c6538aa59 )