<commit_before>/**
 * Copyright reelyActive 2021
 * We believe in an open Internet of Things
 */


let cuttlefishDynamb = (function() {

  // Internal constants
  const IDENTIFIER_TYPES = [
      'Unknown',
      'EUI-64',
      'EUI-48',
      'RND-48'
  ];
  const AXIS_NAMES = [ 'x', 'y', 'z' ];

  // Standard data properties (property: {icon, suffix}) in alphabetical order
  const STANDARD_DATA_PROPERTIES = {
      acceleration: { icon: "fas fa-rocket", suffix: "g",
                      transform: "progressXYZ" },
      batteryPercentage: { icon: "fas fa-battery-half", suffix: " %",
                           transform: "progressPercentage" },
      batteryVoltage: { icon: "fas fa-battery-half", suffix: " V",
                        transform: "toFixed(2)" },
      deviceId: { icon: "fas fa-wifi", suffix: "", transform: "text" },
      relativeHumidity: { icon: "fas fa-water", suffix: " %",
                          transform: "progressPercentage" },
      temperature: { icon: "fas fa-thermometer-half", suffix: " \u2103",
                     transform: "toFixed(2)" },
      timestamp: { icon: "fas fa-clock", suffix: "", transform: "timeOfDay" },
      uptime: { icon: "fas fa-stopwatch", suffix: " ms" }
  };

  // Render a dynamb
  function render(dynamb, target, options) {
    let tbody = createElement('tbody');
    let table = createElement('table', 'table table-hover', tbody);

    if(dynamb.hasOwnProperty('timestamp')) {
      let localeTimestamp = new Date(dynamb['timestamp']).toLocaleTimeString();
      let captionIcon = createElement('i', 'fas fa-clock');
      let captionText = ' \u00a0' + localeTimestamp;
      let caption = createElement('caption', 'caption-top',
                                  [ captionIcon, captionText ]);
      table.appendChild(caption);
    }
    if(dynamb.hasOwnProperty('deviceId') &&
       dynamb.hasOwnProperty('deviceIdType')) {
      let deviceId = dynamb.deviceId + ' / ' +
                     IDENTIFIER_TYPES[dynamb.deviceIdType];
      let tr = renderAsRow('deviceId', deviceId);
      tbody.appendChild(tr);
    }

    for(const property in dynamb) {
      if((property !== 'timestamp') && (property !== 'deviceId') &&
         (property !== 'deviceIdType')) {
        let tr = renderAsRow(property, dynamb[property]);

        if(tr) {
          tbody.appendChild(tr);
        }
      }
    }

    if(target) {
      target.replaceChildren(table);
    }

    return table;
  }

  // Render a table row
  function renderAsRow(property, data) {
    let isKnownProperty = STANDARD_DATA_PROPERTIES.hasOwnProperty(property);

    if(isKnownProperty) {
      let dataRender = STANDARD_DATA_PROPERTIES[property];
      let content = renderAsTransform(dataRender.transform, data,
                                      dataRender.suffix);
      let icon = createElement('i', dataRender.icon);
      let th = createElement('th', 'text-center', icon);
      let td = createElement('td', 'align-middle', content);
      let tr = createElement('tr', null, [ th, td ]);

      return tr;
    }

    return null; // TODO: handle unknown property
  }

  // Render property as per given transform
  function renderAsTransform(transform, data, suffix) {
    suffix = suffix || '';

    switch(transform) {
      case 'progressPercentage':
        return renderProgress(data, 100, 0, '%');
      case 'progressXYZ':
        return renderProgressXYZ(data, suffix);
      case 'toFixed(2)':
        return data.toFixed(2) + suffix;
      default:
        return data.toString() + suffix;
    }
  }

  // Render a progress bar
  function renderProgress(value, maxValue, decimalDigits, suffix) {
    decimalDigits = decimalDigits || 0;
    suffix = suffix || '';

    let isPositive = (value >= 0);
    let valueString = value.toFixed(decimalDigits) + suffix;
    let widthPercentage = (100 * Math.abs(value) / maxValue).toFixed(0) + '%';
    let progressBar = createElement('div', 'progress-bar', valueString);
    let progressClass = isPositive ? 'progress' : 'progress flex-row-reverse';
    let progress = createElement('div', progressClass, progressBar);

    progressBar.setAttribute('style', 'width: ' + widthPercentage);

    return progress;
  }

  // Render a progress XYZ array
  function renderProgressXYZ(data, suffix) {
    suffix = suffix || '';

    let maxValue = Math.max(Math.max(...data), Math.abs(Math.min(...data)));
    let magnitude = 0;
    let tbody = createElement('tbody', 'align-middle');
    let table = createElement('table', 'table table-borderless', tbody);

    data.forEach(function(value, index) {
      let progressNeg = renderProgress(Math.min(value, 0), maxValue, 2, suffix);
      let progressPos = renderProgress(Math.max(value, 0), maxValue, 2, suffix);
      let tdNeg = createElement('td', null, progressNeg);
      let th = createElement('th', 'text-center small', AXIS_NAMES[index]);
      let tdPos = createElement('td', null, progressPos);
      let tr = createElement('tr', null, [ tdNeg, th, tdPos ]);
      tbody.appendChild(tr);
      magnitude += (value * value);
    });

    magnitude = Math.sqrt(magnitude).toFixed(2);
    let caption = createElement('caption', null, magnitude + suffix);
    table.appendChild(caption);

    return table;
  }

  // Create an element as specified, appending optional content as child(ren)
  function createElement(elementName, classNames, content) {<commit_msg>Added renderTableNearest() function<commit_after>/**
 * Copyright reelyActive 2021
 * We believe in an open Internet of Things
 */


let cuttlefishDynamb = (function() {

  // Internal constants
  const IDENTIFIER_TYPES = [
      'Unknown',
      'EUI-64',
      'EUI-48',
      'RND-48'
  ];
  const AXIS_NAMES = [ 'x', 'y', 'z' ];

  // Standard data properties (property: {icon, suffix}) in alphabetical order
  const STANDARD_DATA_PROPERTIES = {
      acceleration: { icon: "fas fa-rocket", suffix: "g",
                      transform: "progressXYZ" },
      batteryPercentage: { icon: "fas fa-battery-half", suffix: " %",
                           transform: "progressPercentage" },
      batteryVoltage: { icon: "fas fa-battery-half", suffix: " V",
                        transform: "toFixed(2)" },
      deviceId: { icon: "fas fa-wifi", suffix: "", transform: "text" },
      nearest: { icon: "fas fa-people-arrows", suffix: "",
                 transform: "tableNearest" },
      relativeHumidity: { icon: "fas fa-water", suffix: " %",
                          transform: "progressPercentage" },
      temperature: { icon: "fas fa-thermometer-half", suffix: " \u2103",
                     transform: "toFixed(2)" },
      timestamp: { icon: "fas fa-clock", suffix: "", transform: "timeOfDay" },
      uptime: { icon: "fas fa-stopwatch", suffix: " ms" }
  };

  // Render a dynamb
  function render(dynamb, target, options) {
    let tbody = createElement('tbody');
    let table = createElement('table', 'table', tbody);

    if(dynamb.hasOwnProperty('timestamp')) {
      let localeTimestamp = new Date(dynamb['timestamp']).toLocaleTimeString();
      let captionIcon = createElement('i', 'fas fa-clock');
      let captionText = ' \u00a0' + localeTimestamp;
      let caption = createElement('caption', 'caption-top',
                                  [ captionIcon, captionText ]);
      table.appendChild(caption);
    }
    if(dynamb.hasOwnProperty('deviceId') &&
       dynamb.hasOwnProperty('deviceIdType')) {
      let deviceId = dynamb.deviceId + ' / ' +
                     IDENTIFIER_TYPES[dynamb.deviceIdType];
      let tr = renderAsRow('deviceId', deviceId);
      tbody.appendChild(tr);
    }

    for(const property in dynamb) {
      if((property !== 'timestamp') && (property !== 'deviceId') &&
         (property !== 'deviceIdType')) {
        let tr = renderAsRow(property, dynamb[property]);

        if(tr) {
          tbody.appendChild(tr);
        }
      }
    }

    if(target) {
      target.replaceChildren(table);
    }

    return table;
  }

  // Render a table row
  function renderAsRow(property, data) {
    let isKnownProperty = STANDARD_DATA_PROPERTIES.hasOwnProperty(property);

    if(isKnownProperty) {
      let dataRender = STANDARD_DATA_PROPERTIES[property];
      let content = renderAsTransform(dataRender.transform, data,
                                      dataRender.suffix);
      let icon = createElement('i', dataRender.icon);
      let th = createElement('th', 'text-center', icon);
      let td = createElement('td', 'align-middle', content);
      let tr = createElement('tr', null, [ th, td ]);

      return tr;
    }

    return null; // TODO: handle unknown property
  }

  // Render property as per given transform
  function renderAsTransform(transform, data, suffix) {
    suffix = suffix || '';

    switch(transform) {
      case 'progressPercentage':
        return renderProgress(data, 100, 0, '%');
      case 'progressXYZ':
        return renderProgressXYZ(data, suffix);
      case 'toFixed(2)':
        return data.toFixed(2) + suffix;
      case 'tableNearest':
        return renderTableNearest(data);
      default:
        return data.toString() + suffix;
    }
  }

  // Render a progress bar
  function renderProgress(value, maxValue, decimalDigits, suffix) {
    decimalDigits = decimalDigits || 0;
    suffix = suffix || '';

    let isPositive = (value >= 0);
    let valueString = value.toFixed(decimalDigits) + suffix;
    let widthPercentage = (100 * Math.abs(value) / maxValue).toFixed(0) + '%';
    let progressBar = createElement('div', 'progress-bar', valueString);
    let progressClass = isPositive ? 'progress' : 'progress flex-row-reverse';
    let progress = createElement('div', progressClass, progressBar);

    progressBar.setAttribute('style', 'width: ' + widthPercentage);

    return progress;
  }

  // Render a progress XYZ array
  function renderProgressXYZ(data, suffix) {
    suffix = suffix || '';

    let maxValue = Math.max(Math.max(...data), Math.abs(Math.min(...data)));
    let magnitude = 0;
    let tbody = createElement('tbody', 'align-middle');
    let table = createElement('table', 'table table-borderless', tbody);

    data.forEach(function(value, index) {
      let progressNeg = renderProgress(Math.min(value, 0), maxValue, 2, suffix);
      let progressPos = renderProgress(Math.max(value, 0), maxValue, 2, suffix);
      let tdNeg = createElement('td', null, progressNeg);
      let th = createElement('th', 'text-center small', AXIS_NAMES[index]);
      let tdPos = createElement('td', null, progressPos);
      let tr = createElement('tr', null, [ tdNeg, th, tdPos ]);
      tbody.appendChild(tr);
      magnitude += (value * value);
    });

    magnitude = Math.sqrt(magnitude).toFixed(2);
    let caption = createElement('caption', null, magnitude + suffix);
    table.appendChild(caption);

    return table;
  }

  // Render a nearest array in tabular format
  function renderTableNearest(data) {
    let tbody = createElement('tbody', 'align-middle font-monospace');
    let table = createElement('table',
                              'table table-hover table-borderless mb-0', tbody);

    data.forEach(function(entry, index) {
      let tdInstance = createElement('td', null, entry.deviceId);
      let tdValue;

      if(entry.hasOwnProperty('rssi')) {
        tdValue = createElement('td', null, entry.rssi + ' dBm');
      }
      else if(entry.hasOwnProperty('interactionCount')) {
        tdValue = createElement('td', null,
                                entry.interactionCount + ' interactions');
      }

      let trClass = (index === 0) ? 'table-success' : null;
      let tr = createElement('tr', trClass, [ tdInstance, tdValue ]);
      tbody.appendChild(tr);
    });

    return table;
  }

  // Create an element as specified, appending optional content as child(ren)
  function createElement(elementName, classNames, content) {
{'fraction_non_alphanumeric': 0.13841499285654257, 'fraction_numerical': 0.006387091352214472, 'mean_word_length': 2.794642857142857, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '805199', 'n_tokens_mistral': 3568, 'n_tokens_neox': 3461, 'n_words': 847}