<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" },
      elevation: { icon: "fas fa-layer-group", suffix: " m",
                   transform: "toFixed(2)" },
      heading: { icon: "fas fa-compass", suffix: "  \u00b0",
                 transform: "toFixed(1)" },
      heartRate: { icon: "fas fa-heartbeat", suffix: " bpm",
                   transform: "toFixed(0)" },
      illuminance: { icon: "fas fa-sun", suffix: " lx",
                     transform: "toFixed(0)" },
      interactionDigest: { icon: "fas fa-history", suffix: "interactions",
                           transform: "tableDigest" },
      isButtonPressed: { icon: "fas fa-hand-pointer", suffix: "",
                         transform: "booleanArray" },
      magneticField: { icon: "fas fa-magnet", suffix: " G",
                       transform: "progressXYZ" },
      nearest: { icon: "fas fa-people-arrows", suffix: "dBm",
                 transform: "tableNearest" },
      position: { icon: "fas fa-map-pin", suffix: "", transform: "position" },
      pressure: { icon: "fas fa-cloud", suffix: " Pa",
                  transform: "toFixed(0)" },
      relativeHumidity: { icon: "fas fa-water", suffix: " %",
                          transform: "progressPercentage" },
      speed: { icon: "fas fa-tachometer-alt", suffix: " m/s",
               transform: "toFixed(2)" },
      temperature: { icon: "fas fa-thermometer-half", suffix: " \u2103",
                     transform: "toFixed(2)" },
      timestamp: { icon: "fas fa-clock", suffix: "", transform: "timeOfDay" },
      unicodeCodePoints: { icon: "fas fa-language", suffix: "",
                          transform: "unicodeCodePoints" },
      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 'booleanArray':
        return renderBooleanArray(data);
      case 'unicodeCodePoints':
        return renderUnicodeCodePoints(data);
      case 'position':
        return renderPosition(data);
      case 'progressPercentage':
        return renderProgress(data, 100, 0, '%');
      case 'progressXYZ':
        return renderProgressXYZ(data, suffix);
      case 'toFixed(2)':
        return data.toFixed(2) + suffix;
      case 'tableNearest':
        return renderTableDevices(data, 'rssi', suffix);
      case 'tableDigest':
        return renderTableDevices(data.interactions, 'count', suffix,
                                  data.timestamp);
      default:
        return data.toString() + suffix;
    }
  }

  // Render an array of boolean values
  function renderBooleanArray(values) {
    let buttons = [];

    for(const value of values) {
      let iconClass = value ? 'fas fa-check' : 'fas fa-times';
      let buttonClass = value ? 'btn btn-success' : 'btn btn-outline-info';
      let icon = createElement('i', iconClass);
      buttons.push(createElement('button', buttonClass, icon));
    }

    let buttonGroup = createElement('div', 'btn-group btn-group-sm', buttons);

    return createElement('div', 'btn-toolbar', buttonGroup);
  }

  // Render an array of Unicode code points
  function renderUnicodeCodePoints(codePoints) {
    let characters = "";

    for(const codePoint of codePoints) {
      characters += String.fromCodePoint(codePoint);
    }

    return createElement('span', 'display-1', characters);
  }

  // Render a 2D or 3D position
  function renderPosition(position) {
    let list = position[1] + '\u00b0 ' + ((position[1] >= 0) ? '(N)' : '(S)') +
               ', ' +
               position[0] + '\u00b0 ' + ((position[0] >= 0) ? '(E)' : '(W)');
    if(position.length > 2) {
      list += ', ' + position[2] + 'm';
    }

    return list;
  }

  // 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 device array in tabular format
  function renderTableDevices(data, property, suffix, timestamp) {
    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 = createElement('td', null, entry[property] + ' ' + suffix);
      let trClass = (index === 0) ? 'table-success' : null;
      let tr = createElement('tr', trClass, [ tdInstance, tdValue ]);

      tbody.appendChild(tr);
    });

    if(timestamp) {
      let captionTime = new Date(timestamp).toLocaleTimeString();
      let captionIcon = createElement('i', 'fas fa-clock');
      let isUptime = (timestamp < (Date.now() / 10000));
      if(isUptime) {
        let hours = Math.floor(timestamp / 3600);
        let minutes = Math.floor((timestamp % 3600) / 60);
        let seconds = (timestamp % 3600) % 60;
        captionTime = '\u00a0 ' + hours + 'h ' + minutes + 'm ' + seconds + 's';
        captionIcon = createElement('i', 'fas fa-stopwatch');
      }
      let caption = createElement('caption', null,
                                  [ captionIcon, captionTime ]);
      table.appendChild(caption);
    }

    return table;
  }

  // Create an element as specified, appending optional content as child(ren)
  function createElement(elementName, classNames, content) {
    let element = document.createElement(elementName);

    if(classNames) {
      element.setAttribute('class', classNames);
    }

    if((content instanceof Element) || (content instanceof DocumentFragment)) {
      element.appendChild(content);
    }
    else if(Array.isArray(content)) {
      content.forEach(function(item) {
        if((item instanceof Element) || (item instanceof DocumentFragment)) {
          element.appendChild(item);
        }
        else {
          element.appendChild(document.createTextNode(item));
        }
      });
    }
    else if(content) {
      element.appendChild(document.createTextNode(content));
    }

    return element;
  }

  // Expose the following functions and variables
  return {
    render: render
  }

}());<commit_msg>Updated to latest cuttlefish-dynamb<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' ];
  const MS_IN_YEAR = 31536000000;
  const MS_IN_DAY = 86400000;
  const MS_IN_HOUR = 3600000;
  const MS_IN_MINUTE = 60000;
  const MS_IN_SECOND = 1000;

  // 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" },
      elevation: { icon: "fas fa-layer-group", suffix: " m",
                   transform: "toFixed(2)" },
      heading: { icon: "fas fa-compass", suffix: "  \u00b0",
                 transform: "toFixed(1)" },
      heartRate: { icon: "fas fa-heartbeat", suffix: " bpm",
                   transform: "toFixed(0)" },
      illuminance: { icon: "fas fa-sun", suffix: " lx",
                     transform: "toFixed(0)" },
      interactionDigest: { icon: "fas fa-history", suffix: "interactions",
                           transform: "tableDigest" },
      isButtonPressed: { icon: "fas fa-hand-pointer", suffix: "",
                         transform: "booleanArray" },
      magneticField: { icon: "fas fa-magnet", suffix: " G",
                       transform: "progressXYZ" },
      nearest: { icon: "fas fa-people-arrows", suffix: "dBm",
                 transform: "tableNearest" },
      position: { icon: "fas fa-map-pin", suffix: "", transform: "position" },
      pressure: { icon: "fas fa-cloud", suffix: " Pa",
                  transform: "toFixed(0)" },
      relativeHumidity: { icon: "fas fa-water", suffix: " %",
                          transform: "progressPercentage" },
      speed: { icon: "fas fa-tachometer-alt", suffix: " m/s",
               transform: "toFixed(2)" },
      temperature: { icon: "fas fa-thermometer-half", suffix: " \u2103",
                     transform: "toFixed(2)" },
      timestamp: { icon: "fas fa-clock", suffix: "", transform: "timeOfDay" },
      txCount: { icon: "fas fa-satellite-dish", transform: "localeString",
                 suffix: " Tx" },
      unicodeCodePoints: { icon: "fas fa-language", suffix: "",
                          transform: "unicodeCodePoints" },
      uptime: { icon: "fas fa-stopwatch", transform: "elapsedTime" }
  };

  // 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 single dynamb value
  function renderValue(property, data, target, options) {
    let isKnownProperty = STANDARD_DATA_PROPERTIES.hasOwnProperty(property);
    let content = createElement('span', null, data);

    if(isKnownProperty) {
      let dataRender = STANDARD_DATA_PROPERTIES[property];
      content = renderAsTransform(dataRender.transform, data,
                                  dataRender.suffix);
    }

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

    return content;
  }

  // Render a single dynamb icon
  function renderIcon(property, target, options) {
    let isKnownProperty = STANDARD_DATA_PROPERTIES.hasOwnProperty(property);
    let content = createElement('i', 'fas fa-question-circle');

    if(isKnownProperty) {
      content = createElement('i', STANDARD_DATA_PROPERTIES[property].icon);
    }

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

    return content;
  }

  // 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 'booleanArray':
        return renderBooleanArray(data);
      case 'elapsedTime':
        return renderElapsedTime(data);
      case 'unicodeCodePoints':
        return renderUnicodeCodePoints(data);
      case 'position':
        return renderPosition(data);
      case 'progressPercentage':
        return renderProgress(data, 100, 0, '%');
      case 'progressXYZ':
        return renderProgressXYZ(data, suffix);
      case 'toFixed(2)':
        return data.toFixed(2) + suffix;
      case 'localeString':
        return data.toLocaleString() + suffix;
      case 'tableNearest':
        return renderTableDevices(data, 'rssi', suffix);
      case 'tableDigest':
        return renderTableDevices(data.interactions, 'count', suffix,
                                  data.timestamp);
      default:
        return data.toString() + suffix;
    }
  }

  // Render an array of boolean values
  function renderBooleanArray(values) {
    let buttons = [];

    for(const value of values) {
      let iconClass = value ? 'fas fa-check' : 'fas fa-times';
      let buttonClass = value ? 'btn btn-success' : 'btn btn-outline-info';
      let icon = createElement('i', iconClass);
      buttons.push(createElement('button', buttonClass, icon));
    }

    let buttonGroup = createElement('div', 'btn-group btn-group-sm', buttons);

    return createElement('div', 'btn-toolbar', buttonGroup);
  }

  // Render an elapsed time in the appropriate units
  function renderElapsedTime(elapsedTime) {
    let representation = '';
    let remainingTime = elapsedTime;

    if(remainingTime > MS_IN_YEAR) {
      let years = Math.floor(remainingTime / MS_IN_YEAR);
      representation += years + (years === 1 ? ' year, ' : ' years, ');
      remainingTime -= (years * MS_IN_YEAR);
    }
    if((remainingTime !== elapsedTime) || (remainingTime > MS_IN_DAY)) {
      let days = Math.floor(remainingTime / MS_IN_DAY);
      representation += days + (days === 1 ? ' day, ' : ' days, ');
      remainingTime -= (days * MS_IN_DAY);
    }
    if((remainingTime !== elapsedTime) || (remainingTime > MS_IN_HOUR)) {
      let hours = Math.floor(remainingTime / MS_IN_HOUR);
      representation += hours + 'h';
      remainingTime -= (hours * MS_IN_HOUR);
    }
    if((remainingTime !== elapsedTime) || (remainingTime > MS_IN_MINUTE)) {
      let minutes = Math.floor(remainingTime / MS_IN_MINUTE);
      representation += (minutes + 'm').padStart(3, '0');
      remainingTime -= (minutes * MS_IN_MINUTE);
    }
    let seconds = Math.round(remainingTime / MS_IN_SECOND);
    representation += (seconds + 's').padStart(3, '0');

    return representation;
  }

  // Render an array of Unicode code points
  function renderUnicodeCodePoints(codePoints) {
    let characters = "";

    for(const codePoint of codePoints) {
      characters += String.fromCodePoint(codePoint);
    }

    return createElement('span', 'display-1', characters);
  }

  // Render a 2D or 3D position
  function renderPosition(position) {
    let list = position[1] + '\u00b0 ' + ((position[1] >= 0) ? '(N)' : '(S)') +
               ', ' +
               position[0] + '\u00b0 ' + ((position[0] >= 0) ? '(E)' : '(W)');
    if(position.length > 2) {
      list += ', ' + position[2] + 'm';
    }

    return list;
  }

  // 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 device array in tabular format
  function renderTableDevices(data, property, suffix, timestamp) {
    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 = createElement('td', null, entry[property] + ' ' + suffix);
      let trClass = (index === 0) ? 'table-success' : null;
      let tr = createElement('tr', trClass, [ tdInstance, tdValue ]);

      tbody.appendChild(tr);
    });

    if(timestamp) {
      let captionTime = new Date(timestamp).toLocaleTimeString();
      let captionIcon = createElement('i', 'fas fa-clock');
      let isUptime = (timestamp < (Date.now() / 10000));
      if(isUptime) {
        let hours = Math.floor(timestamp / 3600);
        let minutes = Math.floor((timestamp % 3600) / 60);
        let seconds = (timestamp % 3600) % 60;
        captionTime = '\u00a0 ' + hours + 'h ' + minutes + 'm ' + seconds + 's';
        captionIcon = createElement('i', 'fas fa-stopwatch');
      }
      let caption = createElement('caption', null,
                                  [ captionIcon, captionTime ]);
      table.appendChild(caption);
    }

    return table;
  }

  // Create an element as specified, appending optional content as child(ren)
  function createElement(elementName, classNames, content) {
    let element = document.createElement(elementName);

    if(classNames) {
      element.setAttribute('class', classNames);
    }

    if((content instanceof Element) || (content instanceof DocumentFragment)) {
      element.appendChild(content);
    }
    else if(Array.isArray(content)) {
      content.forEach(function(item) {
        if((item instanceof Element) || (item instanceof DocumentFragment)) {
          element.appendChild(item);
        }
        else {
          element.appendChild(document.createTextNode(item));
        }
      });
    }
    else if(content) {
      element.appendChild(document.createTextNode(content));
    }

    return element;
  }

  // Expose the following functions and variables
  return {
    render: render,
    renderIcon: renderIcon,
    renderValue: renderValue
  }

}());<|endoftext|>


