<commit_before>(function($, cloudStack) {
  cloudStack.ui.widgets.browser = {};

  /**
   * Breadcrumb-related functions
   */
  var _breadcrumb = cloudStack.ui.widgets.browser.breadcrumb = {
    fixSize: function($breadcrumbContainer) {
      var width = 0;
      var containerWidth = $breadcrumbContainer.width();
      var $elems = $breadcrumbContainer.find('ul li, div');

      $elems.each(function() {
        width += $(this).width();
      });

      if (width > containerWidth) {
        $elems.filter('li').each(function() {
          var targetWidth = $(this).width() - (
            (width - containerWidth) / $elems.filter('li').size()
          ) - 10;

          $(this).width(targetWidth);

          // Concatenate title
          var $text = $(this).find('span');
          var text = $(this).attr('title');

          $text.html(
            text
              .substring(0, text.length - targetWidth / 15)
              .concat('...')
          );
        });
      } else {
        $elems.css({ width: '' });
        $elems.find('span').each(function() {
          $(this).html($(this).closest('li').attr('title'));
        });
      }
    },

    /**
     * Generate new breadcrumb
     */
    create: function($panel, title) {
      // Attach panel as ref for breadcrumb
      return cloudStack.ui.event.elem(
        'cloudBrowser', 'breadcrumb',
        $('<div>')
          .append(
            $('<li>')
              .attr({
                title: title
              })
              .append(
                $('<span>').html(title)
              )
          )
          .append($('<div>').addClass('end'))
          .children(),
        {
          panel: $panel
        }
      );
    },

    /**
     * Get breadcrumbs matching specified panels
     */
    filter: function($panels) {
      var $breadcrumbs = $('#breadcrumbs ul li');
      var $result = $([]);

      $panels.each(function() {
        var $panel = $(this);

        $.merge(
          $result,
          $.merge(
            $breadcrumbs.filter(function() {
              return $(this).index('#breadcrumbs ul li') == $panel.index();
            }),

            // Also include ends
            $breadcrumbs.siblings('div.end').filter(function() {
              return $(this).index('div.end') == $panel.index() + 1;
            })
          )
        );
      });

      return $result;
    }
  };

  /**
   * Container-related functions
   */
  var _container = cloudStack.ui.widgets.browser.container = {
    /**
     * Get all panels from container
     */
    panels: function($container) {
      return $container.find('div.panel');
    }
  };

  /**
   * Panel-related functions
   */
  var _panel = cloudStack.ui.widgets.browser.panel = {
    /**
     * Compute width of panel, relative to container
     */
    width: function($container, options) {
      options = options ? options : {};
      var width = $container.find('div.panel').size() < 1 || options.maximized == true ?
        $container.width() : $container.width() - $container.width() / 4;

      return width;
    },

    /**
     * Get left position
     */
    position: function($container, options) {
      return $container.find('div.panel').size() <= 1 || options.maximized == true ?
        0 : _panel.width($container, options) - _panel.width($container, options) / 1.5;
    },

    /**
     * Get the top panel z-index, for proper stacking
     */
    topIndex: function($container) {
      var base = 50; // Minimum z-index

      return Math.max.apply(
        null,
        $.map(
          $container.find('div.panel'),
          function(elem) {
            return parseInt($(elem).css('z-index')) || base;
          }
        )
      ) + 1;
    },

    /**
     * State when panel is outside container
     */
    initialState: function($container) {
      return {
        left: $container.width()
      };
    },

    /**
     * Get panel and breadcrumb behind specific panel
     */
    lower: function($container, $panel) {
      return _container.panels($container).filter(function() {
        return $(this).index() < $panel.index();
      });
    },

    /**
     * Get panel and breadcrumb stacked above specific panel
     */
    higher: function($container, $panel) {
      return _container.panels($container).filter(function() {
        return $(this).index() > $panel.index();
      });
    },

    /**
     * Generate new panel
     */
    create: function($container, options) {
      var $panel = $('<div>').addClass('panel').css(
        {
          position: 'absolute',
          width: _panel.width($container, { maximized: options.maximized }),
          zIndex: _panel.topIndex($container)
        }
      ).append(
        // Shadow
        $('<div>').addClass('shadow')
      ).append(options.data);

      if (options.maximized) $panel.addClass('always-maximized');

      return $panel;
    }
  };

  /**
   * Browser -- jQuery widget
   */
  $.widget('cloudStack.cloudBrowser', {
    _init: function() {
      this.element.addClass('cloudStack-widget cloudBrowser');
      $('#breadcrumbs').append(
        $('<ul>')
      );
    },

    /**
     * Make target panel the top-most
     */
    selectPanel: function(args) {
      var $panel = args.panel;
      var $container = this.element;
      var $toShow = _panel.lower($container, $panel);
      var $toRemove = _panel.higher($container, $panel);
      var complete = args.complete;

      if ($panel.hasClass('maximized')) return false;

      _breadcrumb.filter($toRemove).remove();
      _breadcrumb.filter($panel.siblings()).removeClass('active');
      _breadcrumb.filter($panel).addClass('active');
      _breadcrumb.filter($('div.panel')).find('span').animate({
        opacity: 1
      });
      _breadcrumb.filter(
        $('div.panel.maximized')
          .removeClass('maximized')
          .addClass('reduced')
      ).removeClass('active maximized');
      _breadcrumb.fixSize($('#breadcrumbs'));

      $toRemove.animate(
        _panel.initialState($container),
        {
          duration: 500,
          complete: function() {
            $(this).remove();

            if (complete) complete($toShow);
          }
       }
      );
      $toShow.show();
      $panel.animate({
        left: _panel.position($container, { maximized: $panel.hasClass('always-maximized') })
      });
      $panel.show().removeClass('reduced');
    },

    /**
     * Toggle selected panel as fully expanded, hiding/showing other panels
     */
    toggleMaximizePanel: function(args) {
      var $panel = args.panel;
      var $container = this.element;
      var $toHide = $panel.siblings(':not(.always-maximized)');

      if (args.panel.hasClass('maximized')) {
        _breadcrumb.filter($panel).removeClass('maximized');
        $panel.removeClass('maximized');
        $panel.addClass('reduced');
        _breadcrumb.filter($panel.siblings()).find('span').animate({ opacity: 1 });
        $toHide.animate({ left: _panel.position($container, {}) },
                        { duration: 500 });
      } else {
        _breadcrumb.filter($panel).addClass('maximized');
        $panel.removeClass('reduced');
        $panel.addClass('maximized');
        _breadcrumb.filter($panel.siblings()).find('span').animate({ opacity: 0.5 });
        $toHide.animate(_panel.initialState($container),
                        { duration: 500 });
      }
    },

    /**
     * Append new panel to end of container
     */
    addPanel: function(args) {
      var duration = args.duration ? args.duration : 500;
      var $container = this.element;
      var $parent = args.parent;
      var $panel, $reduced, targetPosition;

      // Create panel
      $panel = _panel.create(this.element, {
        maximized: args.maximizeIfSelected,
        data: args.data
      });

      // Remove existing panels from parent
      if ($parent) {
        // Cleanup transitioning panels -- prevent old complete actions from running
        $parent.siblings().stop();

        _breadcrumb.filter(
          $('div.panel.maximized')
            .removeClass('maximized')
            .addClass('reduced')
        ).removeClass('active maximized');

        $parent.removeClass('maximized');
        _breadcrumb.filter($parent.next()).remove();
        $container.find($parent.next()).remove();
      }

      // Append panel
      $panel.appendTo($container);
      _breadcrumb.filter($panel.siblings()).removeClass('active');
      _breadcrumb.create($panel, args.title)
        .addClass('active')
        .appendTo('#breadcrumbs ul');

      _breadcrumb.fixSize($('#breadcrumbs'));

      // Reduced appearance for previous panels
      $panel.siblings().filter(function() {
        return $(this).index() < $panel.index();
      }).addClass('reduced');

      // Panel initial state
      if ($panel.index() == 0) $panel.addClass('always-maximized');
      $panel.css(
        _panel.initialState($container, $panel)
      );

      // Panel slide-in
      targetPosition = _panel.position($container, {
        maximized: args.maximizeIfSelected
      });
      if (!$panel.index()) {
        // Just show immediately if this is the first panel
        $panel.css(
          { left: targetPosition }
        );
        if (args.complete) args.complete($panel);
      } else {
        // Animate slide-in
        $panel.animate({ left: targetPosition }, {
          duration: duration,
          easing: 'easeOutCirc',
          complete: function() {
            // Hide panels
            $panel.siblings().filter(function() {
              return $(this).width() == $panel.width();
            });

            if ($panel.is(':visible') && args.complete) args.complete($panel);
          }
        });
      };

      return $panel;
    },

    /**
     * Clear all panels
     */
    removeAllPanels: function(args) {
      this.element.find('div.panel').remove();
      $('#breadcrumbs').find('ul li').remove();
      $('#breadcrumbs').find('ul div.end').remove();
      _breadcrumb.fixSize($('#breadcrumbs'));
    }
  });

  $('#breadcrumbs li').live('click', cloudStack.ui.event.bind(
    'cloudBrowser',
    {
      'breadcrumb': function($target, $browser, data) {
        $browser.cloudBrowser('selectPanel', { panel: data.panel });
      }
    }
  ));
})(jQuery, cloudStack);
<commit_msg>UI: Browser widget fix<commit_after>(function($, cloudStack) {
  cloudStack.ui.widgets.browser = {};

  /**
   * Breadcrumb-related functions
   */
  var _breadcrumb = cloudStack.ui.widgets.browser.breadcrumb = {
    fixSize: function($breadcrumbContainer) {
      var width = 0;
      var containerWidth = $breadcrumbContainer.width();
      var $elems = $breadcrumbContainer.find('ul li, div');

      $elems.each(function() {
        width += $(this).width();
      });

      if (width > containerWidth) {
        $elems.filter('li').each(function() {
          var targetWidth = $(this).width() - (
            (width - containerWidth) / $elems.filter('li').size()
          ) - 10;

          $(this).width(targetWidth);

          // Concatenate title
          var $text = $(this).find('span');
          var text = $(this).attr('title');

          $text.html(
            text
              .substring(0, text.length - targetWidth / 15)
              .concat('...')
          );
        });
      } else {
        $elems.css({ width: '' });
        $elems.find('span').each(function() {
          $(this).html($(this).closest('li').attr('title'));
        });
      }
    },

    /**
     * Generate new breadcrumb
     */
    create: function($panel, title) {
      // Attach panel as ref for breadcrumb
      return cloudStack.ui.event.elem(
        'cloudBrowser', 'breadcrumb',
        $('<div>')
          .append(
            $('<li>')
              .attr({
                title: title
              })
              .append(
                $('<span>').html(title)
              )
          )
          .append($('<div>').addClass('end'))
          .children(),
        {
          panel: $panel
        }
      );
    },

    /**
     * Get breadcrumbs matching specified panels
     */
    filter: function($panels) {
      var $breadcrumbs = $('#breadcrumbs ul li');
      var $result = $([]);

      $panels.each(function() {
        var $panel = $(this);

        $.merge(
          $result,
          $.merge(
            $breadcrumbs.filter(function() {
              return $(this).index('#breadcrumbs ul li') == $panel.index();
            }),

            // Also include ends
            $breadcrumbs.siblings('div.end').filter(function() {
              return $(this).index('div.end') == $panel.index() + 1;
            })
          )
        );
      });

      return $result;
    }
  };

  /**
   * Container-related functions
   */
  var _container = cloudStack.ui.widgets.browser.container = {
    /**
     * Get all panels from container
     */
    panels: function($container) {
      return $container.find('div.panel');
    }
  };

  /**
   * Panel-related functions
   */
  var _panel = cloudStack.ui.widgets.browser.panel = {
    /**
     * Compute width of panel, relative to container
     */
    width: function($container, options) {
      options = options ? options : {};
      var width = $container.find('div.panel').size() < 1 || options.maximized == true ?
        $container.width() : $container.width() - $container.width() / 4;

      return width;
    },

    /**
     * Get left position
     */
    position: function($container, options) {
      return $container.find('div.panel').size() <= 1 || options.maximized == true ?
        0 : _panel.width($container, options) - _panel.width($container, options) / 1.5;
    },

    /**
     * Get the top panel z-index, for proper stacking
     */
    topIndex: function($container) {
      var base = 50; // Minimum z-index

      return Math.max.apply(
        null,
        $.map(
          $container.find('div.panel'),
          function(elem) {
            return parseInt($(elem).css('z-index')) || base;
          }
        )
      ) + 1;
    },

    /**
     * State when panel is outside container
     */
    initialState: function($container) {
      return {
        left: $container.width()
      };
    },

    /**
     * Get panel and breadcrumb behind specific panel
     */
    lower: function($container, $panel) {
      return _container.panels($container).filter(function() {
        return $(this).index() < $panel.index();
      });
    },

    /**
     * Get panel and breadcrumb stacked above specific panel
     */
    higher: function($container, $panel) {
      return _container.panels($container).filter(function() {
        return $(this).index() > $panel.index();
      });
    },

    /**
     * Generate new panel
     */
    create: function($container, options) {
      var $panel = $('<div>').addClass('panel').css(
        {
          position: 'absolute',
          width: _panel.width($container, { maximized: options.maximized }),
          zIndex: _panel.topIndex($container)
        }
      ).append(
        // Shadow
        $('<div>').addClass('shadow')
      ).append(options.data);

      if (options.maximized) $panel.addClass('always-maximized');

      return $panel;
    }
  };

  /**
   * Browser -- jQuery widget
   */
  $.widget('cloudStack.cloudBrowser', {
    _init: function() {
      this.element.addClass('cloudStack-widget cloudBrowser');
      $('#breadcrumbs').append(
        $('<ul>')
      );
    },

    /**
     * Make target panel the top-most
     */
    selectPanel: function(args) {
      var $panel = args.panel;
      var $container = this.element;
      var $toShow = _panel.lower($container, $panel);
      var $toRemove = _panel.higher($container, $panel);
      var complete = args.complete;

      if ($panel.hasClass('maximized')) return false;

      _breadcrumb.filter($toRemove).remove();
      _breadcrumb.filter($panel.siblings()).removeClass('active');
      _breadcrumb.filter($panel).addClass('active');
      _breadcrumb.filter($('div.panel')).find('span').animate({
        opacity: 1
      });
      _breadcrumb.filter(
        $('div.panel.maximized')
          .removeClass('maximized')
          .addClass('reduced')
      ).removeClass('active maximized');
      _breadcrumb.fixSize($('#breadcrumbs'));

      $toRemove.animate(
        _panel.initialState($container),
        {
          duration: 500,
          complete: function() {
            $(this).remove();

            if (complete) complete($toShow);
          }
       }
      );
      $toShow.show();
      $panel.animate({
        left: _panel.position($container, { maximized: $panel.hasClass('always-maximized') })
      });
      $panel.show().removeClass('reduced');
    },

    /**
     * Toggle selected panel as fully expanded, hiding/showing other panels
     */
    toggleMaximizePanel: function(args) {
      var $panel = args.panel;
      var $container = this.element;
      var $toHide = $panel.siblings(':not(.always-maximized)');

      if (args.panel.hasClass('maximized')) {
        _breadcrumb.filter($panel).removeClass('maximized');
        $panel.removeClass('maximized');
        $panel.addClass('reduced');
        _breadcrumb.filter($panel.siblings()).find('span').animate({ opacity: 1 });
        $toHide.animate({ left: _panel.position($container, {}) },
                        { duration: 500 });
      } else {
        _breadcrumb.filter($panel).addClass('maximized');
        $panel.removeClass('reduced');
        $panel.addClass('maximized');
        _breadcrumb.filter($panel.siblings()).find('span').animate({ opacity: 0.5 });
        $toHide.animate(_panel.initialState($container),
                        { duration: 500 });
      }
    },

    /**
     * Append new panel to end of container
     */
    addPanel: function(args) {
      var duration = args.duration ? args.duration : 500;
      var $container = this.element;
      var $parent = args.parent;
      var $panel, $reduced, targetPosition;

      // Create panel
      $panel = _panel.create(this.element, {
        maximized: args.maximizeIfSelected,
        data: args.data
      });

      // Remove existing panels from parent
      if ($parent) {
        // Cleanup transitioning panels -- prevent old complete actions from running
        $parent.siblings().stop();

        _breadcrumb.filter(
          $('div.panel.maximized')
            .removeClass('maximized')
            .addClass('reduced')
        ).removeClass('active maximized');

        $parent.removeClass('maximized');
        _breadcrumb.filter($parent.next()).remove();
        $container.find($parent.next()).remove();
      }

      // Append panel
      $panel.appendTo($container);
      _breadcrumb.filter($panel.siblings()).removeClass('active');
      _breadcrumb.create($panel, args.title)
        .addClass('active')
        .appendTo('#breadcrumbs ul');

      _breadcrumb.fixSize($('#breadcrumbs'));

      // Reduced appearance for previous panels
      $panel.siblings().filter(function() {
        return $(this).index() < $panel.index();
      }).addClass('reduced');

      // Panel initial state
      if ($panel.index() == 0) $panel.addClass('always-maximized');
      $panel.css(
        _panel.initialState($container, $panel)
      );

      // Panel slide-in
      targetPosition = _panel.position($container, {
        maximized: args.maximizeIfSelected
      });
      if (!$panel.index()) {
        // Just show immediately if this is the first panel
        $panel.css(
          { left: targetPosition }
        );
        if (args.complete) args.complete($panel);
      } else {
        // Animate slide-in
        $panel.animate({ left: targetPosition }, {
          duration: duration,
          easing: 'easeOutCirc',
          complete: function() {
            // Hide panels
            $panel.siblings().filter(function() {
              return $(this).width() == $panel.width();
            });

            if ($panel.is(':visible') && args.complete) args.complete($panel);
          }
        });
      };

      return $panel;
    },

    /**
     * Clear all panels
     */
    removeAllPanels: function(args) {
      $('div.panel').stop(); // Prevent destroyed panels from animating
      this.element.find('div.panel').remove();
      $('#breadcrumbs').find('ul li').remove();
      $('#breadcrumbs').find('ul div.end').remove();
      _breadcrumb.fixSize($('#breadcrumbs'));
    }
  });

  $('#breadcrumbs li').live('click', cloudStack.ui.event.bind(
    'cloudBrowser',
    {
      'breadcrumb': function($target, $browser, data) {
        $browser.cloudBrowser('selectPanel', { panel: data.panel });
      }
    }
  ));
})(jQuery, cloudStack);
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react'

