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 '&nbsp;&nbsp;<i class="fas fa-book nd_icon-archived"></i> ';
    return '&nbsp;<i class="fas fa-book nd_icon-archived"></i>&nbsp;';
  };

  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 )