<commit_before>import icon from 'react-icons/lib/md/code'

export default {
  name: 'codeTest',
  type: 'document',
  title: 'Code test',
  icon,
  fields: [
    {
      name: 'title',
      title: 'Title',
      type: 'string'
    },
    {
      name: 'code',
      title: 'Default theme',
      description: 'Selectable language',
      type: 'code',
      options: {
        languageAlternatives: [
          {title: 'LaTeX', value: 'latex'},
          {title: 'JavaScript', value: 'javascript'},
          {title: 'CSS', value: 'css'},
          {title: 'text', value: 'text'}
        ]
      }
    },
    {
      name: 'cssOrJsCode',
      title: 'Github theme',
      type: 'code',
      options: {
        theme: 'github',
        languageAlternatives: [
          {title: 'JavaScript', value: 'javascript'},
          {title: 'CSS', value: 'css'}
        ]
      }
    },
    {
      name: 'jscode',
      title: 'Monokai theme',
      description: 'Only javascript',
      type: 'code',
      options: {
        theme: 'monokai',
        language: 'javascript'
      }
    }
  ]
}
<commit_msg>[test-studio] Add new code input languages<commit_after>import icon from 'react-icons/lib/md/code'

export default {
  name: 'codeTest',
  type: 'document',
  title: 'Code test',
  icon,
  fields: [
    {
      name: 'title',
      title: 'Title',
      type: 'string'
    },
    {
      name: 'code',
      title: 'Default theme',
      description: 'Selectable language',
      type: 'code',
      options: {
        languageAlternatives: [
          {title: 'LaTeX', value: 'latex'},
          {title: 'JavaScript', value: 'javascript'},
          {title: 'CSS', value: 'css'},
          {title: 'text', value: 'text'},
          {title: 'Python', value: 'python'},
          {title: 'SASS', value: 'sass'},
          {title: 'SCSS', value: 'scss'}
        ]
      }
    },
    {
      name: 'cssOrJsCode',
      title: 'Github theme',
      type: 'code',
      options: {
        theme: 'github',
        languageAlternatives: [
          {title: 'JavaScript', value: 'javascript'},
          {title: 'CSS', value: 'css'}
        ]
      }
    },
    {
      name: 'jscode',
      title: 'Monokai theme',
      description: 'Only javascript',
      type: 'code',
      options: {
        theme: 'monokai',
        language: 'javascript'
      }
    }
  ]
}
<|endoftext|>