import UserRow from 'src/admin/components/UserRow'
import EmptyRow from 'src/admin/components/EmptyRow'

const newDefaultUser = {
  name: '',
  password: '',
  roles: [],
  permissions: [],
}

const isValid = (user) => {
  return (user.name.length >= 3 && user.password.length >= 3)
}

class UsersTable extends Component {
  constructor(props) {
    super(props)

    this.state = {
      isAddingUser: false,
      newUser: {...newDefaultUser},
    }

    this.handleClearNewUser = ::this.handleClearNewUser
    this.handleSubmitNewUser = ::this.handleSubmitNewUser
    this.handleInputChange = ::this.handleInputChange
    this.handleInputKeyPress = ::this.handleInputKeyPress
  }

  handleSubmitNewUser() {
    const {source, addUser, addFlashMessage} = this.props
    const {newUser} = this.state
    if (isValid(newUser)) {
      addUser(source.links.users, newUser)
      this.handleClearNewUser()
    } else {
      addFlashMessage({type: 'error', text: 'Username and/or password too short'})
    }
  }

  handleClearNewUser() {
    this.setState({
      isAddingUser: false,
      newUser: {...newDefaultUser},
    })
  }

  handleInputChange(e) {
    const newUser = Object.assign({}, this.state.newUser, {[e.target.name]: e.target.value})

    this.setState({newUser})
  }

  handleInputKeyPress(e) {
    if (e.key === 'Enter') {
      this.handleSubmitNewUser()
    }
  }

  render() {
    const {users} = this.props
    const {isAddingUser} = this.state

    return (

            <input
              type="text"
              className="form-control"
              placeholder="Filter Role..."
            />

          <button
            className="btn btn-primary"
            onClick={() => this.setState({isAddingUser: true})}
          >Create User</button>

                User
                Roles
                Permissions

              {
                isAddingUser ?
                  <UserRow
                    user={this.state.newUser}
                    isEditing={true}
                    onCancel={this.handleClearNewUser}
                    onSave={this.handleSubmitNewUser}
                    onInputChange={this.handleInputChange}
                    onInputKeyPress={this.handleInputKeyPress}
                  />
                  : null
              }
              {
                users.length ?
                  users.map((user, i) =>
                    <UserRow
                      key={i}
                      user={user}
                    />
                  ) : <EmptyRow tableName={'Users'} />
              }

    )
  }
}

const {
  arrayOf,
  func,
  shape,
  string,
} = PropTypes

UsersTable.propTypes = {
  users: arrayOf(shape({
    name: string.isRequired,
    roles: arrayOf(shape({
      name: string,
    })),
    permissions: arrayOf(shape({
      name: string,
      scope: string.isRequired,
    })),
  })),
  source: shape(),
  addUser: func.isRequired,
  addFlashMessage: func.isRequired,
}

export default UsersTable
<commit_msg>Clean up basic validation<commit_after>import React, {Component, PropTypes} from 'react'

import UserRow from 'src/admin/components/UserRow'
import EmptyRow from 'src/admin/components/EmptyRow'

const newDefaultUser = {
  name: '',
  password: '',
  roles: [],
  permissions: [],
}

const isValid = (user) => {
  const minLen = 3
  return (user.name.length >= minLen && user.password.length >= minLen)
}

class UsersTable extends Component {
  constructor(props) {
    super(props)

    this.state = {
      isAddingUser: false,
      newUser: {...newDefaultUser},
    }

    this.handleClearNewUser = ::this.handleClearNewUser
    this.handleSubmitNewUser = ::this.handleSubmitNewUser
    this.handleInputChange = ::this.handleInputChange
    this.handleInputKeyPress = ::this.handleInputKeyPress
  }

  handleSubmitNewUser() {
    const {source, addUser, addFlashMessage} = this.props
    const {newUser} = this.state
    if (isValid(newUser)) {
      addUser(source.links.users, newUser)
      this.handleClearNewUser()
    } else {
      addFlashMessage({type: 'error', text: 'Username and/or password too short'})
    }
  }

  handleClearNewUser() {
    this.setState({
      isAddingUser: false,
      newUser: {...newDefaultUser},
    })
  }

  handleInputChange(e) {
    const newUser = Object.assign({}, this.state.newUser, {[e.target.name]: e.target.value})

    this.setState({newUser})
  }

  handleInputKeyPress(e) {
    if (e.key === 'Enter') {
      this.handleSubmitNewUser()
    }
  }

  render() {
    const {users} = this.props
    const {isAddingUser} = this.state

    return (

            <input
              type="text"
              className="form-control"
              placeholder="Filter Role..."
            />

          <button
            className="btn btn-primary"
            onClick={() => this.setState({isAddingUser: true})}
          >Create User</button>

                User
                Roles
                Permissions

              {
                isAddingUser ?
                  <UserRow
                    user={this.state.newUser}
                    isEditing={true}
                    onCancel={this.handleClearNewUser}
                    onSave={this.handleSubmitNewUser}
                    onInputChange={this.handleInputChange}
                    onInputKeyPress={this.handleInputKeyPress}
                  />
                  : null
              }
              {
                users.length ?
                  users.map((user, i) =>
                    <UserRow
                      key={i}
                      user={user}
                    />
                  ) : <EmptyRow tableName={'Users'} />
              }

    )
  }
}

const {
  arrayOf,
  func,
  shape,
  string,
} = PropTypes

UsersTable.propTypes = {
  users: arrayOf(shape({
    name: string.isRequired,
    roles: arrayOf(shape({
      name: string,
    })),
    permissions: arrayOf(shape({
      name: string,
      scope: string.isRequired,
    })),
  })),
  source: shape(),
  addUser: func.isRequired,
  addFlashMessage: func.isRequired,
}

export default UsersTable
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react';
import Dygraph from './Dygraph';
import classNames from 'classnames';
import shallowCompare from 'react-addons-shallow-compare';
import _ from 'lodash';

import timeSeriesToDygraph from 'utils/timeSeriesToDygraph';
import lastValues from 'src/shared/parsing/lastValues';

const {
  array,
  arrayOf,
  bool,
  func,
  number,
  shape,
  string,
} = PropTypes;