<commit_before>/**
 * Context objects store the current parse state.
 * Enables lookup of parsed operators, event streams, accessors, etc.
 * Provides a 'fork' method for creating child contexts for subflows.
 */
export default function(df, transforms, functions) {
  return new Context(df, transforms, functions);
}

function Context(df, transforms, functions) {
  this.dataflow = df;
  this.transforms = transforms;
  this.events = df.events.bind(df);
  this.signals = {};
  this.scales = {};
  this.nodes = {};
  this.data = {};
  this.fn = {};
  this.itempath = [];
  if (functions) {
    this.functions = functions;
    this.functions.context = this;
  }
}

function ContextFork(ctx, index) {
  this.dataflow = ctx.dataflow;
  this.transforms = ctx.transforms;
  this.functions = ctx.functions;
  this.events = ctx.events;
  this.signals = Object.create(ctx.signals);
  this.scales = Object.create(ctx.scales);
  this.nodes = Object.create(ctx.nodes);
  this.data = Object.create(ctx.data);
  this.fn = Object.create(ctx.fn);
  this.itempath = ctx.itempath.concat(index);
  if (ctx.functions) {
    this.functions = Object.create(ctx.functions);
    this.functions.context = this;
  }
}

Context.prototype = ContextFork.prototype = {
  fork: function(index) {
    var ctx = new ContextFork(this, index);
    (this.subcontext || (this.subcontext = [])).push(ctx);
    return ctx;
  },
  get: function(id) {
    return this.nodes[id];
  },
  set: function(id, node) {
    return this.nodes[id] = node;
  },
  add: function(spec, op) {
    var ctx = this,
        df = ctx.dataflow,
        data;

    ctx.set(spec.id, op);

    if (spec.type === 'Collect' && (data = spec.value)) {
      if (data.$ingest) {
        df.ingest(op, data.$ingest, data.$format);
      } else if (data.$request) {
        df.request(op, data.$request, data.$format);
      } else {
        df.pulse(op, df.changeset().insert(data));
      }
    }

    if (spec.root) {
      ctx.root = op;
    }

    if (spec.parent) {
      var p = ctx.get(spec.parent.$ref);
      df.connect(p, [op]);
      op.targets().add(p);
    }

    if (spec.signal) {
      ctx.signals[spec.signal] = op;
    }

    if (spec.scale) {
      ctx.scales[spec.scale] = op;
    }

    if (spec.data) {
      for (var name in spec.data) {
        data = ctx.data[name] || (ctx.data[name] = {});
        spec.data[name].forEach(function(role) { data[role] = op; });
      }
    }
  },
  operator: function(spec, update, params) {
    this.add(spec, this.dataflow.add(spec.value, update, params, spec.react));
  },
  transform: function(spec, type, params) {
    this.add(spec, this.dataflow.add(this.transforms[type], params));
  },
  stream: function(spec, stream) {
    this.set(spec.id, stream);
  },
  update: function(spec, stream, target, update, params) {
    this.dataflow.on(stream, target, update, params, spec.options);
  }
};
<commit_msg>Defensively wrap functions context.<commit_after>/**
 * Context objects store the current parse state.
 * Enables lookup of parsed operators, event streams, accessors, etc.
 * Provides a 'fork' method for creating child contexts for subflows.
 */
export default function(df, transforms, functions) {
  return new Context(df, transforms, functions);
}

function Context(df, transforms, functions) {
  this.dataflow = df;
  this.transforms = transforms;
  this.events = df.events.bind(df);
  this.signals = {};
  this.scales = {};
  this.nodes = {};
  this.data = {};
  this.fn = {};
  this.itempath = [];
  if (functions) {
    this.functions = Object.create(functions);
    this.functions.context = this;
  }
}

function ContextFork(ctx, index) {
  this.dataflow = ctx.dataflow;
  this.transforms = ctx.transforms;
  this.functions = ctx.functions;
  this.events = ctx.events;
  this.signals = Object.create(ctx.signals);
  this.scales = Object.create(ctx.scales);
  this.nodes = Object.create(ctx.nodes);
  this.data = Object.create(ctx.data);
  this.fn = Object.create(ctx.fn);
  this.itempath = ctx.itempath.concat(index);
  if (ctx.functions) {
    this.functions = Object.create(ctx.functions);
    this.functions.context = this;
  }
}