export default React.createClass({
  displayName: 'LineGraph',
  propTypes: {
    data: arrayOf(shape({}).isRequired).isRequired,
    ranges: shape({
      y: arrayOf(number),
      y2: arrayOf(number),
    }),
    title: string,
    isFetchingInitially: bool,
    isRefreshing: bool,
    underlayCallback: func,
    isGraphFilled: bool,
    overrideLineColors: array,
    queries: arrayOf(shape({}).isRequired).isRequired,
    showSingleStat: bool,
    activeQueryIndex: number,
    ruleValues: shape({}),
    isInDataExplorer: bool,
  },

  getDefaultProps() {
    return {
      underlayCallback: () => {},
      isGraphFilled: true,
      overrideLineColors: null,
    };
  },

  shouldComponentUpdate(nextProps, nextState) {
    return shallowCompare(this, nextProps, nextState);
  },

  componentWillMount() {
    const {data, activeQueryIndex, isInDataExplorer} = this.props;
    this._timeSeries = timeSeriesToDygraph(data, activeQueryIndex, isInDataExplorer);
  },

  componentWillUpdate(nextProps) {
    const {data, activeQueryIndex} = this.props;
    if (data !== nextProps.data || activeQueryIndex !== nextProps.activeQueryIndex) {
      this._timeSeries = timeSeriesToDygraph(nextProps.data, nextProps.activeQueryIndex, nextProps.isInDataExplorer);
    }
  },

  render() {
    const {data, ranges, isFetchingInitially, isRefreshing, isGraphFilled, overrideLineColors, title, underlayCallback, queries, showSingleStat, ruleValues} = this.props;
    const {labels, timeSeries, dygraphSeries} = this._timeSeries;

    // If data for this graph is being fetched for the first time, show a graph-wide spinner.
    if (isFetchingInitially) {
      return (

      );
    }

    const options = {
      labels,
      connectSeparatedPoints: true,
      labelsKMB: true,
      axisLineColor: '#383846',
      gridLineColor: '#383846',
      title,
      rightGap: 0,
      yRangePad: 10,
      axisLabelWidth: 38,
      drawAxesAtZero: true,
      underlayCallback,
      ylabel: _.get(queries, ['0', 'label'], ''),
      y2label: _.get(queries, ['1', 'label'], ''),
    };

    let roundedValue;
    if (showSingleStat) {
      const lastValue = lastValues(data)[1];

      const precision = 100.0;
      roundedValue = Math.round(+lastValue * precision) / precision;
    }

    return (

        {this.renderSpinner()}
        <Dygraph
          containerStyle={{width: '100%', height: '100%'}}
          overrideLineColors={overrideLineColors}
          isGraphFilled={isGraphFilled}
          timeSeries={timeSeries}
          labels={labels}
          options={options}
          dygraphSeries={dygraphSeries}
          ranges={ranges || this.getRanges()}
          ruleValues={ruleValues}
        />
        {showSingleStat ?
{roundedValue}
 : null}

    );
  },

  renderSpinner() {
    return (

    );
  },

  getRanges() {
    const {queries} = this.props;
    if (!queries) {
      return {};
    }

    const ranges = {};
    const q0 = queries[0];
    const q1 = queries[1];

    if (q0 && q0.range) {
      ranges.y = [q0.range.lower, q0.range.upper];
    }

    if (q1 && q1.range) {
      ranges.y2 = [q1.range.lower, q1.range.upper];
    }

    return ranges;
  },
});
<commit_msg>Undo debug mode on graph spinner<commit_after>import React, {PropTypes} from 'react';
import Dygraph from './Dygraph';
import classNames from 'classnames';
import shallowCompare from 'react-addons-shallow-compare';
import _ from 'lodash';

import timeSeriesToDygraph from 'utils/timeSeriesToDygraph';
import lastValues from 'src/shared/parsing/lastValues';

const {
  array,
  arrayOf,
  bool,
  func,
  number,
  shape,
  string,
} = PropTypes;

export default React.createClass({
  displayName: 'LineGraph',
  propTypes: {
    data: arrayOf(shape({}).isRequired).isRequired,
    ranges: shape({
      y: arrayOf(number),
      y2: arrayOf(number),
    }),
    title: string,
    isFetchingInitially: bool,
    isRefreshing: bool,
    underlayCallback: func,
    isGraphFilled: bool,
    overrideLineColors: array,
    queries: arrayOf(shape({}).isRequired).isRequired,
    showSingleStat: bool,
    activeQueryIndex: number,
    ruleValues: shape({}),
    isInDataExplorer: bool,
  },

  getDefaultProps() {
    return {
      underlayCallback: () => {},
      isGraphFilled: true,
      overrideLineColors: null,
    };
  },

  shouldComponentUpdate(nextProps, nextState) {
    return shallowCompare(this, nextProps, nextState);
  },

  componentWillMount() {
    const {data, activeQueryIndex, isInDataExplorer} = this.props;
    this._timeSeries = timeSeriesToDygraph(data, activeQueryIndex, isInDataExplorer);
  },

  componentWillUpdate(nextProps) {
    const {data, activeQueryIndex} = this.props;
    if (data !== nextProps.data || activeQueryIndex !== nextProps.activeQueryIndex) {
      this._timeSeries = timeSeriesToDygraph(nextProps.data, nextProps.activeQueryIndex, nextProps.isInDataExplorer);
    }
  },

  render() {
    const {data, ranges, isFetchingInitially, isRefreshing, isGraphFilled, overrideLineColors, title, underlayCallback, queries, showSingleStat, ruleValues} = this.props;
    const {labels, timeSeries, dygraphSeries} = this._timeSeries;

    // If data for this graph is being fetched for the first time, show a graph-wide spinner.
    if (isFetchingInitially) {
      return (

      );
    }

    const options = {
      labels,
      connectSeparatedPoints: true,
      labelsKMB: true,
      axisLineColor: '#383846',
      gridLineColor: '#383846',
      title,
      rightGap: 0,
      yRangePad: 10,
      axisLabelWidth: 38,
      drawAxesAtZero: true,
      underlayCallback,
      ylabel: _.get(queries, ['0', 'label'], ''),
      y2label: _.get(queries, ['1', 'label'], ''),
    };

    let roundedValue;
    if (showSingleStat) {
      const lastValue = lastValues(data)[1];

      const precision = 100.0;
      roundedValue = Math.round(+lastValue * precision) / precision;
    }

    return (

        {isRefreshing ? this.renderSpinner() : null}
        <Dygraph
          containerStyle={{width: '100%', height: '100%'}}
          overrideLineColors={overrideLineColors}
          isGraphFilled={isGraphFilled}
          timeSeries={timeSeries}
          labels={labels}
          options={options}
          dygraphSeries={dygraphSeries}
          ranges={ranges || this.getRanges()}
          ruleValues={ruleValues}
        />
        {showSingleStat ?
{roundedValue}
 : null}

    );
  },

  renderSpinner() {
    return (

    );
  },

  getRanges() {
    const {queries} = this.props;
    if (!queries) {
      return {};
    }

    const ranges = {};
    const q0 = queries[0];
    const q1 = queries[1];

    if (q0 && q0.range) {
      ranges.y = [q0.range.lower, q0.range.upper];
    }

    if (q1 && q1.range) {
      ranges.y2 = [q1.range.lower, q1.range.upper];
    }

    return ranges;
  },
});
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import chroma from 'chroma-js'
import titleCase from 'title-case'
import styled from 'styled-components'
import {Box, Flex, Heading, Text} from '@primer/components'
import {colors, variables, getBackgroundPalette} from './color-variables'

export {gradientPalettes} from './color-variables'

const {black, white} = colors
export {black, white}

export function ColorVariable({hue, ...rest}) {
  const values = colors[hue]
  return (
    <Flex.Item as={Box} minWidth={240} pr={4} mb={6} className="col-12 col-md-6 markdown-no-margin" {...rest}>
      <Box bg={colors[hue][5]} my={2} p={3} color="white">
        <Heading as="div" pb={3} fontSize={56} fontWeight="light">
          {titleCase(hue)}
        </Heading>
        <Flex justifyContent="space-between">
          <Flex.Item as={Var} fontWeight="bold">
            ${hue}-500
          </Flex.Item>
          <Text justifySelf="end" fontFamily="mono">
            {values[5]}
          </Text>
        </Flex>
      </Box>
      {values.map((value, index) => (
        <Swatch name={hue} index={index} key={value} />
      ))}
    </Flex.Item>
  )
}

ColorVariable.propTypes = {
  hue: PropTypes.oneOf(Object.keys(colors)).isRequired
}

export function FadeVariables({hue, color, bg, over, children, ...rest}) {
  const colorValue = colors[hue]
  const alphas = [15, 30, 50, 70, 85]
  const values = alphas.map(alpha => {
    const value = chroma(colorValue)
      .alpha(alpha / 100)
      .css()
    return {
      variable: `${hue}-fade-${alpha}`,
      textColor: fadeTextColor(value, over),
      value
    }
  })
  const boxProps = {color, bg}
  return (
    <Flex.Item as={Box} pr={4} mb={6} width={1 / 2} className="markdown-no-margin" {...rest}>
      {/* <Heading as="div">{titleCase(hue)}</Heading> */}
      <Box my={2} p={3} {...boxProps}>
        <Heading as="div" pb={3} fontSize={56} fontWeight="light">
          {titleCase(hue)}
        </Heading>
        <Flex justifyContent="space-between">
          <Flex.Item flex="1 1 auto" as={Var} fontWeight="bold">
            ${hue}
          </Flex.Item>
          <Text fontFamily="mono">
            {chroma(colorValue).css()}
            {' / '}
            {colorValue}
          </Text>
        </Flex>
        {children}
      </Box>
      <Box bg={over}>
        {values.map(swatchProps => (
          <Swatch {...swatchProps} key={swatchProps.name} />
        ))}
      </Box>
    </Flex.Item>
  )
}

FadeVariables.propTypes = {
  bg: Box.propTypes.color,
  color: Box.propTypes.color,
  hue: PropTypes.oneOf(['black', 'white']),
  over: PropTypes.string
}

export function Swatch(props) {
  const {
    name,
    index,
    value = colors[name][index],
    textColor = overlayColor(value),
    variable = `${name}-${index}00`,
    children,
    ...rest
  } = props

  return (
    <Flex bg={value} color={textColor} {...rest}>
      {children}
      {variable ? (
        <Box as={Var} pr={4}>
          ${variable}
        </Box>
      ) : null}
      <Box as={Text} fontFamily="mono">
        {value}
      </Box>
    </Flex>
  )
}

Swatch.defaultProps = {
  as: Text,
  flexWrap: 'wrap',
  fontSize: 1,
  justifyContent: 'space-between',
  p: 3
}

Swatch.propTypes = {
  index: PropTypes.number.isRequired,
  name: PropTypes.string.isRequired,
  textColor: PropTypes.string,
  value: PropTypes.string.isRequired
}

export function BackgroundHueSwatches({hue, ...rest}) {
  const namedUtilities = Object.keys(variables).filter(name => name.startsWith(`bg-${hue}`))

  const namedUtilitiesByValue = {}
  for (const name of namedUtilities) {
    const value = variables[name]
    namedUtilitiesByValue[value] = name
  }

  const {values} = getBackgroundPalette(hue)
  const rows = values.map(({value, ...rest}) => ({
    value,
    named: namedUtilitiesByValue[value],
    ...rest
  }))

  return (
    <ColorTable {...rest}>

          Default classes
          Extended classes
          Variable
          CSS value

        {rows.map(({value, named, slug, variable}) => {
          const overlay = overlayColor(value)
          return (
            <ColorRow key={slug}>
              <ColorCell bg={named ? value : null} color={overlay}>
                {named ? `.${named}` : null}
              </ColorCell>
              <ColorCell bg={value} color={overlay}>
                .bg-{slug}
              </ColorCell>
              <ColorCell bg={value} color={overlay}>
                <Var>${variable}</Var>
              </ColorCell>
              <ColorCell bg={value} color={overlay}>
                <Text fontFamily="mono">{value}</Text>
              </ColorCell>
            </ColorRow>
          )
        })}

    </ColorTable>
  )
}

export function Var(props) {
  return <Text as="var" fontFamily="mono" fontStyle="normal" {...props} />
}

export function overlayColor(bg) {
  return chroma(bg).luminance() > 0.5 ? black : white
}

const ColorTable = styled.table`
  display: table !important;
  table-layout: fixed;
  border-collapse: separate;
  border-spacing: 0 4px;

  tr,
  td,
  th {
    border: 0 !important;
  }

  td,
  th {
    text-align: left;
    width: 25%;
  }

  tr {
    background-color: transparent !important;
  }
`

const ColorRow = styled(Box).attrs({as: 'tr'})`
  border-bottom: 1px solid ${colors.gray[2]} !important;
`