Context.prototype = ContextFork.prototype = {
  fork: function(index) {
    var ctx = new ContextFork(this, index);
    (this.subcontext || (this.subcontext = [])).push(ctx);
    return ctx;
  },
  get: function(id) {
    return this.nodes[id];
  },
  set: function(id, node) {
    return this.nodes[id] = node;
  },
  add: function(spec, op) {
    var ctx = this,
        df = ctx.dataflow,
        data;

    ctx.set(spec.id, op);

    if (spec.type === 'Collect' && (data = spec.value)) {
      if (data.$ingest) {
        df.ingest(op, data.$ingest, data.$format);
      } else if (data.$request) {
        df.request(op, data.$request, data.$format);
      } else {
        df.pulse(op, df.changeset().insert(data));
      }
    }

    if (spec.root) {
      ctx.root = op;
    }

    if (spec.parent) {
      var p = ctx.get(spec.parent.$ref);
      df.connect(p, [op]);
      op.targets().add(p);
    }

    if (spec.signal) {
      ctx.signals[spec.signal] = op;
    }

    if (spec.scale) {
      ctx.scales[spec.scale] = op;
    }

    if (spec.data) {
      for (var name in spec.data) {
        data = ctx.data[name] || (ctx.data[name] = {});
        spec.data[name].forEach(function(role) { data[role] = op; });
      }
    }
  },
  operator: function(spec, update, params) {
    this.add(spec, this.dataflow.add(spec.value, update, params, spec.react));
  },
  transform: function(spec, type, params) {
    this.add(spec, this.dataflow.add(this.transforms[type], params));
  },
  stream: function(spec, stream) {
    this.set(spec.id, stream);
  },
  update: function(spec, stream, target, update, params) {
    this.dataflow.on(stream, target, update, params, spec.options);
  }
};
<|endoftext|>


<commit_before>/* globals controlplane: true */

/* AppsController
 * Displays top level apps
 */
(function() {
    'use strict';

    controlplane.controller("AppsController", [
        "$rootScope", "$scope", "$routeParams", "$location",
        "$notification", "resourcesFactory", "authService",
        "$modalService", "$translate", "$timeout",
        "$cookies", "miscUtils",
        "ngTableParams", "$filter",
        "Service","InternalService", "$q",
    function($rootScope, $scope, $routeParams, $location,
    $notification, resourcesFactory, authService,
    $modalService, $translate, $timeout,
    $cookies, utils,
    NgTableParams, $filter,
    Service, InternalService, $q){

        // Ensure logged in
        authService.checkLogin($scope);

        // alias hostname instead of using localhost or IP
        $scope.defaultHostAlias = $location.host();

        // redirect to specific service details
        $scope.routeToService = function(service) {
            if (service.isIsvc()) {
                resourcesFactory.routeToInternalServices();
            } else {
                resourcesFactory.routeToService(service.id);
            }
        };

        // redirect to specific pool
        $scope.routeToPool = function(id) {
            resourcesFactory.routeToPool(id);
        };

        $scope.modal_deployWizard = function() {

            // the modal occasionally won't show on page load, so we use a timeout to get around that.
            $timeout(function(){
                $('#addApp').modal('show');

                // don't auto-show this wizard again
                // NOTE: $cookies can only deal with string values
                $cookies.put("autoRunWizardHasRun","true");
            });
        };

        $scope.modal_addTemplate = function() {
            $modalService.create({
                templateUrl: "add-template.html",
                model: $scope,
                title: "template_add",
                actions: [
                    {
                        role: "cancel",
                        action: function(){
                            $scope.newHost = {};
                            this.close();
                        }
                    },{
                        role: "ok",
                        label: "template_add",
                        action: function(){
                            if(this.validate()){
                                var data = new FormData();

                                $.each($("#new_template_filename")[0].files, function(key, value){
                                    data.append("tpl", value);
                                });

                                // disable ok button, and store the re-enable function
                                var enableSubmit = this.disableSubmitButton();

                                resourcesFactory.addAppTemplate(data)
                                    .success(function(data){
                                        $notification.create("Added template", data.Detail).success();
                                        refreshTemplates();
                                        this.close();
                                    }.bind(this))
                                    .error(function(data){
                                        this.createNotification("Adding template failed", data.Detail).error();
                                        enableSubmit();
                                    }.bind(this));
                            }
                        }
                    }
                ]
            });
        };

        $scope.modal_removeService = function(service) {
            $modalService.create({
                template: $translate.instant("warning_remove_service"),
                model: $scope,
                title: "remove_service",
                actions: [
                    {
                        role: "cancel"
                    },{
                        role: "ok",
                        label: "remove_service",
                        classes: "btn-danger submit",
                        action: function(){
                            if(this.validate()){
                                this.disableSubmitButton();

                                removeService(service)
                                    .success(() => {
                                        refreshApps().then(() => {
                                            $notification.create("Removed App", service.name).success();
                                        });
                                        this.close();
                                    })
                                    .error((data, status) => {
                                        $notification.create("Remove App failed", data.Detail).error();
                                        this.close();
                                    });
                            }
                        }
                    }
                ]
            });
        };

        $scope.startService = function(service){
            $scope.modal_startStopService(service, "start");
        };
        $scope.stopService = function(service){
            $scope.modal_startStopService(service, "stop");
        };
        $scope.modal_startStopService = function(service, status) {
            var displayStatus = utils.capitalizeFirst(status);

            $modalService.create({
                template: $translate.instant("confirm_"+ status +"_app"),
                model: $scope,
                title: displayStatus +" Services",
                actions: [
                    {
                        role: "cancel"
                    },{
                        role: "ok",
                        label: displayStatus +" Services",
                        action: function(){
                            service[status]();
                            this.close();
                        }
                    }
                ]
            });
        };

        // sets a service to start, stop or restart state
        $scope.setServiceState = function(service, state, skipChildren){
            service[state](skipChildren).error(function(data, status){
                $notification.create("Unable to " + state + " service", data.Detail).error();
            });
        };

        // clicks to a service's start, stop, or restart
        // button should first determine if the service has
        // children and ask the user to choose to start all
        // children or only the top service
        $scope.clickRunning = function(service, state){
            let onStartService = function(modal){
                // the arg here explicitly prevents child services
                // from being started
                $scope.setServiceState(service, state, true);
                modal.close();
            };
            let onStartServiceAndChildren = function(modal){
                $scope.setServiceState(service, state);
                modal.close();
            };

            Service.countAffectedDescendants(service, state)
                .then(count => {
                    $modalService.modals.confirmServiceStateChange(service, state,
                        count, onStartService,
                        onStartServiceAndChildren);
                })
                .catch(err => {
                    console.warn("couldnt get descendant count", err);
                    $modalService.modals.confirmServiceStateChange(service, state,
                        0, onStartService,
                        onStartServiceAndChildren);
                });

            // let the user know they gonna have to hold onto
            // their horses for just one moment.
            $modalService.modals.oneMoment();
        };

        // verifies if use wants to start parent service, or parent
        // and all children
        $scope.modal_confirmSetServiceState = function(service, state, childCount){
            $modalService.create({
                template: ["
"+ $translate.instant("choose_services_"+ state) +"

",
                    "
"+ $translate.instant(state +"_service_name", {name: ""+service.name+""}) +"
",
                    "
"+ $translate.instant(state +"_service_name_and_children", {name: ""+service.name+"", count: ""+childCount+""}) +"

"
                ].join(""),
                model: $scope,
                title: $translate.instant(state +"_service"),
                actions: [
                    {
                        role: "cancel"
                    },{
                        role: "ok",
                        classes: " ",
                        label: $translate.instant(state +"_service"),
                        action: function(){
                            // the arg here explicitly prevents child services
                            // from being started
                            $scope.setServiceState(service, state, true);
                            this.close();
                        }
                    },{
                        role: "ok",
                        label: $translate.instant(state +"_service_and_children", {count: childCount}),
                        action: function(){
                            $scope.setServiceState(service, state);
                            this.close();
                        }
                    }
                ]
            });
        };

        $scope.modal_deleteTemplate = function(templateID){
            $modalService.create({
                template: $translate.instant("template_remove_confirm") + ""+ templateID +"",
                model: $scope,
                title: "template_remove",
                actions: [
                    {
                        role: "cancel"
                    },{
                        role: "ok",
                        label: "template_remove",
                        classes: "btn-danger",
                        action: function(){
                            deleteTemplate(templateID)
                                .success(() => {
                                    $notification.create("Removed Template", templateID).success();
                                    this.close();
                                })
                                .error((data, status) => {
                                    $notification.create("Remove Template failed", data.Detail).error();
                                });
                        }
                    }
                ]
            });
        };

        /*
         * PRIVATE FUNCTIONS
         */
        function refreshTemplates(){
            resourcesFactory.getAppTemplates().success(function(templates) {
                $scope.templates.data = utils.mapToArr(templates);
            });
        }

        function refreshApps() {
            return fetchApps()
                .then(fillOutApps)
                .then(fetchServicesHealth)
                .then(fetchInternalServicesHealth)
                .then(() => touch())
                .catch((error) => console.warn(error));
        }

        function fetchApps() {
            return $q.all([fetchInternalService(), fetchServices()])
                .then(results => {
                    $scope.apps = [];

                    var isvc = results[0];
                    $scope.apps.push(isvc);

                    var services = results[1];
                    services.forEach(s => {
                        $scope.apps.push(s);
                    });
                });
        }

        function fetchChildEndpoints(app) {
            return resourcesFactory.v2.getServiceChildPublicEndpoints(app.id).then(data => {
                app.publicEndpoints = data;
            });
        }

        function fillOutApps() {
            let promises = [];
            $scope.apps.forEach(a => {
                if (a.isIsvc()) {
                    promises.push(() => a.fetchInstances());
                } else {
                    promises.push(fetchChildEndpoints(a));
                }
            });

            return $q.all(promises);
        }

        function fetchServices() {
            let deferred = $q.defer();
            resourcesFactory.v2.getTenants().then( data => {
                    var services = data.map(s => new Service(s));
                    deferred.resolve(services);
                },
                error => {
                    console.warn(error);
                    deferred.reject();
                });
            return deferred.promise;
        }

        function fetchServicesHealth() {
            if (!$scope.apps) { return; }

            $scope.apps.forEach(app => {
                if (!app.isIsvc()) {
                    app.fetchAllStates();
                }
            });
        }

        function fetchInternalService() {
            let deferred = $q.defer();
            resourcesFactory.v2.getInternalServices()
                .then(data => {
                    let parent = data.find(i => !i.Parent);
                    if (parent) {
                        var internalServices = new InternalService(parent);
                        deferred.resolve(internalServices);
                    } else {
                        deferred.reject("Parent not found");
                    }
                },
                error => {
                    console.warn(error);
                    deferred.reject();
                });
            return deferred.promise;
        }

        function fetchInternalServicesHealth() {
            $scope.apps.forEach(app => {
                if (app.isIsvc()) {
                    app.fetchInstances().then(() => {
                        resourcesFactory.v2.getInternalServiceStatuses([app.id]).then(data => {
                            let statusMap = data.reduce((map, s) => {
                                map[s.ServiceID] = s;
                                return map;
                            }, {});

                            app.updateStatus(statusMap[app.id]);
                        });
                    });
                }
            });
        }

        function fetchGraphConfigs() {
          resourcesFactory.getStorage().then(data => {
            for (let [n, s] of data.entries()) {
              s.MonitoringProfile.GraphConfigs.forEach(g => {
                if (g.id === "storage.pool.data.usage") {
                  // in the case where we have more than one entry in storage
                  // we need our graph ids to be unique, so concat their index
                  g.id += "." + String(n)
                  $scope.graphConfigs.push(g)
                }
              });
            }
          });
        }

        // poll for apps that are being deployed
        $scope.deployingServices = [];
        var lastPollResults = 0;
        function getDeploying(){
            resourcesFactory.getDeployingTemplates().success(function(data) {
                if(data){
                    $scope.deployingServices = data;
                }

                //if we have fewer results than last poll, we need to refresh our table
                //TODO - better checking for deploying apps
                if(lastPollResults > $scope.deployingServices.length){
                    refreshApps();
                }
                lastPollResults = $scope.deployingServices.length;
            });
        }

        function removeService(service) {
            return resourcesFactory.removeService(service.id)
                .success(function(){
                    // NOTE: this is here because services are
                    // incrementally updated, which makes it impossible
                    // to determine if a service has been removed
                    // TODO - once the backend notifies on deleted
                    // services, this should be removed
                    // FIXME - should not modify servicesFactory's
                    // objects!
                    for(var i = 0; i < $scope.apps.length; i++){
                        // find the removed service and remove it
                        if($scope.apps[i].id === service.id){
                            $scope.apps.splice(i, 1);
                            touch();
                            return;
                        }
                    }
                });
        }

        function deleteTemplate(templateID){
            return resourcesFactory.removeAppTemplate(templateID)
                .success(refreshTemplates);
        }

        var lastUpdate;
        function touch() {
            lastUpdate = new Date().getTime();
        }

        // init stuff for this controller
        function init(){
            touch();

            if(utils.needsHostAlias($location.host())){
                resourcesFactory.getHostAlias().success(function(data) {
                    $scope.defaultHostAlias = data.hostalias;
                });
            }

            // configure tables
            // TODO - move table config to view/directive
            $scope.breadcrumbs = [
                { label: 'breadcrumb_deployed', itemClass: 'active' }
            ];

            $scope.servicesTable = {
                sorting: {
                    name: "asc"
                },
                searchColumns: ['name','model.Description', 'model.DeploymentID', 'model.PoolID'],
                getData: function(data, params) {
                    // use built-in angular filter
                    var orderedData = params.sorting() ?
                        $filter('orderBy')(data, params.orderBy()) :
                        data;

                    if(!orderedData){
                        return;
                    }

                    // mark any deploying services so they can be treated differently
                    orderedData.forEach(function(service){
                        service.deploying = false;
                        $scope.deployingServices.forEach(function(deploying){
                            if(service.model.DeploymentID === deploying.DeploymentID){
                                service.deploying = true;
                            }
                        });
                    });

                    return orderedData;
                },
                watchExpression: function(){
                    // TODO - check $scope.deployingServices as well
                    return lastUpdate;
                }
            };

            $scope.templates = { data: [] };
            // table config
            $scope.templatesTable = {
                sorting: {
                    Name: "asc"
                },
                searchColumns: ['Name','ID', 'Description']
            };

            // get configurations for graphs
            $scope.graphConfigs = [];
            fetchGraphConfigs();

            // Get a list of templates
            refreshTemplates();

            // check for deploying apps
            getDeploying();

            refreshApps().then(() => {
                $scope.$emit("ready");

                // if only isvcs are deployed, and this is the first time
                // running deploy wizard, show the deploy apps modal
                if(!$cookies.get("autoRunWizardHasRun") && $scope.apps.length === 1){
                    $scope.modal_deployWizard();
                }
            });

            //register polls
            resourcesFactory.registerPoll("deployingApps", getDeploying, 3000);
            resourcesFactory.registerPoll("templates", refreshTemplates, 5000);
            resourcesFactory.registerPoll("internalServiceHealth", fetchInternalServicesHealth, 3000);
            resourcesFactory.registerPoll("appHealth", fetchServicesHealth, 3000);

            //unregister polls on destroy
            $scope.$on("$destroy", function(){
                resourcesFactory.unregisterAllPolls();
            });

            // be sure to update apps table after deploywiz
            // finishes its dark magicks
            $rootScope.$on("wizard.deployed", () => {
                refreshApps();
            });
        }

        // kick this controller off
        init();

        $scope.$on("$destroy", function(){
            resourcesFactory.unregisterAllPolls();
        });
    }]);
})();
<commit_msg>semicolons...<commit_after>/* globals controlplane: true */