const ColorCell = styled(Box).attrs({as: 'td'})``

function fadeTextColor(fg, bg = white) {
  const rgb = compositeRGB(fg, bg)
  return overlayColor(rgb)
}

/**
 * Composite ("flatten") a foreground RGBA value with an RGB background into an
 * RGB color for the purposes of measuring contrast or luminance.
 */
function compositeRGB(foreground, background) {
  const [fr, fg, fb, fa] = chroma(foreground).rgba()
  const [br, bg, bb] = chroma(background).rgb()
  return chroma([(1 - fa) * br + fa * fr, (1 - fa) * bg + fa * fg, (1 - fa) * bb + fa * fb]).css()
}
<commit_msg>fix Swatch prop warnings<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import chroma from 'chroma-js'
import titleCase from 'title-case'
import styled from 'styled-components'
import {Box, Flex, Heading, Text} from '@primer/components'
import {colors, variables, getBackgroundPalette} from './color-variables'

export {gradientPalettes} from './color-variables'

const {black, white} = colors
export {black, white}

export function ColorVariable({hue, ...rest}) {
  const values = colors[hue]
  return (
    <Flex.Item as={Box} minWidth={240} pr={4} mb={6} className="col-12 col-md-6 markdown-no-margin" {...rest}>
      <Box bg={colors[hue][5]} my={2} p={3} color="white">
        <Heading as="div" pb={3} fontSize={56} fontWeight="light">
          {titleCase(hue)}
        </Heading>
        <Flex justifyContent="space-between">
          <Flex.Item as={Var} fontWeight="bold">
            ${hue}-500
          </Flex.Item>
          <Text justifySelf="end" fontFamily="mono">
            {values[5]}
          </Text>
        </Flex>
      </Box>
      {values.map((value, index) => (
        <Swatch name={hue} index={index} key={value} />
      ))}
    </Flex.Item>
  )
}

ColorVariable.propTypes = {
  hue: PropTypes.oneOf(Object.keys(colors)).isRequired
}

export function FadeVariables({hue, color, bg, over, children, ...rest}) {
  const colorValue = colors[hue]
  const alphas = [15, 30, 50, 70, 85]
  const values = alphas.map(alpha => {
    const value = chroma(colorValue)
      .alpha(alpha / 100)
      .css()
    return {
      variable: `${hue}-fade-${alpha}`,
      textColor: fadeTextColor(value, over),
      value
    }
  })
  const boxProps = {color, bg}
  return (
    <Flex.Item as={Box} pr={4} mb={6} width={1 / 2} className="markdown-no-margin" {...rest}>
      {/* <Heading as="div">{titleCase(hue)}</Heading> */}
      <Box my={2} p={3} {...boxProps}>
        <Heading as="div" pb={3} fontSize={56} fontWeight="light">
          {titleCase(hue)}
        </Heading>
        <Flex justifyContent="space-between">
          <Flex.Item flex="1 1 auto" as={Var} fontWeight="bold">
            ${hue}
          </Flex.Item>
          <Text fontFamily="mono">
            {chroma(colorValue).css()}
            {' / '}
            {colorValue}
          </Text>
        </Flex>
        {children}
      </Box>
      <Box bg={over}>
        {values.map(swatchProps => (
          <Swatch {...swatchProps} key={swatchProps.value} />
        ))}
      </Box>
    </Flex.Item>
  )
}

FadeVariables.propTypes = {
  bg: Box.propTypes.color,
  color: Box.propTypes.color,
  hue: PropTypes.oneOf(['black', 'white']),
  over: PropTypes.string
}

export function Swatch(props) {
  const {
    name,
    index,
    value = colors[name][index],
    textColor = overlayColor(value),
    variable = `${name}-${index}00`,
    children,
    ...rest
  } = props

  return (
    <Flex bg={value} color={textColor} {...rest}>
      {children}
      {variable ? (
        <Box as={Var} pr={4}>
          ${variable}
        </Box>
      ) : null}
      <Box as={Text} fontFamily="mono">
        {value}
      </Box>
    </Flex>
  )
}

Swatch.defaultProps = {
  as: Text,
  flexWrap: 'wrap',
  fontSize: 1,
  justifyContent: 'space-between',
  p: 3
}

Swatch.propTypes = {
  name: PropTypes.string,
  textColor: PropTypes.string,
  value: PropTypes.string.isRequired
}

export function BackgroundHueSwatches({hue, ...rest}) {
  const namedUtilities = Object.keys(variables).filter(name => name.startsWith(`bg-${hue}`))

  const namedUtilitiesByValue = {}
  for (const name of namedUtilities) {
    const value = variables[name]
    namedUtilitiesByValue[value] = name
  }

  const {values} = getBackgroundPalette(hue)
  const rows = values.map(({value, ...rest}) => ({
    value,
    named: namedUtilitiesByValue[value],
    ...rest
  }))

  return (
    <ColorTable {...rest}>

          Default classes
          Extended classes
          Variable
          CSS value

        {rows.map(({value, named, slug, variable}) => {
          const overlay = overlayColor(value)
          return (
            <ColorRow key={slug}>
              <ColorCell bg={named ? value : null} color={overlay}>
                {named ? `.${named}` : null}
              </ColorCell>
              <ColorCell bg={value} color={overlay}>
                .bg-{slug}
              </ColorCell>
              <ColorCell bg={value} color={overlay}>
                <Var>${variable}</Var>
              </ColorCell>
              <ColorCell bg={value} color={overlay}>
                <Text fontFamily="mono">{value}</Text>
              </ColorCell>
            </ColorRow>
          )
        })}

    </ColorTable>
  )
}

export function Var(props) {
  return <Text as="var" fontFamily="mono" fontStyle="normal" {...props} />
}

export function overlayColor(bg) {
  return chroma(bg).luminance() > 0.5 ? black : white
}

const ColorTable = styled.table`
  display: table !important;
  table-layout: fixed;
  border-collapse: separate;
  border-spacing: 0 4px;

  tr,
  td,
  th {
    border: 0 !important;
  }

  td,
  th {
    text-align: left;
    width: 25%;
  }

  tr {
    background-color: transparent !important;
  }
`

const ColorRow = styled(Box).attrs({as: 'tr'})`
  border-bottom: 1px solid ${colors.gray[2]} !important;
`

const ColorCell = styled(Box).attrs({as: 'td'})``

function fadeTextColor(fg, bg = white) {
  const rgb = compositeRGB(fg, bg)
  return overlayColor(rgb)
}

/**
 * Composite ("flatten") a foreground RGBA value with an RGB background into an
 * RGB color for the purposes of measuring contrast or luminance.
 */
function compositeRGB(foreground, background) {
  const [fr, fg, fb, fa] = chroma(foreground).rgba()
  const [br, bg, bb] = chroma(background).rgb()
  return chroma([(1 - fa) * br + fa * fr, (1 - fa) * bg + fa * fg, (1 - fa) * bb + fa * fb]).css()
}
<|endoftext|>


<commit_before>import _ from 'lodash'

export const MAX_THRESHOLDS = 5
export const MIN_THRESHOLDS = 2

export const COLOR_TYPE_MIN = 'min'
export const DEFAULT_VALUE_MIN = 0
export const COLOR_TYPE_MAX = 'max'
export const DEFAULT_VALUE_MAX = 100
export const COLOR_TYPE_THRESHOLD = 'threshold'

export const THRESHOLD_TYPE_TEXT = 'text'
export const THRESHOLD_TYPE_BG = 'background'
export const THRESHOLD_TYPE_BASE = 'base'

export const THRESHOLD_COLORS = [
  {
    hex: '#BF3D5E',
    name: 'ruby',
  },
  {
    hex: '#DC4E58',
    name: 'fire',
  },
  {
    hex: '#F95F53',
    name: 'curacao',
  },
  {
    hex: '#F48D38',
    name: 'tiger',
  },
  {
    hex: '#FFB94A',
    name: 'pineapple',
  },
  {
    hex: '#FFD255',
    name: 'thunder',
  },
  {
    hex: '#7CE490',
    name: 'honeydew',
  },
  {
    hex: '#4ED8A0',
    name: 'rainforest',
  },
  {
    hex: '#32B08C',
    name: 'viridian',
  },
  {
    hex: '#4591ED',
    name: 'ocean',
  },
  {
    hex: '#22ADF6',
    name: 'pool',
  },
  {
    hex: '#00C9FF',
    name: 'laser',
  },
  {
    hex: '#513CC6',
    name: 'planet',
  },
  {
    hex: '#7A65F2',
    name: 'star',
  },
  {
    hex: '#9394FF',
    name: 'comet',
  },
  {
    hex: '#383846',
    name: 'pepper',
  },
  {
    hex: '#545667',
    name: 'graphite',
  },
  {
    hex: '#ffffff',
    name: 'white',
  },
  {
    hex: '#292933',
    name: 'castle',
  },
]

export const DEFAULT_GAUGE_COLORS = [
  {
    type: COLOR_TYPE_MIN,
    hex: THRESHOLD_COLORS[11].hex,
    id: '0',
    name: THRESHOLD_COLORS[11].name,
    value: DEFAULT_VALUE_MIN,
  },
  {
    type: COLOR_TYPE_MAX,
    hex: THRESHOLD_COLORS[14].hex,
    id: '1',
    name: THRESHOLD_COLORS[14].name,
    value: DEFAULT_VALUE_MAX,
  },
]

export const DEFAULT_THRESHOLDS_LIST_COLORS = [
  {
    type: THRESHOLD_TYPE_TEXT,
    hex: THRESHOLD_COLORS[11].hex,
    id: THRESHOLD_TYPE_BASE,
    name: THRESHOLD_COLORS[11].name,
    value: -999999999999999999,
  },
]

export const validateThresholdsListColors = (colors, type) => {
  if (!colors || colors.length === 0) {
    return DEFAULT_THRESHOLDS_LIST_COLORS
  }

  let containsBaseColor = false

  const formattedColors = colors.map(color => {
    if (color.id === THRESHOLD_TYPE_BASE) {
      // Check for existance of base color
      containsBaseColor = true
      return {...color, value: Number(color.value), type}
    }
    // Single stat colors should all have type of 'text' or 'background'
    return {...color, value: Number(color.value), type}
  })

  const formattedColorsWithBase = [
    ...formattedColors,
    DEFAULT_THRESHOLDS_LIST_COLORS[0],
  ]

  return containsBaseColor ? formattedColors : formattedColorsWithBase
}

export const getThresholdsListType = colors => {
  const type = _.get(colors, ['0', 'type'], false)

  if (type) {
    if (_.includes([THRESHOLD_TYPE_TEXT, THRESHOLD_TYPE_BG], type)) {
      return type
    }
  }

  return THRESHOLD_TYPE_TEXT
}

export const validateGaugeColors = colors => {
  if (!colors || colors.length < MIN_THRESHOLDS) {
    return DEFAULT_GAUGE_COLORS
  }

  const colorsAreIncorrectType =
    colors.reduce((acc, color) => {
      if (
        color.type === COLOR_TYPE_MIN ||
        color.type === COLOR_TYPE_MAX ||
        color.type === COLOR_TYPE_THRESHOLD
      ) {
        return (acc += 1)
      }
    }, 0) !== colors.length

  if (colorsAreIncorrectType) {
    return DEFAULT_GAUGE_COLORS
  }

  // Gauge colors should have a type of min, any number of thresholds, and a max
  const formattedColors = _.sortBy(colors, color => Number(color.value)).map(
    color => ({
      ...color,
      value: Number(color.value),
      type: COLOR_TYPE_THRESHOLD,
    })
  )

  formattedColors[0].type = COLOR_TYPE_MIN
  formattedColors[formattedColors.length - 1].type = COLOR_TYPE_MAX

  return formattedColors
}

export const stringifyColorValues = colors => {
  return colors.map(color => ({...color, value: `${color.value}`}))
}
<commit_msg>Guard against thresholds list accepting incorrect color types<commit_after>import _ from 'lodash'

export const MAX_THRESHOLDS = 5
export const MIN_THRESHOLDS = 2

export const COLOR_TYPE_MIN = 'min'
export const DEFAULT_VALUE_MIN = 0
export const COLOR_TYPE_MAX = 'max'
export const DEFAULT_VALUE_MAX = 100
export const COLOR_TYPE_THRESHOLD = 'threshold'

export const THRESHOLD_TYPE_TEXT = 'text'
export const THRESHOLD_TYPE_BG = 'background'
export const THRESHOLD_TYPE_BASE = 'base'