/* AppsController
 * Displays top level apps
 */
(function() {
    'use strict';

    controlplane.controller("AppsController", [
        "$rootScope", "$scope", "$routeParams", "$location",
        "$notification", "resourcesFactory", "authService",
        "$modalService", "$translate", "$timeout",
        "$cookies", "miscUtils",
        "ngTableParams", "$filter",
        "Service","InternalService", "$q",
    function($rootScope, $scope, $routeParams, $location,
    $notification, resourcesFactory, authService,
    $modalService, $translate, $timeout,
    $cookies, utils,
    NgTableParams, $filter,
    Service, InternalService, $q){

        // Ensure logged in
        authService.checkLogin($scope);

        // alias hostname instead of using localhost or IP
        $scope.defaultHostAlias = $location.host();

        // redirect to specific service details
        $scope.routeToService = function(service) {
            if (service.isIsvc()) {
                resourcesFactory.routeToInternalServices();
            } else {
                resourcesFactory.routeToService(service.id);
            }
        };

        // redirect to specific pool
        $scope.routeToPool = function(id) {
            resourcesFactory.routeToPool(id);
        };

        $scope.modal_deployWizard = function() {

            // the modal occasionally won't show on page load, so we use a timeout to get around that.
            $timeout(function(){
                $('#addApp').modal('show');

                // don't auto-show this wizard again
                // NOTE: $cookies can only deal with string values
                $cookies.put("autoRunWizardHasRun","true");
            });
        };

        $scope.modal_addTemplate = function() {
            $modalService.create({
                templateUrl: "add-template.html",
                model: $scope,
                title: "template_add",
                actions: [
                    {
                        role: "cancel",
                        action: function(){
                            $scope.newHost = {};
                            this.close();
                        }
                    },{
                        role: "ok",
                        label: "template_add",
                        action: function(){
                            if(this.validate()){
                                var data = new FormData();

                                $.each($("#new_template_filename")[0].files, function(key, value){
                                    data.append("tpl", value);
                                });

                                // disable ok button, and store the re-enable function
                                var enableSubmit = this.disableSubmitButton();

                                resourcesFactory.addAppTemplate(data)
                                    .success(function(data){
                                        $notification.create("Added template", data.Detail).success();
                                        refreshTemplates();
                                        this.close();
                                    }.bind(this))
                                    .error(function(data){
                                        this.createNotification("Adding template failed", data.Detail).error();
                                        enableSubmit();
                                    }.bind(this));
                            }
                        }
                    }
                ]
            });
        };

        $scope.modal_removeService = function(service) {
            $modalService.create({
                template: $translate.instant("warning_remove_service"),
                model: $scope,
                title: "remove_service",
                actions: [
                    {
                        role: "cancel"
                    },{
                        role: "ok",
                        label: "remove_service",
                        classes: "btn-danger submit",
                        action: function(){
                            if(this.validate()){
                                this.disableSubmitButton();

                                removeService(service)
                                    .success(() => {
                                        refreshApps().then(() => {
                                            $notification.create("Removed App", service.name).success();
                                        });
                                        this.close();
                                    })
                                    .error((data, status) => {
                                        $notification.create("Remove App failed", data.Detail).error();
                                        this.close();
                                    });
                            }
                        }
                    }
                ]
            });
        };

        $scope.startService = function(service){
            $scope.modal_startStopService(service, "start");
        };
        $scope.stopService = function(service){
            $scope.modal_startStopService(service, "stop");
        };
        $scope.modal_startStopService = function(service, status) {
            var displayStatus = utils.capitalizeFirst(status);

            $modalService.create({
                template: $translate.instant("confirm_"+ status +"_app"),
                model: $scope,
                title: displayStatus +" Services",
                actions: [
                    {
                        role: "cancel"
                    },{
                        role: "ok",
                        label: displayStatus +" Services",
                        action: function(){
                            service[status]();
                            this.close();
                        }
                    }
                ]
            });
        };

        // sets a service to start, stop or restart state
        $scope.setServiceState = function(service, state, skipChildren){
            service[state](skipChildren).error(function(data, status){
                $notification.create("Unable to " + state + " service", data.Detail).error();
            });
        };

        // clicks to a service's start, stop, or restart
        // button should first determine if the service has
        // children and ask the user to choose to start all
        // children or only the top service
        $scope.clickRunning = function(service, state){
            let onStartService = function(modal){
                // the arg here explicitly prevents child services
                // from being started
                $scope.setServiceState(service, state, true);
                modal.close();
            };
            let onStartServiceAndChildren = function(modal){
                $scope.setServiceState(service, state);
                modal.close();
            };

            Service.countAffectedDescendants(service, state)
                .then(count => {
                    $modalService.modals.confirmServiceStateChange(service, state,
                        count, onStartService,
                        onStartServiceAndChildren);
                })
                .catch(err => {
                    console.warn("couldnt get descendant count", err);
                    $modalService.modals.confirmServiceStateChange(service, state,
                        0, onStartService,
                        onStartServiceAndChildren);
                });

            // let the user know they gonna have to hold onto
            // their horses for just one moment.
            $modalService.modals.oneMoment();
        };

        // verifies if use wants to start parent service, or parent
        // and all children
        $scope.modal_confirmSetServiceState = function(service, state, childCount){
            $modalService.create({
                template: ["
"+ $translate.instant("choose_services_"+ state) +"

",
                    "
"+ $translate.instant(state +"_service_name", {name: ""+service.name+""}) +"
",
                    "
"+ $translate.instant(state +"_service_name_and_children", {name: ""+service.name+"", count: ""+childCount+""}) +"

"
                ].join(""),
                model: $scope,
                title: $translate.instant(state +"_service"),
                actions: [
                    {
                        role: "cancel"
                    },{
                        role: "ok",
                        classes: " ",
                        label: $translate.instant(state +"_service"),
                        action: function(){
                            // the arg here explicitly prevents child services
                            // from being started
                            $scope.setServiceState(service, state, true);
                            this.close();
                        }
                    },{
                        role: "ok",
                        label: $translate.instant(state +"_service_and_children", {count: childCount}),
                        action: function(){
                            $scope.setServiceState(service, state);
                            this.close();
                        }
                    }
                ]
            });
        };

        $scope.modal_deleteTemplate = function(templateID){
            $modalService.create({
                template: $translate.instant("template_remove_confirm") + ""+ templateID +"",
                model: $scope,
                title: "template_remove",
                actions: [
                    {
                        role: "cancel"
                    },{
                        role: "ok",
                        label: "template_remove",
                        classes: "btn-danger",
                        action: function(){
                            deleteTemplate(templateID)
                                .success(() => {
                                    $notification.create("Removed Template", templateID).success();
                                    this.close();
                                })
                                .error((data, status) => {
                                    $notification.create("Remove Template failed", data.Detail).error();
                                });
                        }
                    }
                ]
            });
        };

        /*
         * PRIVATE FUNCTIONS
         */
        function refreshTemplates(){
            resourcesFactory.getAppTemplates().success(function(templates) {
                $scope.templates.data = utils.mapToArr(templates);
            });
        }

        function refreshApps() {
            return fetchApps()
                .then(fillOutApps)
                .then(fetchServicesHealth)
                .then(fetchInternalServicesHealth)
                .then(() => touch())
                .catch((error) => console.warn(error));
        }

        function fetchApps() {
            return $q.all([fetchInternalService(), fetchServices()])
                .then(results => {
                    $scope.apps = [];

                    var isvc = results[0];
                    $scope.apps.push(isvc);

                    var services = results[1];
                    services.forEach(s => {
                        $scope.apps.push(s);
                    });
                });
        }

        function fetchChildEndpoints(app) {
            return resourcesFactory.v2.getServiceChildPublicEndpoints(app.id).then(data => {
                app.publicEndpoints = data;
            });
        }

        function fillOutApps() {
            let promises = [];
            $scope.apps.forEach(a => {
                if (a.isIsvc()) {
                    promises.push(() => a.fetchInstances());
                } else {
                    promises.push(fetchChildEndpoints(a));
                }
            });

            return $q.all(promises);
        }

        function fetchServices() {
            let deferred = $q.defer();
            resourcesFactory.v2.getTenants().then( data => {
                    var services = data.map(s => new Service(s));
                    deferred.resolve(services);
                },
                error => {
                    console.warn(error);
                    deferred.reject();
                });
            return deferred.promise;
        }

        function fetchServicesHealth() {
            if (!$scope.apps) { return; }

            $scope.apps.forEach(app => {
                if (!app.isIsvc()) {
                    app.fetchAllStates();
                }
            });
        }

        function fetchInternalService() {
            let deferred = $q.defer();
            resourcesFactory.v2.getInternalServices()
                .then(data => {
                    let parent = data.find(i => !i.Parent);
                    if (parent) {
                        var internalServices = new InternalService(parent);
                        deferred.resolve(internalServices);
                    } else {
                        deferred.reject("Parent not found");
                    }
                },
                error => {
                    console.warn(error);
                    deferred.reject();
                });
            return deferred.promise;
        }

        function fetchInternalServicesHealth() {
            $scope.apps.forEach(app => {
                if (app.isIsvc()) {
                    app.fetchInstances().then(() => {
                        resourcesFactory.v2.getInternalServiceStatuses([app.id]).then(data => {
                            let statusMap = data.reduce((map, s) => {
                                map[s.ServiceID] = s;
                                return map;
                            }, {});

                            app.updateStatus(statusMap[app.id]);
                        });
                    });
                }
            });
        }

        function fetchGraphConfigs() {
          resourcesFactory.getStorage().then(data => {
            for (let [n, s] of data.entries()) {
              s.MonitoringProfile.GraphConfigs.forEach(g => {
                if (g.id === "storage.pool.data.usage") {
                  // in the case where we have more than one entry in storage
                  // we need our graph ids to be unique, so concat their index
                  g.id += "." + String(n);
                  $scope.graphConfigs.push(g);
                }
              });
            }
          });
        }

        // poll for apps that are being deployed
        $scope.deployingServices = [];
        var lastPollResults = 0;
        function getDeploying(){
            resourcesFactory.getDeployingTemplates().success(function(data) {
                if(data){
                    $scope.deployingServices = data;
                }

                //if we have fewer results than last poll, we need to refresh our table
                //TODO - better checking for deploying apps
                if(lastPollResults > $scope.deployingServices.length){
                    refreshApps();
                }
                lastPollResults = $scope.deployingServices.length;
            });
        }

        function removeService(service) {
            return resourcesFactory.removeService(service.id)
                .success(function(){
                    // NOTE: this is here because services are
                    // incrementally updated, which makes it impossible
                    // to determine if a service has been removed
                    // TODO - once the backend notifies on deleted
                    // services, this should be removed
                    // FIXME - should not modify servicesFactory's
                    // objects!
                    for(var i = 0; i < $scope.apps.length; i++){
                        // find the removed service and remove it
                        if($scope.apps[i].id === service.id){
                            $scope.apps.splice(i, 1);
                            touch();
                            return;
                        }
                    }
                });
        }

        function deleteTemplate(templateID){
            return resourcesFactory.removeAppTemplate(templateID)
                .success(refreshTemplates);
        }

        var lastUpdate;
        function touch() {
            lastUpdate = new Date().getTime();
        }

        // init stuff for this controller
        function init(){
            touch();

            if(utils.needsHostAlias($location.host())){
                resourcesFactory.getHostAlias().success(function(data) {
                    $scope.defaultHostAlias = data.hostalias;
                });
            }

            // configure tables
            // TODO - move table config to view/directive
            $scope.breadcrumbs = [
                { label: 'breadcrumb_deployed', itemClass: 'active' }
            ];

            $scope.servicesTable = {
                sorting: {
                    name: "asc"
                },
                searchColumns: ['name','model.Description', 'model.DeploymentID', 'model.PoolID'],
                getData: function(data, params) {
                    // use built-in angular filter
                    var orderedData = params.sorting() ?
                        $filter('orderBy')(data, params.orderBy()) :
                        data;

                    if(!orderedData){
                        return;
                    }

                    // mark any deploying services so they can be treated differently
                    orderedData.forEach(function(service){
                        service.deploying = false;
                        $scope.deployingServices.forEach(function(deploying){
                            if(service.model.DeploymentID === deploying.DeploymentID){
                                service.deploying = true;
                            }
                        });
                    });

                    return orderedData;
                },
                watchExpression: function(){
                    // TODO - check $scope.deployingServices as well
                    return lastUpdate;
                }
            };

            $scope.templates = { data: [] };
            // table config
            $scope.templatesTable = {
                sorting: {
                    Name: "asc"
                },
                searchColumns: ['Name','ID', 'Description']
            };

            // get configurations for graphs
            $scope.graphConfigs = [];
            fetchGraphConfigs();

            // Get a list of templates
            refreshTemplates();

            // check for deploying apps
            getDeploying();

            refreshApps().then(() => {
                $scope.$emit("ready");

                // if only isvcs are deployed, and this is the first time
                // running deploy wizard, show the deploy apps modal
                if(!$cookies.get("autoRunWizardHasRun") && $scope.apps.length === 1){
                    $scope.modal_deployWizard();
                }
            });

            //register polls
            resourcesFactory.registerPoll("deployingApps", getDeploying, 3000);
            resourcesFactory.registerPoll("templates", refreshTemplates, 5000);
            resourcesFactory.registerPoll("internalServiceHealth", fetchInternalServicesHealth, 3000);
            resourcesFactory.registerPoll("appHealth", fetchServicesHealth, 3000);

            //unregister polls on destroy
            $scope.$on("$destroy", function(){
                resourcesFactory.unregisterAllPolls();
            });

            // be sure to update apps table after deploywiz
            // finishes its dark magicks
            $rootScope.$on("wizard.deployed", () => {
                refreshApps();
            });
        }

        // kick this controller off
        init();

        $scope.$on("$destroy", function(){
            resourcesFactory.unregisterAllPolls();
        });
    }]);
})();
<|endoftext|>


<commit_before>isic.views.DatasetsView = isic.View.extend({
    // TODO refactor
    events: {
        'show.bs.collapse .isic-listing-panel-collapse': function (event) {
            var target = $(event.target);
            target.parent().find('.icon-right-open').removeClass('icon-right-open').addClass('icon-down-open');

            var viewIndex = parseInt(target.attr('data-model-index'), 10);
            var viewContainer = target.find('.isic-listing-panel-body');
            this.renderDataset(viewIndex, viewContainer);
        },
        'hide.bs.collapse .isic-listing-panel-collapse': function (event) {
            $(event.target).parent().find('.icon-down-open').removeClass('icon-down-open').addClass('icon-right-open');
        },
        'click .isic-dataset-add-button': function () {
            isic.router.navigate('dataset/create', {trigger: true});
        }
    },

    initialize: function (settings) {
        this.loaded = false;

        this.datasets = new isic.collections.DatasetCollection();
        this.listenTo(this.datasets, 'g:changed', function () {
            this.loaded = true;
            this.render();
        }, this);
        this.datasets.fetch();

        this.paginateWidget = new girder.views.PaginateWidget({
            collection: this.datasets,
            parentView: this
        });

        this.render();
    },

    render: function () {
        this.$el.html(isic.templates.datasetsPage({
            title: 'Datasets',
            models: this.datasets.models,
            loaded: this.loaded,
            currentUser: girder.currentUser
        }));

        this.paginateWidget.setElement(this.$('.isic-listing-paginate-container')).render();

        // Display loading indicator
        if (!this.loaded) {
            this.loadingAnimation = new girder.views.LoadingAnimation({
                el: this.$('.isic-listing-loading-animation-container'),
                parentView: this
            }).render();
        } else {
            if (this.loadingAnimation) {
                this.loadingAnimation.destroy();
                delete this.loadingAnimation;
            }
        }

        return this;
    },

    renderDataset: function (index, container) {
        if (container.children().length === 0) {
            var dataset = this.datasets.at(index);

            new isic.views.DatasetView({ // eslint-disable-line no-new
                el: container,
                model: dataset,
                parentView: this
            });
        }
    }
});