export const THRESHOLD_COLORS = [
  {
    hex: '#BF3D5E',
    name: 'ruby',
  },
  {
    hex: '#DC4E58',
    name: 'fire',
  },
  {
    hex: '#F95F53',
    name: 'curacao',
  },
  {
    hex: '#F48D38',
    name: 'tiger',
  },
  {
    hex: '#FFB94A',
    name: 'pineapple',
  },
  {
    hex: '#FFD255',
    name: 'thunder',
  },
  {
    hex: '#7CE490',
    name: 'honeydew',
  },
  {
    hex: '#4ED8A0',
    name: 'rainforest',
  },
  {
    hex: '#32B08C',
    name: 'viridian',
  },
  {
    hex: '#4591ED',
    name: 'ocean',
  },
  {
    hex: '#22ADF6',
    name: 'pool',
  },
  {
    hex: '#00C9FF',
    name: 'laser',
  },
  {
    hex: '#513CC6',
    name: 'planet',
  },
  {
    hex: '#7A65F2',
    name: 'star',
  },
  {
    hex: '#9394FF',
    name: 'comet',
  },
  {
    hex: '#383846',
    name: 'pepper',
  },
  {
    hex: '#545667',
    name: 'graphite',
  },
  {
    hex: '#ffffff',
    name: 'white',
  },
  {
    hex: '#292933',
    name: 'castle',
  },
]

export const DEFAULT_GAUGE_COLORS = [
  {
    type: COLOR_TYPE_MIN,
    hex: THRESHOLD_COLORS[11].hex,
    id: '0',
    name: THRESHOLD_COLORS[11].name,
    value: DEFAULT_VALUE_MIN,
  },
  {
    type: COLOR_TYPE_MAX,
    hex: THRESHOLD_COLORS[14].hex,
    id: '1',
    name: THRESHOLD_COLORS[14].name,
    value: DEFAULT_VALUE_MAX,
  },
]

export const DEFAULT_THRESHOLDS_LIST_COLORS = [
  {
    type: THRESHOLD_TYPE_TEXT,
    hex: THRESHOLD_COLORS[11].hex,
    id: THRESHOLD_TYPE_BASE,
    name: THRESHOLD_COLORS[11].name,
    value: -999999999999999999,
  },
]

export const validateThresholdsListColors = (colors, type) => {
  if (!colors || colors.length === 0) {
    return DEFAULT_THRESHOLDS_LIST_COLORS
  }

  const colorsAreIncorrectType =
    colors.reduce((acc, color) => {
      if (
        color.type === THRESHOLD_TYPE_BASE ||
        color.type === THRESHOLD_TYPE_TEXT ||
        color.type === THRESHOLD_TYPE_BG
      ) {
        return (acc += 1)
      }
    }, 0) !== colors.length

  if (colorsAreIncorrectType) {
    return DEFAULT_THRESHOLDS_LIST_COLORS
  }

  let containsBaseColor = false

  const formattedColors = colors.map(color => {
    if (color.id === THRESHOLD_TYPE_BASE) {
      // Check for existance of base color
      containsBaseColor = true
      return {...color, value: Number(color.value), type}
    }
    // Single stat colors should all have type of 'text' or 'background'
    return {...color, value: Number(color.value), type}
  })

  const formattedColorsWithBase = [
    ...formattedColors,
    DEFAULT_THRESHOLDS_LIST_COLORS[0],
  ]

  return containsBaseColor ? formattedColors : formattedColorsWithBase
}

export const getThresholdsListType = colors => {
  const type = _.get(colors, ['0', 'type'], false)

  if (type) {
    if (_.includes([THRESHOLD_TYPE_TEXT, THRESHOLD_TYPE_BG], type)) {
      return type
    }
  }

  return THRESHOLD_TYPE_TEXT
}

export const validateGaugeColors = colors => {
  if (!colors || colors.length < MIN_THRESHOLDS) {
    return DEFAULT_GAUGE_COLORS
  }

  const colorsAreIncorrectType =
    colors.reduce((acc, color) => {
      if (
        color.type === COLOR_TYPE_MIN ||
        color.type === COLOR_TYPE_MAX ||
        color.type === COLOR_TYPE_THRESHOLD
      ) {
        return (acc += 1)
      }
    }, 0) !== colors.length

  if (colorsAreIncorrectType) {
    return DEFAULT_GAUGE_COLORS
  }

  // Gauge colors should have a type of min, any number of thresholds, and a max
  const formattedColors = _.sortBy(colors, color => Number(color.value)).map(
    color => ({
      ...color,
      value: Number(color.value),
      type: COLOR_TYPE_THRESHOLD,
    })
  )

  formattedColors[0].type = COLOR_TYPE_MIN
  formattedColors[formattedColors.length - 1].type = COLOR_TYPE_MAX

  return formattedColors
}

export const stringifyColorValues = colors => {
  return colors.map(color => ({...color, value: `${color.value}`}))
}
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react'
import {withRouter, Link} from 'react-router'
import {connect} from 'react-redux'

import Authorized, {ADMIN_ROLE} from 'src/auth/Authorized'

import UserNavBlock from 'src/side_nav/components/UserNavBlock'
import {
  NavBar,
  NavBlock,
  NavHeader,
  NavListItem,
} from 'src/side_nav/components/NavItems'

import {DEFAULT_HOME_PAGE} from 'shared/constants'

const {arrayOf, bool, shape, string} = PropTypes

const SideNav = React.createClass({
  propTypes: {
    params: shape({
      sourceID: string.isRequired,
    }).isRequired,
    location: shape({
      pathname: string.isRequired,
    }).isRequired,
    isHidden: bool.isRequired,
    isUsingAuth: bool,
    logoutLink: string,
    links: shape({
      me: string,
      external: shape({
        custom: arrayOf(
          shape({
            name: string.isRequired,
            url: string.isRequired,
          })
        ),
      }),
    }),
    me: shape({
      currentOrganization: shape({
        id: string.isRequired,
        name: string.isRequired,
      }),
      name: string,
      organizations: arrayOf(
        shape({
          id: string.isRequired,
          name: string.isRequired,
        })
      ),
      roles: arrayOf(
        shape({
          id: string,
          name: string,
        })
      ),
    }).isRequired,
  },

  render() {
    const {
      params: {sourceID},
      location: {pathname: location},
      isHidden,
      isUsingAuth,
      logoutLink,
      links,
      me,
    } = this.props

    const sourcePrefix = `/sources/${sourceID}`
    const dataExplorerLink = `${sourcePrefix}/chronograf/data-explorer`

    const isDefaultPage = location.split('/').includes(DEFAULT_HOME_PAGE)

    return isHidden
      ? null
      : <NavBar location={location}>

            <Link
              to={`${sourcePrefix}/${DEFAULT_HOME_PAGE}`}
              className="sidebar--square sidebar--logo"
            >

            </Link>

          <NavBlock
            icon="cubo-node"
            link={`${sourcePrefix}/hosts`}
            location={location}
          >
            <NavHeader link={`${sourcePrefix}/hosts`} title="Host List" />
          </NavBlock>
          <NavBlock
            icon="graphline"
            link={dataExplorerLink}
            location={location}
          >
            <NavHeader link={dataExplorerLink} title="Data Explorer" />
          </NavBlock>
          <NavBlock
            icon="dash-h"
            link={`${sourcePrefix}/dashboards`}
            location={location}
          >
            <NavHeader
              link={`${sourcePrefix}/dashboards`}
              title={'Dashboards'}
            />
          </NavBlock>
          <NavBlock
            matcher="alerts"
            icon="alert-triangle"
            link={`${sourcePrefix}/alerts`}
            location={location}
          >
            <NavHeader link={`${sourcePrefix}/alerts`} title="Alerting" />
            <NavListItem link={`${sourcePrefix}/alerts`}>History</NavListItem>
            <NavListItem link={`${sourcePrefix}/alert-rules`}>
              Create
            </NavListItem>
          </NavBlock>

          <Authorized
            requiredRole={ADMIN_ROLE}
            replaceWithWhenNotUsingAuth={
              <NavBlock
                icon="crown2"
                link={`${sourcePrefix}/admin-influxdb`}
                location={location}
              >
                <NavHeader
                  link={`${sourcePrefix}/admin-influxdb`}
                  title="Admin"
                />
              </NavBlock>
            }
          >
            <NavBlock
              icon="crown2"
              link={`${sourcePrefix}/admin-chronograf`}
              location={location}
            >
              <NavHeader
                link={`${sourcePrefix}/admin-chronograf`}
                title="Admin"
              />
              <NavListItem link={`${sourcePrefix}/admin-chronograf`}>
                Chronograf
              </NavListItem>
              <NavListItem link={`${sourcePrefix}/admin-influxdb`}>
                InfluxDB
              </NavListItem>
            </NavBlock>
          </Authorized>

          <NavBlock
            icon="cog-thick"
            link={`${sourcePrefix}/manage-sources`}
            location={location}
          >
            <NavHeader
              link={`${sourcePrefix}/manage-sources`}
              title="Configuration"
            />
          </NavBlock>
          {isUsingAuth
            ? <UserNavBlock logoutLink={logoutLink} links={links} me={me} />
            : null}
        </NavBar>
  },
})
const mapStateToProps = ({
  auth: {isUsingAuth, logoutLink, me},
  app: {ephemeral: {inPresentationMode}},
  links,
}) => ({
  isHidden: inPresentationMode,
  isUsingAuth,
  logoutLink,
  links,
  me,
})

export default connect(mapStateToProps)(withRouter(SideNav))
<commit_msg>Move user nav item to top of side nav<commit_after>import React, {PropTypes} from 'react'
import {withRouter, Link} from 'react-router'
import {connect} from 'react-redux'

import Authorized, {ADMIN_ROLE} from 'src/auth/Authorized'

import UserNavBlock from 'src/side_nav/components/UserNavBlock'
import {
  NavBar,
  NavBlock,
  NavHeader,
  NavListItem,
} from 'src/side_nav/components/NavItems'

import {DEFAULT_HOME_PAGE} from 'shared/constants'

const {arrayOf, bool, shape, string} = PropTypes

const SideNav = React.createClass({
  propTypes: {
    params: shape({
      sourceID: string.isRequired,
    }).isRequired,
    location: shape({
      pathname: string.isRequired,
    }).isRequired,
    isHidden: bool.isRequired,
    isUsingAuth: bool,
    logoutLink: string,
    links: shape({
      me: string,
      external: shape({
        custom: arrayOf(
          shape({
            name: string.isRequired,
            url: string.isRequired,
          })
        ),
      }),
    }),
    me: shape({
      currentOrganization: shape({
        id: string.isRequired,
        name: string.isRequired,
      }),
      name: string,
      organizations: arrayOf(
        shape({
          id: string.isRequired,
          name: string.isRequired,
        })
      ),
      roles: arrayOf(
        shape({
          id: string,
          name: string,
        })
      ),
    }).isRequired,
  },

  render() {
    const {
      params: {sourceID},
      location: {pathname: location},
      isHidden,
      isUsingAuth,
      logoutLink,
      links,
      me,
    } = this.props

    const sourcePrefix = `/sources/${sourceID}`
    const dataExplorerLink = `${sourcePrefix}/chronograf/data-explorer`

    const isDefaultPage = location.split('/').includes(DEFAULT_HOME_PAGE)

    return isHidden
      ? null
      : <NavBar location={location}>

            <Link
              to={`${sourcePrefix}/${DEFAULT_HOME_PAGE}`}
              className="sidebar--square sidebar--logo"
            >

            </Link>

          {isUsingAuth
            ? <UserNavBlock logoutLink={logoutLink} links={links} me={me} />
            : null}
          <NavBlock
            icon="cubo-node"
            link={`${sourcePrefix}/hosts`}
            location={location}
          >
            <NavHeader link={`${sourcePrefix}/hosts`} title="Host List" />
          </NavBlock>
          <NavBlock
            icon="graphline"
            link={dataExplorerLink}
            location={location}
          >
            <NavHeader link={dataExplorerLink} title="Data Explorer" />
          </NavBlock>
          <NavBlock
            icon="dash-h"
            link={`${sourcePrefix}/dashboards`}
            location={location}
          >
            <NavHeader
              link={`${sourcePrefix}/dashboards`}
              title={'Dashboards'}
            />
          </NavBlock>
          <NavBlock
            matcher="alerts"
            icon="alert-triangle"
            link={`${sourcePrefix}/alerts`}
            location={location}
          >
            <NavHeader link={`${sourcePrefix}/alerts`} title="Alerting" />
            <NavListItem link={`${sourcePrefix}/alerts`}>History</NavListItem>
            <NavListItem link={`${sourcePrefix}/alert-rules`}>
              Create
            </NavListItem>
          </NavBlock>

          <Authorized
            requiredRole={ADMIN_ROLE}
            replaceWithWhenNotUsingAuth={
              <NavBlock
                icon="crown2"
                link={`${sourcePrefix}/admin-influxdb`}
                location={location}
              >
                <NavHeader
                  link={`${sourcePrefix}/admin-influxdb`}
                  title="Admin"
                />
              </NavBlock>
            }
          >
            <NavBlock
              icon="crown2"
              link={`${sourcePrefix}/admin-chronograf`}
              location={location}
            >
              <NavHeader
                link={`${sourcePrefix}/admin-chronograf`}
                title="Admin"
              />
              <NavListItem link={`${sourcePrefix}/admin-chronograf`}>
                Chronograf
              </NavListItem>
              <NavListItem link={`${sourcePrefix}/admin-influxdb`}>
                InfluxDB
              </NavListItem>
            </NavBlock>
          </Authorized>

          <NavBlock
            icon="cog-thick"
            link={`${sourcePrefix}/manage-sources`}
            location={location}
          >
            <NavHeader
              link={`${sourcePrefix}/manage-sources`}
              title="Configuration"
            />
          </NavBlock>
        </NavBar>
  },
})
const mapStateToProps = ({
  auth: {isUsingAuth, logoutLink, me},
  app: {ephemeral: {inPresentationMode}},
  links,
}) => ({
  isHidden: inPresentationMode,
  isUsingAuth,
  logoutLink,
  links,
  me,
})

export default connect(mapStateToProps)(withRouter(SideNav))
<|endoftext|>


<commit_before>function EasyComplete(input) {
  this.input = input;

  this.setupList();

  this.list = input.nextSibling;

  // I should look into using bind with anonymous functions.
  var self = this;

  this.input.addEventListener("input", function inputModified(ev) {
    var matches = [];

    if (!ev.srcElement.value) {
      self.setMatches(matches);
      return;
    }

    var fullList = ev.srcElement.getAttribute("data-list").split(", ");
    for (var i = 0, length = fullList.length; i < length; ++i) {
      var entry = fullList[i];
      // Use RegExp for case-insensitive search.
      if (entry.search(new RegExp(ev.srcElement.value, "i")) !== -1) {
        matches.push(entry);
      }
    }

    self.setMatches(matches);
  });

  this.input.addEventListener("keydown", function keyOnInputPressed(ev) {
    switch (ev.keyCode) {
    case 13:
      self.selectMatch();
      break;
    case 38:
      self.traverseList(self.input.nextSibling, self.TraverseDirection.UP);
      break;
    case 40:
      self.traverseList(self.input.nextSibling, self.TraverseDirection.DOWN);
      break;
    default:
      break;
    }
  });
}

EasyComplete.prototype = {
  testing: function(test) {
    console.log(test);
  },

  // Resets the matched values for a input, then adds new matches.
  setMatches: function(matches) {
    var list = document.getElementsByTagName("ul")[0];

    this.clearList(list);

    // Add new matches.
    for (var i = 0, length = matches.length; i < length; ++i) {
      var element = document.createElement("li");
      element.appendChild(
        document.createTextNode(matches[i])
      );
      list.appendChild(element);

      var self = this;
      element.addEventListener("click", function matchClicked(ev) {
        self.input.value = ev.srcElement.innerText;
        self.clearList(list);
      });
    }
  },

  selectMatch: function() {
    this.input.value = this.list.getElementsByClassName("active")[0].innerText;
  },

  setupList: function() {
    var container = document.createElement("div");
    this.input.parentNode.insertBefore(container, this.input);
    container.appendChild(this.input);

    var list = document.createElement("ul");
    list.classList.add("easy_complete");
    container.appendChild(list);
  },

  clearList: function(list) {
    while (list.firstChild) {
      list.removeChild(list.firstChild);
    }
  },

  traverseList: function(list, direction) {
    var currentListItem = list.getElementsByClassName("active")[0];

    switch (direction) {
    case this.TraverseDirection.UP:
      if (currentListItem) {
        currentListItem.previousSibling.classList.add("active");
      } else {
        list.childNodes[list.childNodes.length - 1].classList.add("active");
      }
      break;
    case this.TraverseDirection.DOWN:
      if (currentListItem) {
        currentListItem.nextSibling.classList.add("active");
      } else {
        list.childNodes[0].classList.add("active");
      }
      break;
    }

    if (currentListItem) {
      currentListItem.classList.remove("active");
    }
  },

  TraverseDirection: {
    UP: 0,
    DOWN: 1
  }
}

//(function() {
  // Initialize all standard inputs.
  var inputs = document.getElementsByTagName("input");
  for (var i = 0, length = inputs.length; i < length; ++i) {
    if (inputs[i].getAttribute("data-list") !== null) {
      new EasyComplete(inputs[i]);
    }
  }
//}());

<commit_msg>Use list as a class variable<commit_after>function EasyComplete(input) {
  this.input = input;

  this.setupList();

  // I should look into using bind with anonymous functions.
  var self = this;

  this.input.addEventListener("input", function inputModified(ev) {
    var matches = [];

    if (!ev.srcElement.value) {
      self.setMatches(matches);
      return;
    }

    var fullList = ev.srcElement.getAttribute("data-list").split(", ");
    for (var i = 0, length = fullList.length; i < length; ++i) {
      var entry = fullList[i];
      // Use RegExp for case-insensitive search.
      if (entry.search(new RegExp(ev.srcElement.value, "i")) !== -1) {
        matches.push(entry);
      }
    }

    self.setMatches(matches);
  });

  this.input.addEventListener("keydown", function keyOnInputPressed(ev) {
    switch (ev.keyCode) {
    case 13:
      self.selectMatch();
      break;
    case 38:
      self.traverseList(self.TraverseDirection.UP);
      break;
    case 40:
      self.traverseList(self.TraverseDirection.DOWN);
      break;
    default:
      break;
    }
  });
}

EasyComplete.prototype = {
  testing: function(test) {
    console.log(test);
  },

  // Resets the matched values for a input, then adds new matches.
  setMatches: function(matches) {
    this.clearList();

    // Add new matches.
    for (var i = 0, length = matches.length; i < length; ++i) {
      var element = document.createElement("li");
      element.appendChild(
        document.createTextNode(matches[i])
      );
      this.list.appendChild(element);

      var self = this;
      element.addEventListener("click", function matchClicked(ev) {
        self.input.value = ev.srcElement.innerText;
        self.clearList();
      });
    }
  },

  selectMatch: function() {
    this.input.value = this.list.getElementsByClassName("active")[0].innerText;
  },

  setupList: function() {
    var container = document.createElement("div");
    this.input.parentNode.insertBefore(container, this.input);
    container.appendChild(this.input);

    this.list = document.createElement("ul");
    this.list.classList.add("easy_complete");
    container.appendChild(this.list);
  },

  clearList: function() {
    while (this.list.firstChild) {
      this.list.removeChild(this.list.firstChild);
    }
  },

  traverseList: function(direction) {
    var currentListItem = this.list.getElementsByClassName("active")[0];

    switch (direction) {
    case this.TraverseDirection.UP:
      if (currentListItem) {
        currentListItem.previousSibling.classList.add("active");
      } else {
        this.list.childNodes[this.list.childNodes.length - 1].classList.add("active");
      }
      break;
    case this.TraverseDirection.DOWN:
      if (currentListItem) {
        currentListItem.nextSibling.classList.add("active");
      } else {
        this.list.childNodes[0].classList.add("active");
      }
      break;
    }

    if (currentListItem) {
      currentListItem.classList.remove("active");
    }
  },

  TraverseDirection: {
    UP: 0,
    DOWN: 1
  }
}

//(function() {
  // Initialize all standard inputs.
  var inputs = document.getElementsByTagName("input");
  for (var i = 0, length = inputs.length; i < length; ++i) {
    if (inputs[i].getAttribute("data-list") !== null) {
      new EasyComplete(inputs[i]);
    }
  }
//}());

<|endoftext|>


<commit_before>"use strict";

var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var uuid = require("uuid");

var toJSONOpts = {
  versionKey: false,
  transform: function(doc, ret) {
    strictSchema(doc.schema, ret);
  }
};

var CADComment = new Schema({
  Comment: {
    type: String
  },
  CommentSource: {
    type: String
  },
  CommentDateTime: {
    type: String
  }
}, {
  _id: false
});

CADComment.set("toJSON", toJSONOpts);

var CADPerson = new Schema({
  PersonnelID: {
    type: String
  },
  PersonnelName: {
    type: String
  },
  PersonnelRank: {
    type: String
  },
  PersonnelWorkCode: {
    type: String
  },
  PersonnelNote: {
    type: String
  }
}, {
  _id: false
});
CADPerson.set("toJSON", toJSONOpts);

var CADUnit = new Schema({
  UnitID: {
    type: String,
    required: true
  },
  UnitDispatchNumber: {
    type: String,
    required: true
  },
  TimeDispatched: {
    type: String
  },
  TimeEnroute: {
    type: String
  },
  TimeArrived: {
    type: String
  },
  TimeStaged: {
    type: String
  },
  TimeCleared: {
    type: String
  },
  TimeAtHospital: {
    type: String
  },
  TimePatient: {
    type: String
  },
  TimeTransport: {
    type: String
  },
  TimeTransporting: {
    type: String
  },

  PersonnelCount: {
    type: Number
  },
  Personnel: [CADPerson]
}, {
  _id: false
});
CADUnit.set("toJSON", toJSONOpts);

var APNNotificationType = new Schema({
  name: {
    type: String
  },
  value: {
    type: String
  }
}, {
  _id: false
});
APNNotificationType.set("toJSON", toJSONOpts);

var CADPriorIncident = new Schema({
  Address: {
    type: String
  },
  Comment: {
    type: [CADComment]
  },
  IncidentDateTime: {
    type: String
  },
  IncidentNumber: {
    type: String
  },
  Jurisdiction: {
    type: String
  },
  Problem: {
    type: String
  },
  Suite: {
    type: String
  }
}, {
  _id: false
});
CADPriorIncident.set("toJSON", toJSONOpts);