isic.router.route('dataset', 'dataset', function () {
    var nextView = isic.views.DatasetsView;
    if (!isic.models.UserModel.currentUserCanAcceptTerms()) {
        nextView = isic.views.TermsAcceptanceView;
    }
    girder.events.trigger('g:navigateTo', nextView);
});
<commit_msg>Remove an incorrect parameter to "listenTo"<commit_after>isic.views.DatasetsView = isic.View.extend({
    // TODO refactor
    events: {
        'show.bs.collapse .isic-listing-panel-collapse': function (event) {
            var target = $(event.target);
            target.parent().find('.icon-right-open').removeClass('icon-right-open').addClass('icon-down-open');

            var viewIndex = parseInt(target.attr('data-model-index'), 10);
            var viewContainer = target.find('.isic-listing-panel-body');
            this.renderDataset(viewIndex, viewContainer);
        },
        'hide.bs.collapse .isic-listing-panel-collapse': function (event) {
            $(event.target).parent().find('.icon-down-open').removeClass('icon-down-open').addClass('icon-right-open');
        },
        'click .isic-dataset-add-button': function () {
            isic.router.navigate('dataset/create', {trigger: true});
        }
    },

    initialize: function (settings) {
        this.loaded = false;

        this.datasets = new isic.collections.DatasetCollection();
        this.listenTo(this.datasets, 'g:changed', function () {
            this.loaded = true;
            this.render();
        });
        this.datasets.fetch();

        this.paginateWidget = new girder.views.PaginateWidget({
            collection: this.datasets,
            parentView: this
        });

        this.render();
    },

    render: function () {
        this.$el.html(isic.templates.datasetsPage({
            title: 'Datasets',
            models: this.datasets.models,
            loaded: this.loaded,
            currentUser: girder.currentUser
        }));

        this.paginateWidget.setElement(this.$('.isic-listing-paginate-container')).render();

        // Display loading indicator
        if (!this.loaded) {
            this.loadingAnimation = new girder.views.LoadingAnimation({
                el: this.$('.isic-listing-loading-animation-container'),
                parentView: this
            }).render();
        } else {
            if (this.loadingAnimation) {
                this.loadingAnimation.destroy();
                delete this.loadingAnimation;
            }
        }

        return this;
    },

    renderDataset: function (index, container) {
        if (container.children().length === 0) {
            var dataset = this.datasets.at(index);

            new isic.views.DatasetView({ // eslint-disable-line no-new
                el: container,
                model: dataset,
                parentView: this
            });
        }
    }
});

isic.router.route('dataset', 'dataset', function () {
    var nextView = isic.views.DatasetsView;
    if (!isic.models.UserModel.currentUserCanAcceptTerms()) {
        nextView = isic.views.TermsAcceptanceView;
    }
    girder.events.trigger('g:navigateTo', nextView);
});
<|endoftext|>