var modelSchema = new Schema({
  _id: {
    type: Schema.ObjectId,
    auto: true
  },
  uuid: {
    type: String,
    default: uuid.v4
  },
  departmentId: {
    type: String,
    default: "",
    required: true,
    index: true
  },
  AgencyID: {
    type: String,
    required: true
  },
  IncidentNumber: {
    type: String,
    required: true
  },
  TransactionID: {
    type: String,
    required: true
  },

  // Incident Specific
  AgencyIncidentCallTypeDescription: {
    type: String
  },
  AgencyIncidentCallType: {
    type: String
  },
  AgencyIncidentCallSubTypeDescription: {
    type: String
  },
  AgencyIncidentCallSubType: {
    type: String
  },
  AgencyIncidentPriorityDescription: {
    type: String
  },
  PulsePointIncidentCallType: {
    type: String
  },
  PulsePointDeterminantCode: {
    type: String
  },
  AlarmLevel: {
    type: String
  },
  CommandName: {
    type: String
  },
  FireMap: {
    type: String
  },
  TBMap: {
    type: String
  },
  MapPages: {
    type: String
  },
  TacticalChannel: {
    type: String
  },
  TacticalAltChannel: {
    type: String
  },
  CommandChannel: {
    type: String
  },

  // Dates
  EntryDateTime: {
    type: String
  },
  ClosedDateTime: {
    type: String
  },
  CallReceivedDateTime: {
    type: String
  },
  DispatchDateTime: {
    type: String
  },
  IncidentLastUpdate: {
    type: String
  },
  EnrouteDateTime: {
    type: String
  },
  OnSceneDateTime: {
    type: String
  },

  modified_date: {
    type: String
  },
  modified_unix_date: {
    type: Number
  },
  start_unix_date: {
    type: Number
  },
  closed_unix_date: {
    type: Number
  },
  queued_at: {
    type: Number
  },
  scheduled_at: {
    type: Number
  },

  // Address fields
  StreetName: {
    type: String
  },
  StreetSuffix: {
    type: String
  },
  Predirectional: {
    type: String
  },
  Postdirectional: {
    type: String
  },
  CityOrLocality: {
    type: String
  },
  StateOrProvince: {
    type: String
  },
  // Legacy
  StateOfProvince: {
    type: String
  },
  CommonPlaceName: {
    type: String
  },
  CrossStreet1: {
    type: String
  },
  CrossStreet2: {
    type: String
  },
  StreetNumber: {
    type: String
  },
  Building: {
    type: String
  },
  Floor: {
    type: String
  },
  Suite: {
    type: String
  },
  City: {
    type: String
  },
  County: {
    type: String
  },
  PostalCode: {
    type: String
  },
  CrossStreetName: {
    type: String
  },
  LocationComment: {
    type: String
  },
  // TC Address
  full_address: {
    type: String
  },
  address: {
    type: String
  },
  cross_streets: {
    type: String
  },

  // Prior History
  PriorIncidentChanged: {
    type: Boolean
  },
  PriorIncident: {
    type: [CADPriorIncident]
  },

  // Caller
  CallerNumber: {
    type: String
  },

  tag: {
    type: String
  },

  // Coordinate
  Latitude: {
    type: String
  },
  Longitude: {
    type: String
  },

  Comment: {
    type: [CADComment]
  },

  units: {
    type: [CADUnit]
  },

  // Legacy Unit
  Unit: {
    type: [CADUnit]
  },

  // Tablet Command Internal fields
  preference_location: {
    type: String
  },

  // Leaked
  admin_note: {
    type: String
  },
  Interface: {
    type: String
  },
  InterfaceVersion: {
    type: String
  },
  CADSimulator: {
    type: String
  },

  notificationType: {
    type: [APNNotificationType]
  },
  notifiedUnits: {
    type: [String]
  },

  // Anaheim
  ReportNumber: {
    type: [String]
  },

  // Other Agencies
  AgencyDeterminantCode: {
    type: String
  },
  AgencyIncidentCategory: {
    type: String
  },
  CFMap: {
    type: String
  },
  CallSource: {
    type: String
  },
  CaseNumber: {
    type: String
  },
  CrossRefDataOwnerAgyID1: {
    type: String
  },
  CrossRefDataSubmitterAgyID1: {
    type: String
  },
  CrossRefEventCategory1: {
    type: String
  },
  CrossRefEventNum1: {
    type: String
  },
  EventLevel: {
    type: String
  },
  EventNumber: {
    type: String
  },
  FirstDueStation: {
    type: String
  },
  MedicalAgencyIncidentType: {
    type: String
  },
  MedicalEventCode: {
    type: String
  },
  PopulationDensity: {
    type: String
  },
  Priority: {
    type: String
  },
  WorkstationID: {
    type: String
  },
  LatitudeDegree: {
    type: String
  },
  LatitudeMinute: {
    type: String
  },
  LatitudeSecond: {
    type: String
  },
  LongitudeDegree: {
    type: String
  },
  LongitudeMinute: {
    type: String
  },
  LongitudeSecond: {
    type: String
  },
  Location: {
    type: String
  },
  LocationType: {
    type: String
  },
  OrigLocation: {
    type: String
  },
  Jurisdiction: {
    type: String
  }
}, {
  collection: "massive_incident_cad",
  strict: false // Because we accept all kind of data in
});
modelSchema.set("autoIndex", false);

modelSchema.set("toJSON", {
  virtuals: true,
  versionKey: false,
  transform: function(doc, ret) {
    // Remove fields that should not be here
    delete ret.apikey;

    strictSchema(doc.schema, ret);

    ret.id = ret._id;
  }
});

modelSchema.virtual("id").get(function() {
  return this._id.toString();
});

function strictSchema(schema, ret) {
  Object.keys(ret).forEach(function(element, index) {
    // Don't complain about the virtuals
    if (element === "id") {
      return;
    }

    var ignoreFields = ["station", "callerNumber"];
    if (ignoreFields.indexOf(element) !== -1) {
      delete ret[element];
      return;
    }

    if (schema.paths[element] === undefined) {
      console.log("backend-models.cad-incident: undefined schema.paths[element]:", element, schema.paths[element]);
      delete ret[element];
    }
  });
}

module.exports = mongoose.model("CADIncident", modelSchema);
<commit_msg>Remove legacy Unit field<commit_after>"use strict";

var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var uuid = require("uuid");

var toJSONOpts = {
  versionKey: false,
  transform: function(doc, ret) {
    strictSchema(doc.schema, ret);
  }
};

var CADComment = new Schema({
  Comment: {
    type: String
  },
  CommentSource: {
    type: String
  },
  CommentDateTime: {
    type: String
  }
}, {
  _id: false
});

CADComment.set("toJSON", toJSONOpts);

var CADPerson = new Schema({
  PersonnelID: {
    type: String
  },
  PersonnelName: {
    type: String
  },
  PersonnelRank: {
    type: String
  },
  PersonnelWorkCode: {
    type: String
  },
  PersonnelNote: {
    type: String
  }
}, {
  _id: false
});
CADPerson.set("toJSON", toJSONOpts);

var CADUnit = new Schema({
  UnitID: {
    type: String,
    required: true
  },
  UnitDispatchNumber: {
    type: String,
    required: true
  },
  TimeDispatched: {
    type: String
  },
  TimeEnroute: {
    type: String
  },
  TimeArrived: {
    type: String
  },
  TimeStaged: {
    type: String
  },
  TimeCleared: {
    type: String
  },
  TimeAtHospital: {
    type: String
  },
  TimePatient: {
    type: String
  },
  TimeTransport: {
    type: String
  },
  TimeTransporting: {
    type: String
  },

  PersonnelCount: {
    type: Number
  },
  Personnel: [CADPerson]
}, {
  _id: false
});
CADUnit.set("toJSON", toJSONOpts);

var APNNotificationType = new Schema({
  name: {
    type: String
  },
  value: {
    type: String
  }
}, {
  _id: false
});
APNNotificationType.set("toJSON", toJSONOpts);

var CADPriorIncident = new Schema({
  Address: {
    type: String
  },
  Comment: {
    type: [CADComment]
  },
  IncidentDateTime: {
    type: String
  },
  IncidentNumber: {
    type: String
  },
  Jurisdiction: {
    type: String
  },
  Problem: {
    type: String
  },
  Suite: {
    type: String
  }
}, {
  _id: false
});
CADPriorIncident.set("toJSON", toJSONOpts);

var modelSchema = new Schema({
  _id: {
    type: Schema.ObjectId,
    auto: true
  },
  uuid: {
    type: String,
    default: uuid.v4
  },
  departmentId: {
    type: String,
    default: "",
    required: true,
    index: true
  },
  AgencyID: {
    type: String,
    required: true
  },
  IncidentNumber: {
    type: String,
    required: true
  },
  TransactionID: {
    type: String,
    required: true
  },

  // Incident Specific
  AgencyIncidentCallTypeDescription: {
    type: String
  },
  AgencyIncidentCallType: {
    type: String
  },
  AgencyIncidentCallSubTypeDescription: {
    type: String
  },
  AgencyIncidentCallSubType: {
    type: String
  },
  AgencyIncidentPriorityDescription: {
    type: String
  },
  PulsePointIncidentCallType: {
    type: String
  },
  PulsePointDeterminantCode: {
    type: String
  },
  AlarmLevel: {
    type: String
  },
  CommandName: {
    type: String
  },
  FireMap: {
    type: String
  },
  TBMap: {
    type: String
  },
  MapPages: {
    type: String
  },
  TacticalChannel: {
    type: String
  },
  TacticalAltChannel: {
    type: String
  },
  CommandChannel: {
    type: String
  },

  // Dates
  EntryDateTime: {
    type: String
  },
  ClosedDateTime: {
    type: String
  },
  CallReceivedDateTime: {
    type: String
  },
  DispatchDateTime: {
    type: String
  },
  IncidentLastUpdate: {
    type: String
  },
  EnrouteDateTime: {
    type: String
  },
  OnSceneDateTime: {
    type: String
  },

  modified_date: {
    type: String
  },
  modified_unix_date: {
    type: Number
  },
  start_unix_date: {
    type: Number
  },
  closed_unix_date: {
    type: Number
  },
  queued_at: {
    type: Number
  },
  scheduled_at: {
    type: Number
  },

  // Address fields
  StreetName: {
    type: String
  },
  StreetSuffix: {
    type: String
  },
  Predirectional: {
    type: String
  },
  Postdirectional: {
    type: String
  },
  CityOrLocality: {
    type: String
  },
  StateOrProvince: {
    type: String
  },
  // Legacy
  StateOfProvince: {
    type: String
  },
  CommonPlaceName: {
    type: String
  },
  CrossStreet1: {
    type: String
  },
  CrossStreet2: {
    type: String
  },
  StreetNumber: {
    type: String
  },
  Building: {
    type: String
  },
  Floor: {
    type: String
  },
  Suite: {
    type: String
  },
  City: {
    type: String
  },
  County: {
    type: String
  },
  PostalCode: {
    type: String
  },
  CrossStreetName: {
    type: String
  },
  LocationComment: {
    type: String
  },
  // TC Address
  full_address: {
    type: String
  },
  address: {
    type: String
  },
  cross_streets: {
    type: String
  },

  // Prior History
  PriorIncidentChanged: {
    type: Boolean
  },
  PriorIncident: {
    type: [CADPriorIncident]
  },

  // Caller
  CallerNumber: {
    type: String
  },

  tag: {
    type: String
  },

  // Coordinate
  Latitude: {
    type: String
  },
  Longitude: {
    type: String
  },

  Comment: {
    type: [CADComment]
  },

  units: {
    type: [CADUnit]
  },

  // Tablet Command Internal fields
  preference_location: {
    type: String
  },

  // Leaked
  admin_note: {
    type: String
  },
  Interface: {
    type: String
  },
  InterfaceVersion: {
    type: String
  },
  CADSimulator: {
    type: String
  },

  notificationType: {
    type: [APNNotificationType]
  },
  notifiedUnits: {
    type: [String]
  },

  // Anaheim
  ReportNumber: {
    type: [String]
  },

  // Other Agencies
  AgencyDeterminantCode: {
    type: String
  },
  AgencyIncidentCategory: {
    type: String
  },
  CFMap: {
    type: String
  },
  CallSource: {
    type: String
  },
  CaseNumber: {
    type: String
  },
  CrossRefDataOwnerAgyID1: {
    type: String
  },
  CrossRefDataSubmitterAgyID1: {
    type: String
  },
  CrossRefEventCategory1: {
    type: String
  },
  CrossRefEventNum1: {
    type: String
  },
  EventLevel: {
    type: String
  },
  EventNumber: {
    type: String
  },
  FirstDueStation: {
    type: String
  },
  MedicalAgencyIncidentType: {
    type: String
  },
  MedicalEventCode: {
    type: String
  },
  PopulationDensity: {
    type: String
  },
  Priority: {
    type: String
  },
  WorkstationID: {
    type: String
  },
  LatitudeDegree: {
    type: String
  },
  LatitudeMinute: {
    type: String
  },
  LatitudeSecond: {
    type: String
  },
  LongitudeDegree: {
    type: String
  },
  LongitudeMinute: {
    type: String
  },
  LongitudeSecond: {
    type: String
  },
  Location: {
    type: String
  },
  LocationType: {
    type: String
  },
  OrigLocation: {
    type: String
  },
  Jurisdiction: {
    type: String
  }
}, {
  collection: "massive_incident_cad",
  strict: false // Because we accept all kind of data in
});
modelSchema.set("autoIndex", false);

modelSchema.set("toJSON", {
  virtuals: true,
  versionKey: false,
  transform: function(doc, ret) {
    // Remove fields that should not be here
    delete ret.apikey;

    strictSchema(doc.schema, ret);

    ret.id = ret._id;
  }
});

modelSchema.virtual("id").get(function() {
  return this._id.toString();
});

function strictSchema(schema, ret) {
  Object.keys(ret).forEach(function(element, index) {
    // Don't complain about the virtuals
    if (element === "id") {
      return;
    }

    var ignoreFields = ["station", "callerNumber"];
    if (ignoreFields.indexOf(element) !== -1) {
      delete ret[element];
      return;
    }

    if (schema.paths[element] === undefined) {
      console.log("backend-models.cad-incident: undefined schema.paths[element]:", element, schema.paths[element]);
      delete ret[element];
    }
  });
}

module.exports = mongoose.model("CADIncident", modelSchema);
<|endoftext|>


<commit_before>var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var NotificationSchema = new Schema({
  reviewer_id: {type: Schema.Types.ObjectId},
  notification_text: {type: String, default: 'Text not specified'},
  points: {type: Number, default: 0},
  date: {type: Date},
  read: {type: Boolean, default: false}
});

NotificationSchema.statics = {

};

var notification = mongoose.model("Notification", NotificationSchema);

module.exports = {
	Notification : notification
};<commit_msg>Deleted notification.js<commit_after><|endoftext|>


<commit_before>
var dropdown = require('./dropdown');
var saveAs = require('../vendor/FileSaver');
var exp = require('./gist');

function close(el) {
  var d = d3.select(document.body).selectAll('#actionDropdown').data([]);
  d.exit().remove();
}

function open(el, items, _class) {
  var d = d3.select(document.body).selectAll('#actionDropdown').data([0]);
  // enter
  var ul = d.enter().append('ul').attr('id', 'actionDropdown').style('position', 'absolute');
  if (_class) {
    ul.attr('class', _class);
  }

  // update
  var bbox = el.getBoundingClientRect();
  d.style({
    top: (bbox.top + 25) + "px",
    left: bbox.left + "px",
  });

  var drop = dropdown().items(items);
  d.call(drop);
  return drop;

}
function dialog(context) {
  var code = '';
  var evt = d3.dispatch('code', 'template');

  function _dialog (el) {

    var codeEditor = el.selectAll('textarea#code')
      .data([code]);

    var enter = codeEditor.enter();
    var divHeader = enter.append('div')
      .attr('class','header');
    var divOptions = enter.append('div')
      .attr('class','options');

    divHeader.append('a')
      .attr('class','expandButton')
      .on('click', function(){
        // console.log(event.target);
        _expand();
      })

    divHeader.append('h1')
      .text('Odyssey editor');

    divOptions.append('a').attr('class', 'downloadButton').on('click', function() {
        var md = el.select('textarea').node().codemirror.getValue();
        exp.zip(md, context.template(), function(zipBlob) {
          saveAs(zipBlob, 'oddysey.zip');
        });
    });

    var templates = context.templates().map(function(d) { return d.title; });

    divOptions.append('a').attr('class', 'shareButton').on('click', function() {
      var md = el.select('textarea').node().codemirror.getValue();
      exp.gist(md, context.template(), function(gist) {
        console.log(gist);
        window.open(gist.html_url);
      });
    });

    divOptions.append('a').attr('class', 'helpButton').on('click', function() {
    });

    divHeader.append('p')
      .attr('id', 'show_slide')
      .text(templates[0])
      .on('click', function(d) {
        d3.event.stopPropagation();
        var self = this;
        open(this, templates, 'drop-right').on('click', function(value) {
          evt.template(value);
          close();
          d3.select(self).text(value);
        });
      });

    var textarea = enter.append('textarea')
      .attr('id', 'code')
      .on('keyup.editor', function() {
        evt.code(this.value);
      });

    textarea.each(function() {
      var codemirror = this.codemirror = CodeMirror.fromTextArea(this, {
        mode: "markdown",
        lineWrapping: true
      });
      this.codemirror.on('change', function(c) {
        evt.code(c.getValue());
        placeActionButtons(el, codemirror);
      });
    });

    function _expand() {
      var _t = d3.select('#editor_modal');
      var _hassClass = _t.classed('expanded')
      _t.classed('expanded', !_hassClass);

      var _b = d3.select('a.expandButton');
      _b.classed('expanded', !_hassClass);

    }

    // update
    codeEditor.each(function(d) {
      this.codemirror.setValue(d);
      placeActionButtons(el, this.codemirror);
    });

    context.on('error.editor', function(errors) {
      var e = el.selectAll('.error').data(errors)
      e.enter()
        .append('div')
        .attr('class', 'error')
      e.text(String)

      e.exit().remove();
    })

  }

  var SLIDE_REGEXP = /^#[^#]+/i;
  var ACTIONS_BLOCK_REGEXP = /\s*```/i;

  // adds action to slideNumber.
  // creates it if the slide does not have any action
  function addAction(codemirror, slideNumer, action) {
    // search for a actions block
    var currentLine;
    var c = 0;
    for (var i = slideNumer + 1; i < codemirror.lineCount(); ++i) {
      var line = codemirror.getLineHandle(i).text;
      if (ACTIONS_BLOCK_REGEXP.exec(line)) {
        if (++c === 2) {
          // inser in the previous line
          currentLine = codemirror.getLineHandle(i);
          codemirror.setLine(i, action + "\n" + currentLine.text);
          return;
        }
      } else if(SLIDE_REGEXP.exec(line)) {
        // not found, inser a block
        currentLine = codemirror.getLineHandle(slideNumer);
        codemirror.setLine(slideNumer, currentLine.text + "\n```\n" + action +"\n```\n");
        return;
      }
    }
    // insert at the end
    currentLine = codemirror.getLineHandle(slideNumer);
    codemirror.setLine(slideNumer, currentLine.text + "\n```\n"+ action + "\n```\n");
  }

  // place actions buttons on the left of the beggining of each slide
  function placeActionButtons(el, codemirror) {

    // search for h1's
    var positions = [];
    var lineNumber = 0;
    codemirror.eachLine(function(a) {
      if (SLIDE_REGEXP.exec(a.text)) {
         positions.push({
           pos: codemirror.heightAtLine(lineNumber),
           line: lineNumber
         });
      }
      ++lineNumber;
    });

    //remove previously added buttons
    el.selectAll('.actionButton').remove()

    var buttons = el.selectAll('.actionButton')
      .data(positions);

    // enter
    buttons.enter()
      .append('div')
      .attr('class', 'actionButton')
      .style({ position: 'absolute' })
      .html('add')
      .on('click', function(d) {
        d3.event.stopPropagation();
        var self = this;
        open(this, context.actions()).on('click', function(e) {
          context.getAction(e, function(action) {
            addAction(codemirror, d.line, action);
          });
          close(self);
        });
      });

    el.on('click.actionbutton', function() {
      //close popup
      close();
    })

    // update
    var LINE_HEIGHT = 28;
    buttons.style({
      top: function(d) { return (d.pos - LINE_HEIGHT) + "px"; },
      left: 16 + "px"
    });

  }

  _dialog.code = function(_) {
    if (!arguments.length) return _;
    code = _;
    return _dialog;
  };

  return d3.rebind(_dialog, evt, 'on');
}

module.exports = dialog;
<commit_msg>fixed add buttons position<commit_after>
var dropdown = require('./dropdown');
var saveAs = require('../vendor/FileSaver');
var exp = require('./gist');

function close(el) {
  var d = d3.select(document.body).selectAll('#actionDropdown').data([]);
  d.exit().remove();
}

function open(el, items, _class) {
  var d = d3.select(document.body).selectAll('#actionDropdown').data([0]);
  // enter
  var ul = d.enter().append('ul').attr('id', 'actionDropdown').style('position', 'absolute');
  if (_class) {
    ul.attr('class', _class);
  }

  // update
  var bbox = el.getBoundingClientRect();
  d.style({
    top: (bbox.top + 25) + "px",
    left: bbox.left + "px",
  });

  var drop = dropdown().items(items);
  d.call(drop);
  return drop;

}
function dialog(context) {
  var code = '';
  var evt = d3.dispatch('code', 'template');

  function _dialog (el) {

    var codeEditor = el.selectAll('textarea#code')
      .data([code]);

    var enter = codeEditor.enter();
    var divHeader = enter.append('div')
      .attr('class','header');
    var divOptions = enter.append('div')
      .attr('class','options');

    divHeader.append('a')
      .attr('class','expandButton')
      .on('click', function(){
        // console.log(event.target);
        _expand();
      })

    divHeader.append('h1')
      .text('Odyssey editor');

    divOptions.append('a').attr('class', 'downloadButton').on('click', function() {
        var md = el.select('textarea').node().codemirror.getValue();
        exp.zip(md, context.template(), function(zipBlob) {
          saveAs(zipBlob, 'oddysey.zip');
        });
    });

    var templates = context.templates().map(function(d) { return d.title; });

    divOptions.append('a').attr('class', 'shareButton').on('click', function() {
      var md = el.select('textarea').node().codemirror.getValue();
      exp.gist(md, context.template(), function(gist) {
        console.log(gist);
        window.open(gist.html_url);
      });
    });

    divOptions.append('a').attr('class', 'helpButton').on('click', function() {
    });

    divHeader.append('p')
      .attr('id', 'show_slide')
      .text(templates[0])
      .on('click', function(d) {
        d3.event.stopPropagation();
        var self = this;
        open(this, templates, 'drop-right').on('click', function(value) {
          evt.template(value);
          close();
          d3.select(self).text(value);
        });
      });

    var textarea = enter.append('textarea')
      .attr('id', 'code')
      .on('keyup.editor', function() {
        evt.code(this.value);
      });

    function debounce(fn, t) {
      var i;
      return function() {
        var args = arguments;
        clearTimeout(i);
        i = setTimeout(function() { fn.apply(window, arguments); }, t);
      }
    }

    textarea.each(function() {
      var codemirror = this.codemirror = CodeMirror.fromTextArea(this, {
        mode: "markdown",
        lineWrapping: true
      });
      var showActions = debounce(function() { placeActionButtons(el, codemirror); }, 500);
      var hideActions = debounce(function() { el.selectAll('.actionButton').remove(); }, 20);
      codemirror.on('scroll',  function() {
        showActions();
        hideActions();
      });
      this.codemirror.on('change', function(c) {
        evt.code(c.getValue());
        placeActionButtons(el, codemirror);
      });
    });

    function _expand() {
      var _t = d3.select('#editor_modal');
      var _hassClass = _t.classed('expanded')
      _t.classed('expanded', !_hassClass);

      var _b = d3.select('a.expandButton');
      _b.classed('expanded', !_hassClass);

    }

    // update
    codeEditor.each(function(d) {
      this.codemirror.setValue(d);
      placeActionButtons(el, this.codemirror);
    });

    context.on('error.editor', function(errors) {
      var e = el.selectAll('.error').data(errors)
      e.enter()
        .append('div')
        .attr('class', 'error')
      e.text(String)

      e.exit().remove();
    })

  }

  var SLIDE_REGEXP = /^#[^#]+/i;
  var ACTIONS_BLOCK_REGEXP = /\s*```/i;

  // adds action to slideNumber.
  // creates it if the slide does not have any action
  function addAction(codemirror, slideNumer, action) {
    // search for a actions block
    var currentLine;
    var c = 0;
    for (var i = slideNumer + 1; i < codemirror.lineCount(); ++i) {
      var line = codemirror.getLineHandle(i).text;
      if (ACTIONS_BLOCK_REGEXP.exec(line)) {
        if (++c === 2) {
          // inser in the previous line
          currentLine = codemirror.getLineHandle(i);
          codemirror.setLine(i, action + "\n" + currentLine.text);
          return;
        }
      } else if(SLIDE_REGEXP.exec(line)) {
        // not found, inser a block
        currentLine = codemirror.getLineHandle(slideNumer);
        codemirror.setLine(slideNumer, currentLine.text + "\n```\n" + action +"\n```\n");
        return;
      }
    }
    // insert at the end
    currentLine = codemirror.getLineHandle(slideNumer);
    codemirror.setLine(slideNumer, currentLine.text + "\n```\n"+ action + "\n```\n");
  }

  // place actions buttons on the left of the beggining of each slide
  function placeActionButtons(el, codemirror) {

    // search for h1's
    var positions = [];
    var lineNumber = 0;
    codemirror.eachLine(function(a) {
      if (SLIDE_REGEXP.exec(a.text)) {
         positions.push({
           pos: codemirror.heightAtLine(lineNumber),
           line: lineNumber
         });
      }
      ++lineNumber;
    });

    //remove previously added buttons
    el.selectAll('.actionButton').remove()

    var buttons = el.selectAll('.actionButton')
      .data(positions);

    // enter
    buttons.enter()
      .append('div')
      .attr('class', 'actionButton')
      .style({ position: 'absolute' })
      .html('add')
      .on('click', function(d) {
        d3.event.stopPropagation();
        var self = this;
        open(this, context.actions()).on('click', function(e) {
          context.getAction(e, function(action) {
            addAction(codemirror, d.line, action);
          });
          close(self);
        });
      });

    el.on('click.actionbutton', function() {
      //close popup
      close();
    })

    // update
    var LINE_HEIGHT = 38;
    buttons.style({
      top: function(d) { return (d.pos - LINE_HEIGHT) + "px"; },
      left: 16 + "px"
    });

  }

  _dialog.code = function(_) {
    if (!arguments.length) return _;
    code = _;
    return _dialog;
  };

  return d3.rebind(_dialog, evt, 'on');
}

module.exports = dialog;
<|endoftext|>