<commit_before>// @flow

import Icon from '@conveyal/woonerf/components/icon'
import React, {Component} from 'react'
import { LinkContainer } from 'react-router-bootstrap'
import {
  FormGroup,
  InputGroup,
  ListGroup,
  ListGroupItem,
  Row,
  Col,
  Button,
  Panel,
  FormControl,
  Glyphicon,
  Badge,
  ButtonGroup,
  DropdownButton,
  MenuItem
} from 'react-bootstrap'
import {Map, TileLayer, Rectangle} from 'react-leaflet'

import * as deploymentActions from '../../actions/deployments'
import Loading from '../../../common/components/Loading'
import EditableTextField from '../../../common/components/EditableTextField'
import Title from '../../../common/components/Title'
import WatchButton from '../../../common/containers/WatchButton'
import {getComponentMessages, getConfigProperty} from '../../../common/util/config'
import {formatTimestamp, fromNow} from '../../../common/util/date-time'
import {defaultTileLayerProps} from '../../../common/util/maps'
import { versionsSorter } from '../../../common/util/util'
import {getServerDeployedTo} from '../../util/deployment'
import type {Props as ContainerProps} from '../../containers/ActiveDeploymentViewer'
import type {
  ServerJob,
  ShapefileExportType,
  SummarizedFeedVersion
} from '../../../types'
import type {ManagerUserState} from '../../../types/reducers'

import CurrentDeploymentPanel from './CurrentDeploymentPanel'
import DeploymentConfigurationsPanel from './DeploymentConfigurationsPanel'
import DeploymentConfirmModal from './DeploymentConfirmModal'
import DeploymentVersionsTable from './DeploymentVersionsTable'
import PeliasPanel from './PeliasPanel'

type Props = ContainerProps & {
  addFeedVersion: typeof deploymentActions.addFeedVersion,
  deployJobs: Array<ServerJob>,
  deployToTarget: typeof deploymentActions.deployToTarget,
  downloadBuildArtifact: typeof deploymentActions.downloadBuildArtifact,
  downloadDeployment: typeof deploymentActions.downloadDeployment,
  downloadDeploymentShapes: typeof deploymentActions.downloadDeploymentShapes,
  fetchDeployment: typeof deploymentActions.fetchDeployment,
  incrementAllVersionsToLatest: typeof deploymentActions.incrementAllVersionsToLatest,
  terminateEC2InstanceForDeployment: typeof deploymentActions.terminateEC2InstanceForDeployment,
  updateDeployment: typeof deploymentActions.updateDeployment,
  user: ManagerUserState
}

type State = {
  searchText: ?string,
  target: ?string
}

const BOUNDS_LIMIT = 10 // Limit for the decimal degrees span

export default class DeploymentViewer extends Component<Props, State> {
  messages = getComponentMessages('DeploymentViewer')
  state = {
    otp: [],
    searchText: null,
    target: null
  }

  componentDidMount () {
    this.resetMap()
  }

  /**
   * When the component remounts after an initial mount, the map needs to be
   * reset using Leaflet.invalidateSize so that the map tiles do not "grey out."
   *
   * Also, this re-fits the bounds to the deployment bounds.
   */
  resetMap () {
    setTimeout(() => {
      if (this.refs.map) {
        this.refs.map.leafletElement.invalidateSize()
        this.refs.map.leafletElement.fitBounds(this._getBounds())
      }
    }, 500)
  }

  _getBounds = () => {
    const {east, north, south, west} = this.props.deployment.projectBounds ||
      {east: 10, north: 10, west: -10, south: -10}
    return [[north, east], [south, west]]
  }

  _onAddFeedSource = (feedSourceId: string) => {
    const feed = this.props.feedSources.find(fs => fs.id === feedSourceId)
    const id = feed && feed.latestVersionId
    if (!id) {
      console.warn('No latest version ID found for feed', feed)
      return
    }
    this.props.addFeedVersion(this.props.deployment, {id})
  }

  _onChangeSearch = (evt: SyntheticInputEvent<HTMLInputElement>) =>
    this.setState({searchText: evt.target.value})

  _onChangeName = (name: string) => this._updateDeployment({name})

  _onClickDownload = () => this.props.downloadDeployment(this.props.deployment)

  _onClickDownloadShapes = (type: ShapefileExportType) => this.props.downloadDeploymentShapes(this.props.deployment, type)

  _onCloseModal = () => this.setState({target: null})

  _onSelectTarget = (target: string) => this.setState({target})

  _updateDeployment = (props: {[string]: any}) => {
    const {deployment, updateDeployment} = this.props
    updateDeployment(deployment, props)
  }

  _incrementAllVersionsToLatest = () => {
    const {deployment, incrementAllVersionsToLatest} = this.props
    const feedsNeedingUpdates = deployment.feedVersions.filter(
      v => !deployment.pinnedfeedVersionIds.includes(v.id) &&
        v.id !== v.feedSource.latestVersionId
    )
    const message = `Are you sure you would like to advance ${feedsNeedingUpdates.length} feed(s) to their latest versions?`
    if (window.confirm(message)) {
      incrementAllVersionsToLatest(deployment)
    }
  }

  renderHeader () {
    const {
      deployJobs,
      deployment,
      project,
      user
    } = this.props
    const na = (N/A)
    const isWatchingDeployment = user.subscriptions &&
      user.subscriptions.hasFeedSubscription(
        project.id,
        deployment.id,
        'deployment-updated'
      )

    return (

          <ButtonGroup className='pull-right'>
            <WatchButton
              isWatching={isWatchingDeployment}
              user={user}
              target={deployment.id}
              subscriptionType='deployment-updated' />
            <Button
              bsStyle='default'
              onClick={this._onClickDownload}>
              <Glyphicon glyph='download' /> {this.messages('download')}
            </Button>
            <DropdownButton
              id='shp-export'
              onSelect={this._onClickDownloadShapes}
              title={

                  <Icon type='file-zip-o' />
                   Export (.shp)

              }>
              <MenuItem eventKey='STOPS'><Icon type='map-marker' /> Stops</MenuItem>
              <MenuItem eventKey='ROUTES'><Icon type='ellipsis-h' /> Routes</MenuItem>
            </DropdownButton>
            <DropdownButton
              bsStyle='primary'
              id='deploy-server-dropdown'
              pullRight
              disabled={!project.otpServers || !project.otpServers.length}
              title={project.otpServers && project.otpServers.length
                ?
                  <Glyphicon glyph='globe' />{' '}
                  {this.messages('deploy')}

                : {this.messages('noServers')}
              }
              onSelect={this._onSelectTarget}>
              {project.otpServers
                ? project.otpServers.map((server, i) => (
                  <MenuItem
                    data-test-id={`deploy-server-${i}-button`}
                    // Disable server if the server is missing a name,
                    // has neither an internal URL or s3 bucket name,
                    // or has a load balancer target group set, but is
                    // set for a test feed source (with a non-default router).
                    disabled={
                      deployJobs.length > 0 ||
                      !server.name ||
                      (
                        (!server.internalUrl || server.internalUrl.length === 0) &&
                        !server.s3Bucket
                      ) ||
                      (server.ec2Info && server.ec2Info.targetGroupArn && deployment.feedSourceId)
                    }
                    eventKey={server.id}
                    key={server.id}
                  >
                    {server.name || '[Unnamed]'}
                  </MenuItem>
                ))
                : null
              }
              {project.otpServers && <MenuItem divider />}
              <LinkContainer
                to='/admin/servers'
                key={project.id}>
                <MenuItem>
                  <Icon type='cog' /> Manage servers
                </MenuItem>
              </LinkContainer>
            </DropdownButton>
          </ButtonGroup>
          <EditableTextField
            inline
            style={{marginLeft: '5px'}}
            value={deployment.name}
            onChange={this._onChangeName} />

          <Icon type='clock-o' />
          {' '}
          Last deployed {deployment.lastDeployed ? fromNow(deployment.lastDeployed) : na}

    )
  }

  renderMap () {
    const { deployment } = this.props
    const isMissingBounds = !deployment.projectBounds
    const {east, north, south, west} = deployment.projectBounds ||
      {east: 10, north: 10, west: -10, south: -10}
    const boundsTooLarge = east - west > BOUNDS_LIMIT || north - south > BOUNDS_LIMIT
    const bounds = this._getBounds()

    // Flex V2 routes may have no stops and therefore have no bounds detected by
    // datatools server. The bounds will then include NaN. Since there is nothing to
    // represent, we should render nothing.
    // $FlowFixMe flow array method list is outdated
    if (bounds.flat().includes('NaN')) return null
    return (
      <Map
        ref='map'
        bounds={bounds}
        scrollWheelZoom={false}
        style={{width: '100%', height: '300px'}}>
        <TileLayer {...defaultTileLayerProps()} />
        {!isMissingBounds &&
          <Rectangle
            bounds={bounds}
            // If bounds are too large, draw rectangle in red
            // (otherwise use the default path color).
            color={boundsTooLarge ? 'red' : '#3388ff'}
            fillOpacity={0} />
        }
      </Map>
    )
  }

  renderDeployentVersionsTableHeader (versions: Array<SummarizedFeedVersion>) {
    const { deployment, feedSources, project } = this.props
    const { feedVersions } = deployment
    if (!deployment || !project || !feedSources) return <Loading />
    // Only permit adding feed sources to non-feed source deployments.
    const deployableFeeds = !deployment.feedSourceId && feedSources
      ? feedSources.filter(fs =>
        // Include feed sources that are not currently added, have feed versions,
        // are marked deployable, and have been validated.
        feedVersions && feedVersions.findIndex(v => v.feedSource.id === fs.id) === -1 &&
        fs.deployable &&
        fs.latestValidation
      )
      : []
    const earliestDate = formatTimestamp(`${Math.min(...versions.map(v => +v.validationResult.startDate))}`, false)
    const latestDate = formatTimestamp(`${Math.max(...versions.map(v => +v.validationResult.endDate))}`, false)
    const feedsAreUpToDate = deployment.feedVersions.every(
      v => deployment.pinnedfeedVersionIds.includes(v.id) ||
        v.id === v.feedSource.latestVersionId
    )
    return (
      <Row>
        <Col xs={6}>

            <Glyphicon glyph='list' /> {this.messages('versions')}{' '}
            <Badge>{versions.length}</Badge>

              <Glyphicon glyph='calendar' />{' '}
              {earliestDate} {this.messages('to')} {latestDate}

        </Col>
        <Col xs={6}>

            <Button
              bsStyle={feedsAreUpToDate ? 'success' : 'default'}
              disabled={feedsAreUpToDate}
              onClick={this._incrementAllVersionsToLatest}
            >
              <Icon
                type={feedsAreUpToDate ? 'check-circle-o' : 'arrow-circle-o-right'}
              />{' '}
              {feedsAreUpToDate
                ? 'All feeds up-to-date!'
                : 'Advance all feeds'
              }
            </Button>
            <FormGroup style={{marginLeft: '5px', marginBottom: 0}}>
              <InputGroup>
                <FormControl
                  type='text'
                  className='pull-right'
                  style={{width: '160px'}}
                  placeholder={this.messages('search')}
                  onChange={this._onChangeSearch} />
                <DropdownButton
                  id='add-feedsource-button'
                  componentClass={InputGroup.Button}
                  disabled={!deployableFeeds.length}
                  title={deployableFeeds.length
                    ?
                      <Glyphicon glyph='plus' />{' '}
                      {this.messages('addFeedSource')}

                    : {this.messages('allFeedsAdded')}
                  }
                  onSelect={this._onAddFeedSource}>
                  {deployableFeeds.map((fs, i) => (
                    <MenuItem MenuItem key={i} eventKey={fs.id}>{fs.name}</MenuItem>
                  ))}
                </DropdownButton>
              </InputGroup>
            </FormGroup>

        </Col>
      </Row>
    )
  }

  render () {
    const {
      deployJobs,
      deployment,
      deployToTarget,
      feedSources,
      project,
      updateDeployment
    } = this.props
    const {target} = this.state
    const {feedVersions} = deployment
    if (!deployment || !project || !feedSources) return <Loading />
    const versions = feedVersions
      ? feedVersions
        .filter(
          version =>
            version.feedSource.name
              .toLowerCase()
              .indexOf((this.state.searchText || '').toLowerCase()) !== -1
        )
        .sort(versionsSorter)
      : []
    const serverDeployedTo = getServerDeployedTo(deployment, project)
    const oldDeployment = (target && project.deployments)
      ? project.deployments.find(d => d.deployedTo === target && deployment.routerId === d.routerId)
      : null
    const appName = getConfigProperty('application.title')
    const title = `${appName ? appName + ' - ' : ''}${deployment.name}`
    return (

        <Title>{title}</Title>
        {target && (
          <DeploymentConfirmModal
            deployToTarget={deployToTarget}
            deployment={deployment}
            oldDeployment={oldDeployment}
            onClose={this._onCloseModal}
            project={project}
            target={target}
          />
        )}
        <Row>
          <Col sm={9}>
            <Panel header={this.renderHeader()}>
              <ListGroup fill>
                <ListGroupItem style={{ padding: '0px' }}>
                  {this.renderMap()}
                </ListGroupItem>
                <ListGroupItem>
                  {this.renderDeployentVersionsTableHeader(versions)}
                </ListGroupItem>
                {versions.length === 0 ? (

                    No feed sources found.

                ) : (
                  <DeploymentVersionsTable
                    deployment={deployment}
                    project={project}
                    versions={versions}
                  />
                )}
              </ListGroup>
            </Panel>
            {/* Pelias panel */}
            <PeliasPanel
              deployment={deployment}
              project={project}
              updateDeployment={updateDeployment}
            />
          </Col>
          <Col sm={3}>
            {/* Current deployment panel */}
            <CurrentDeploymentPanel
              deployJobs={deployJobs}
              deployment={deployment}
              downloadBuildArtifact={this.props.downloadBuildArtifact}
              fetchDeployment={this.props.fetchDeployment}
              project={project}
              server={serverDeployedTo}
              terminateEC2InstanceForDeployment={
                this.props.terminateEC2InstanceForDeployment
              }
            />
            {/* Configurations panel */}
            <DeploymentConfigurationsPanel
              deployment={deployment}
              updateDeployment={updateDeployment}
            />
          </Col>
        </Row>

    )
  }
}
<commit_msg>refactor(DeploymentViewer): remove test deployment restrictions<commit_after>// @flow

import Icon from '@conveyal/woonerf/components/icon'
import React, {Component} from 'react'
import { LinkContainer } from 'react-router-bootstrap'
import {
  FormGroup,
  InputGroup,
  ListGroup,
  ListGroupItem,
  Row,
  Col,
  Button,
  Panel,
  FormControl,
  Glyphicon,
  Badge,
  ButtonGroup,
  DropdownButton,
  MenuItem
} from 'react-bootstrap'
import {Map, TileLayer, Rectangle} from 'react-leaflet'

import * as deploymentActions from '../../actions/deployments'
import Loading from '../../../common/components/Loading'
import EditableTextField from '../../../common/components/EditableTextField'
import Title from '../../../common/components/Title'
import WatchButton from '../../../common/containers/WatchButton'
import {getComponentMessages, getConfigProperty} from '../../../common/util/config'
import {formatTimestamp, fromNow} from '../../../common/util/date-time'
import {defaultTileLayerProps} from '../../../common/util/maps'
import { versionsSorter } from '../../../common/util/util'
import {getServerDeployedTo} from '../../util/deployment'
import type {Props as ContainerProps} from '../../containers/ActiveDeploymentViewer'
import type {
  ServerJob,
  ShapefileExportType,
  SummarizedFeedVersion
} from '../../../types'
import type {ManagerUserState} from '../../../types/reducers'

import CurrentDeploymentPanel from './CurrentDeploymentPanel'
import DeploymentConfigurationsPanel from './DeploymentConfigurationsPanel'
import DeploymentConfirmModal from './DeploymentConfirmModal'
import DeploymentVersionsTable from './DeploymentVersionsTable'
import PeliasPanel from './PeliasPanel'

type Props = ContainerProps & {
  addFeedVersion: typeof deploymentActions.addFeedVersion,
  deployJobs: Array<ServerJob>,
  deployToTarget: typeof deploymentActions.deployToTarget,
  downloadBuildArtifact: typeof deploymentActions.downloadBuildArtifact,
  downloadDeployment: typeof deploymentActions.downloadDeployment,
  downloadDeploymentShapes: typeof deploymentActions.downloadDeploymentShapes,
  fetchDeployment: typeof deploymentActions.fetchDeployment,
  incrementAllVersionsToLatest: typeof deploymentActions.incrementAllVersionsToLatest,
  terminateEC2InstanceForDeployment: typeof deploymentActions.terminateEC2InstanceForDeployment,
  updateDeployment: typeof deploymentActions.updateDeployment,
  user: ManagerUserState
}

type State = {
  searchText: ?string,
  target: ?string
}

const BOUNDS_LIMIT = 10 // Limit for the decimal degrees span

export default class DeploymentViewer extends Component<Props, State> {
  messages = getComponentMessages('DeploymentViewer')
  state = {
    otp: [],
    searchText: null,
    target: null
  }

  componentDidMount () {
    this.resetMap()
  }

  /**
   * When the component remounts after an initial mount, the map needs to be
   * reset using Leaflet.invalidateSize so that the map tiles do not "grey out."
   *
   * Also, this re-fits the bounds to the deployment bounds.
   */
  resetMap () {
    setTimeout(() => {
      if (this.refs.map) {
        this.refs.map.leafletElement.invalidateSize()
        this.refs.map.leafletElement.fitBounds(this._getBounds())
      }
    }, 500)
  }

  _getBounds = () => {
    const {east, north, south, west} = this.props.deployment.projectBounds ||
      {east: 10, north: 10, west: -10, south: -10}
    return [[north, east], [south, west]]
  }

  _onAddFeedSource = (feedSourceId: string) => {
    const feed = this.props.feedSources.find(fs => fs.id === feedSourceId)
    const id = feed && feed.latestVersionId
    if (!id) {
      console.warn('No latest version ID found for feed', feed)
      return
    }
    this.props.addFeedVersion(this.props.deployment, {id})
  }

  _onChangeSearch = (evt: SyntheticInputEvent<HTMLInputElement>) =>
    this.setState({searchText: evt.target.value})

  _onChangeName = (name: string) => this._updateDeployment({name})

  _onClickDownload = () => this.props.downloadDeployment(this.props.deployment)

  _onClickDownloadShapes = (type: ShapefileExportType) => this.props.downloadDeploymentShapes(this.props.deployment, type)

  _onCloseModal = () => this.setState({target: null})

  _onSelectTarget = (target: string) => this.setState({target})

  _updateDeployment = (props: {[string]: any}) => {
    const {deployment, updateDeployment} = this.props
    updateDeployment(deployment, props)
  }

  _incrementAllVersionsToLatest = () => {
    const {deployment, incrementAllVersionsToLatest} = this.props
    const feedsNeedingUpdates = deployment.feedVersions.filter(
      v => !deployment.pinnedfeedVersionIds.includes(v.id) &&
        v.id !== v.feedSource.latestVersionId
    )
    const message = `Are you sure you would like to advance ${feedsNeedingUpdates.length} feed(s) to their latest versions?`
    if (window.confirm(message)) {
      incrementAllVersionsToLatest(deployment)
    }
  }

  renderHeader () {
    const {
      deployJobs,
      deployment,
      project,
      user
    } = this.props
    const na = (N/A)
    const isWatchingDeployment = user.subscriptions &&
      user.subscriptions.hasFeedSubscription(
        project.id,
        deployment.id,
        'deployment-updated'
      )

    return (

          <ButtonGroup className='pull-right'>
            <WatchButton
              isWatching={isWatchingDeployment}
              user={user}
              target={deployment.id}
              subscriptionType='deployment-updated' />
            <Button
              bsStyle='default'
              onClick={this._onClickDownload}>
              <Glyphicon glyph='download' /> {this.messages('download')}
            </Button>
            <DropdownButton
              id='shp-export'
              onSelect={this._onClickDownloadShapes}
              title={

                  <Icon type='file-zip-o' />
                   Export (.shp)

              }>
              <MenuItem eventKey='STOPS'><Icon type='map-marker' /> Stops</MenuItem>
              <MenuItem eventKey='ROUTES'><Icon type='ellipsis-h' /> Routes</MenuItem>
            </DropdownButton>
            <DropdownButton
              bsStyle='primary'
              id='deploy-server-dropdown'
              pullRight
              disabled={!project.otpServers || !project.otpServers.length}
              title={project.otpServers && project.otpServers.length
                ?
                  <Glyphicon glyph='globe' />{' '}
                  {this.messages('deploy')}

                : {this.messages('noServers')}
              }
              onSelect={this._onSelectTarget}>
              {project.otpServers
                ? project.otpServers.map((server, i) => (
                  <MenuItem
                    data-test-id={`deploy-server-${i}-button`}
                    // Disable server if the server is missing a name or
                    // has neither an internal URL or s3 bucket name.
                    // This used to check for test deployments (using the existence of deployment.feedSourceId)
                    // to avoid deploying a test deployment to a "production" router, however now that there
                    // is only one router (even for OTP1 instances) this functionality has been removed.
                    disabled={
                      deployJobs.length > 0 ||
                      !server.name ||
                      (
                        (!server.internalUrl || server.internalUrl.length === 0) &&
                        !server.s3Bucket
                      )
                    }
                    eventKey={server.id}
                    key={server.id}
                  >
                    {server.name || '[Unnamed]'}
                  </MenuItem>
                ))
                : null
              }
              {project.otpServers && <MenuItem divider />}
              <LinkContainer
                to='/admin/servers'
                key={project.id}>
                <MenuItem>
                  <Icon type='cog' /> Manage servers
                </MenuItem>
              </LinkContainer>
            </DropdownButton>
          </ButtonGroup>
          <EditableTextField
            inline
            style={{marginLeft: '5px'}}
            value={deployment.name}
            onChange={this._onChangeName} />

          <Icon type='clock-o' />
          {' '}
          Last deployed {deployment.lastDeployed ? fromNow(deployment.lastDeployed) : na}

    )
  }

  renderMap () {
    const { deployment } = this.props
    const isMissingBounds = !deployment.projectBounds
    const {east, north, south, west} = deployment.projectBounds ||
      {east: 10, north: 10, west: -10, south: -10}
    const boundsTooLarge = east - west > BOUNDS_LIMIT || north - south > BOUNDS_LIMIT
    const bounds = this._getBounds()

    // Flex V2 routes may have no stops and therefore have no bounds detected by
    // datatools server. The bounds will then include NaN. Since there is nothing to
    // represent, we should render nothing.
    // $FlowFixMe flow array method list is outdated
    if (bounds.flat().includes('NaN')) return null
    return (
      <Map
        ref='map'
        bounds={bounds}
        scrollWheelZoom={false}
        style={{width: '100%', height: '300px'}}>
        <TileLayer {...defaultTileLayerProps()} />
        {!isMissingBounds &&
          <Rectangle
            bounds={bounds}
            // If bounds are too large, draw rectangle in red
            // (otherwise use the default path color).
            color={boundsTooLarge ? 'red' : '#3388ff'}
            fillOpacity={0} />
        }
      </Map>
    )
  }

  renderDeployentVersionsTableHeader (versions: Array<SummarizedFeedVersion>) {
    const { deployment, feedSources, project } = this.props
    const { feedVersions } = deployment
    if (!deployment || !project || !feedSources) return <Loading />
    // Only permit adding feed sources to non-feed source deployments.
    const deployableFeeds = !deployment.feedSourceId && feedSources
      ? feedSources.filter(fs =>
        // Include feed sources that are not currently added, have feed versions,
        // are marked deployable, and have been validated.
        feedVersions && feedVersions.findIndex(v => v.feedSource.id === fs.id) === -1 &&
        fs.deployable &&
        fs.latestValidation
      )
      : []
    const earliestDate = formatTimestamp(`${Math.min(...versions.map(v => +v.validationResult.startDate))}`, false)
    const latestDate = formatTimestamp(`${Math.max(...versions.map(v => +v.validationResult.endDate))}`, false)
    const feedsAreUpToDate = deployment.feedVersions.every(
      v => deployment.pinnedfeedVersionIds.includes(v.id) ||
        v.id === v.feedSource.latestVersionId
    )
    return (
      <Row>
        <Col xs={6}>

            <Glyphicon glyph='list' /> {this.messages('versions')}{' '}
            <Badge>{versions.length}</Badge>

              <Glyphicon glyph='calendar' />{' '}
              {earliestDate} {this.messages('to')} {latestDate}

        </Col>
        <Col xs={6}>

            <Button
              bsStyle={feedsAreUpToDate ? 'success' : 'default'}
              disabled={feedsAreUpToDate}
              onClick={this._incrementAllVersionsToLatest}
            >
              <Icon
                type={feedsAreUpToDate ? 'check-circle-o' : 'arrow-circle-o-right'}
              />{' '}
              {feedsAreUpToDate
                ? 'All feeds up-to-date!'
                : 'Advance all feeds'
              }
            </Button>
            <FormGroup style={{marginLeft: '5px', marginBottom: 0}}>
              <InputGroup>
                <FormControl
                  type='text'
                  className='pull-right'
                  style={{width: '160px'}}
                  placeholder={this.messages('search')}
                  onChange={this._onChangeSearch} />
                <DropdownButton
                  id='add-feedsource-button'
                  componentClass={InputGroup.Button}
                  disabled={!deployableFeeds.length}
                  title={deployableFeeds.length
                    ?
                      <Glyphicon glyph='plus' />{' '}
                      {this.messages('addFeedSource')}

                    : {this.messages('allFeedsAdded')}
                  }
                  onSelect={this._onAddFeedSource}>
                  {deployableFeeds.map((fs, i) => (
                    <MenuItem MenuItem key={i} eventKey={fs.id}>{fs.name}</MenuItem>
                  ))}
                </DropdownButton>
              </InputGroup>
            </FormGroup>

        </Col>
      </Row>
    )
  }

  render () {
    const {
      deployJobs,
      deployment,
      deployToTarget,
      feedSources,
      project,
      updateDeployment
    } = this.props
    const {target} = this.state
    const {feedVersions} = deployment
    if (!deployment || !project || !feedSources) return <Loading />
    const versions = feedVersions
      ? feedVersions
        .filter(
          version =>
            version.feedSource.name
              .toLowerCase()
              .indexOf((this.state.searchText || '').toLowerCase()) !== -1
        )
        .sort(versionsSorter)
      : []
    const serverDeployedTo = getServerDeployedTo(deployment, project)
    const oldDeployment = (target && project.deployments)
      ? project.deployments.find(d => d.deployedTo === target && deployment.routerId === d.routerId)
      : null
    const appName = getConfigProperty('application.title')
    const title = `${appName ? appName + ' - ' : ''}${deployment.name}`
    return (

        <Title>{title}</Title>
        {target && (
          <DeploymentConfirmModal
            deployToTarget={deployToTarget}
            deployment={deployment}
            oldDeployment={oldDeployment}
            onClose={this._onCloseModal}
            project={project}
            target={target}
          />
        )}
        <Row>
          <Col sm={9}>
            <Panel header={this.renderHeader()}>
              <ListGroup fill>
                <ListGroupItem style={{ padding: '0px' }}>
                  {this.renderMap()}
                </ListGroupItem>
                <ListGroupItem>
                  {this.renderDeployentVersionsTableHeader(versions)}
                </ListGroupItem>
                {versions.length === 0 ? (

                    No feed sources found.

                ) : (
                  <DeploymentVersionsTable
                    deployment={deployment}
                    project={project}
                    versions={versions}
                  />
                )}
              </ListGroup>
            </Panel>
            {/* Pelias panel */}
            <PeliasPanel
              deployment={deployment}
              project={project}
              updateDeployment={updateDeployment}
            />
          </Col>
          <Col sm={3}>
            {/* Current deployment panel */}
            <CurrentDeploymentPanel
              deployJobs={deployJobs}
              deployment={deployment}
              downloadBuildArtifact={this.props.downloadBuildArtifact}
              fetchDeployment={this.props.fetchDeployment}
              project={project}
              server={serverDeployedTo}
              terminateEC2InstanceForDeployment={
                this.props.terminateEC2InstanceForDeployment
              }
            />
            {/* Configurations panel */}
            <DeploymentConfigurationsPanel
              deployment={deployment}
              updateDeployment={updateDeployment}
            />
          </Col>
        </Row>

    )
  }
}
<|endoftext|>


<commit_before>
const constants = require('../constants');

const tableCreator = require('./tableCreator');

const alasql = require('alasql');

const debugActivated = constants.DEBUG_ON;

const stringConcat = function stringConcat(...stringToConcat) {
  let fullString = '';
  for (let index = 0; index < stringToConcat.length; index+=1) {
    fullString += stringToConcat[index];
  }
  return fullString;
};

class entitiesCreator {

  static constuctor() {}

  static createUserTable() {
    tableCreator.initCreateTable(constants.ENTITIES_USER);

    tableCreator.addHeader(constants.HEADER_USER_TOKEN, 'VARCHAR(128)');
    tableCreator.addHeader(constants.HEADER_USER_PASSWORD, 'VARCHAR(64)');
    tableCreator.addHeader(constants.HEADER_USER_EMAIL, 'VARCHAR(128)');
    tableCreator.addHeader(constants.HEADER_USER_USERNAME, 'VARCHAR(32)');
    tableCreator.addHeader(constants.HEADER_USER_USER_ID, 'LONG');
    tableCreator.addHeader(constants.HEADER_USER_USER_CREATION_DATE, 'DATE');

    tableCreator.setPrimaryKeys(constants.HEADER_USER_USER_ID);

    const isTableCreatedSuccessful = tableCreator.end_create_table();

    if (debugActivated) {
      const debugUserTable = alasql(stringConcat('SELECT * FROM ', constants.ENTITIES_USER));
      console.log('DEBUG user table:');
      console.log(debugUserTable);
    }

    return isTableCreatedSuccessful;
  }

}
<commit_msg>Add createOrganizationTable entity in database under entitiesCreator.js<commit_after>
const constants = require('../constants');

const tableCreator = require('./tableCreator');

const alasql = require('alasql');

const debugActivated = constants.DEBUG_ON;

const stringConcat = function stringConcat(...stringToConcat) {
  let fullString = '';
  for (let index = 0; index < stringToConcat.length; index+=1) {
    fullString += stringToConcat[index];
  }
  return fullString;
};

class entitiesCreator {

  static constuctor() {}

  static createUserTable() {
    tableCreator.initCreateTable(constants.ENTITIES_USER);

    tableCreator.addHeader(constants.HEADER_USER_TOKEN, 'VARCHAR(128)');
    tableCreator.addHeader(constants.HEADER_USER_PASSWORD, 'VARCHAR(64)');
    tableCreator.addHeader(constants.HEADER_USER_EMAIL, 'VARCHAR(128)');
    tableCreator.addHeader(constants.HEADER_USER_USERNAME, 'VARCHAR(32)');
    tableCreator.addHeader(constants.HEADER_USER_USER_ID, 'LONG');
    tableCreator.addHeader(constants.HEADER_USER_USER_CREATION_DATE, 'DATE');

    tableCreator.setPrimaryKeys(constants.HEADER_USER_USER_ID);

    const isTableCreatedSuccessful = tableCreator.end_create_table();

    if (debugActivated) {
      const debugUserTable = alasql(stringConcat('SELECT * FROM ', constants.ENTITIES_USER));
      console.log('DEBUG user table:');
      console.log(debugUserTable);
    }

    return isTableCreatedSuccessful;
  }

  static createOrganizationTable() {
    tableCreator.init_create_table(constants.ENTITIES_ORGANIZATION);

    tableCreator.add_header(constants.HEADER_ORGANIZATION_ORGANIZATION_NAME, 'VARCHAR(64)');
    tableCreator.add_header(constants.HEADER_ORGANIZATION_CREATION_DATE, 'DATE');
    tableCreator.add_header(constants.HEADER_ORGANIZATION_INDEX, 'INT');
    tableCreator.add_header(constants.HEADER_ORGANIZATION_ORGANIZATION_ID, 'LONG');
    tableCreator.add_header(constants.HEADER_ORGANIZATION_USER_ID, 'LONG');
    tableCreator.add_header(constants.HEADER_ORGANIZATION_PARENT_ORGANIZATION_ID, 'LONG');

    tableCreator.set_foreign_key(constants.HEADER_ORGANIZATION_USER_ID, constants.ENTITIES_USER, constants.HEADER_USER_USER_ID);
    tableCreator.set_foreign_key(constants.HEADER_ORGANIZATION_PARENT_ORGANIZATION_ID, constants.ENTITIES_ORGANIZATION, constants.HEADER_ORGANIZATION_ORGANIZATION_ID);

    tableCreator.set_primary_keys(constants.HEADER_ORGANIZATION_ORGANIZATION_ID, constants.HEADER_ORGANIZATION_USER_ID);

    const isTableCreatedSuccessful = tableCreator.end_create_table();

    if (debugActivated) {
      const debugOrganizationTable = alasql(stringConcat('SELECT * FROM ', constants.ENTITIES_ORGANIZATION));
      console.log('DEBUG organization table:');
      console.log(debugOrganizationTable);
    }
}
<|endoftext|>


<commit_before><script type="text/javascript">
/* Jira Comment Numbering
 *
 * See https://github.com/sweavo/jiraMonkey/blob/master/jira_comment_numbering.js
 *
 * Inserts human-readable #1, #2, etc at the top of each comment.
 * Then other humans can type stuff like "I agree with comment #3"
 *
 * Put this in the announcement banner on your jira instance.
 * It's a bit pointless to have this as a tampermonkey script, as
 * you want to be able to use the numbers when communicating with
 * your colleagues, and tampermonkey will only put it in your
 * browser.
 *
 * TODO: Trigger the re-run of number_comments on expansion of
 *       show-more-comments, rather than doing it every 2 seconds
 *       regardless.
 * TODO: Allow #3 type notation within comments that will jump to
 *       the anchor of the referenced comment.
 *
 */
"use strict";

/* Whether to log stuff to console to show it working */
const JCN_LOG_TO_CONSOLE=false;

/* Insert child as the first child nodes of container. */
function prepend( container, child )
{
    container.insertBefore( child, container.childNodes[0] );
}

/* In a jira comments block, we start counting at 1 unless the "show more comments"
 * div is visible. In that case, we start at the number of hidden comments + 1
 */
function get_start_point( issue_actions_container )
{
    if ( ! issue_actions_container )
    {
        if ( JCN_LOG_TO_CONSOLE ) console.log( "JCN: issue_actions_container was null" );
        return 1;
    }
    var show_more =issue_actions_container.getElementsByClassName( 'show-more-comments' );
    if ( show_more.length === 0 )
    {
        return 1;
    }
    else
    {
        if ( JCN_LOG_TO_CONSOLE ) console.log( 'JCN: show-more found' );
        var number = show_more[0].getAttribute( 'data-collapsed-count' );
        if ( JCN_LOG_TO_CONSOLE ) console.log( "JCN: " + number );
        return 1 + parseInt( number );
    }
}

/* true if and only if node is a textnode whose contents match text */
function isTextNodeMatching( node, text )
{
    if ( node.nodeType != 3 )
    {
        return false;
    }
    if ( JCN_LOG_TO_CONSOLE ) console.log( "JCN: existing: " + node.textContent );
    if ( JCN_LOG_TO_CONSOLE ) console.log( "JCN: new: " + text );
    return ( node.textContent == text ) ;
}

/* insert numbers to the start of the headers of comments on the browse page */
function number_comments( )
{
    if ( JCN_LOG_TO_CONSOLE ) console.log( 'JCN: number_comments start' );
    if ( ! document )
    {
        if ( JCN_LOG_TO_CONSOLE ) console.log( "JCN: document was null" );
        return;
    }
    var comments = document.getElementsByClassName( 'activity-comment' );
    var i;
    var issue_actions_container = document.getElementById( 'issue_actions_container' )
    if ( ! issue_actions_container )
    {
        /* This doesn't look like an issue view; bail out. */
        return;
    }
    var offset = get_start_point( issue_actions_container );

    for ( i = 0; i < comments.length; i++ ) {
        if ( ! comments[i] )
        {
            alert( 'comments[i] was null' );
            continue;
        }
        var deets = comments[i].getElementsByClassName( 'action-details' );
        if ( JCN_LOG_TO_CONSOLE ) console.log( "JCN: " + deets );
        var numberText =  "#" + ( i + offset );
        var firstChild =  deets[0].childNodes[0];
        if ( ! isTextNodeMatching( firstChild, numberText ) )
        {
            var newText = document.createTextNode( numberText );
            prepend( deets[0], newText );
        }
    }
    if ( JCN_LOG_TO_CONSOLE ) console.log( 'JCN: number_comments done' );
}

/* insert numbers and set an interval to repeat it. This is because triggering
 * number_comments on the click event of show-more-comments didn't always work.
 */
function number_comments_persistently( )
{
    setInterval( number_comments, 2000 );
    number_comments( );
}

// for non-tampermonkey bootstrapping
/* once the DOM is loaded, start the modifications. */
document.addEventListener( "DOMContentLoaded", number_comments_persistently, false );
// for tampermonkey bootstrapping:
//number_comments_persistently();

</script>
<commit_msg>everything except the detection part<commit_after><script type="text/javascript">
/* Jira Comment Numbering
 *
 * See https://github.com/sweavo/jiraMonkey/blob/master/jira_comment_numbering.js
 *
 * Inserts human-readable #1, #2, etc at the top of each comment.
 * Then other humans can type stuff like "I agree with comment #3"
 *
 * Put this in the announcement banner on your jira instance.
 * It's a bit pointless to have this as a tampermonkey script, as
 * you want to be able to use the numbers when communicating with
 * your colleagues, and tampermonkey will only put it in your
 * browser.
 *
 * TODO: Trigger the re-run of number_comments on expansion of
 *       show-more-comments, rather than doing it every 2 seconds
 *       regardless.
 * TODO: Allow #3 type notation within comments that will jump to
 *       the anchor of the referenced comment.
 *
 */
"use strict";

(function() {
    'use strict';

    /* Whether to log stuff to console to show it working */
    const JCN_LOG_TO_CONSOLE=false;

    /* Insert child as the first child nodes of container. */
    function prepend( container, child )
    {
        container.insertBefore( child, container.childNodes[0] );
    }

    /* In a jira comments block, we start counting at 1 unless the "show more comments"
     * div is visible. In that case, we start at the number of hidden comments + 1
     */
    function get_start_point( issue_actions_container )
    {
        if ( ! issue_actions_container )
        {
            if ( JCN_LOG_TO_CONSOLE ) console.log( "JCN: issue_actions_container was null" );
            return 1;
        }
        var show_more =issue_actions_container.getElementsByClassName( 'show-more-comments' );
        if ( show_more.length === 0 )
        {
            return 1;
        }
        else
        {
            if ( JCN_LOG_TO_CONSOLE ) console.log( 'JCN: show-more found' );
            var number = show_more[0].getAttribute( 'data-collapsed-count' );
            if ( JCN_LOG_TO_CONSOLE ) console.log( "JCN: " + number );
            return 1 + parseInt( number );
        }
    }

    /* true if and only if node is a textnode whose contents match text */
    function isTextNodeMatching( node, text )
    {
        if ( node.nodeType != 3 )
        {
            return false;
        }
        if ( JCN_LOG_TO_CONSOLE ) console.log( "JCN: existing: " + node.textContent );
        if ( JCN_LOG_TO_CONSOLE ) console.log( "JCN: new: " + text );
        return ( node.textContent == text ) ;
    }

    /* insert numbers to the start of the headers of comments on the browse page */
    function number_comments( )
    {
        if ( JCN_LOG_TO_CONSOLE ) console.log( 'JCN: number_comments start' );
        if ( ! document )
        {
            if ( JCN_LOG_TO_CONSOLE ) console.log( "JCN: document was null" );
            return;
        }
        var descending=false;

        if ( JCN_LOG_TO_CONSOLE ) console.log( "JCN: " + descending );

        var comments = document.getElementsByClassName( 'activity-comment' );
        var i;
        var issue_actions_container = document.getElementById( 'issue_actions_container' )
        if ( ! issue_actions_container )
        {
            /* This doesn't look like an issue view; bail out. */
            return;
        }
        var offset = get_start_point( issue_actions_container );

        for ( i = 0; i < comments.length; i++ ) {
            if ( ! comments[i] )
            {
                alert( 'comments[i] was null' );
                continue;
            }
            var deets = comments[i].getElementsByClassName( 'action-details' );
            if ( JCN_LOG_TO_CONSOLE ) console.log( "JCN: " + deets );
            var comment_number = descending ? comments.length - i : i + offset;
            var numberText = "#" + comment_number
            var firstChild =  deets[0].childNodes[0];
            if ( ! isTextNodeMatching( firstChild, numberText ) )
            {
                var newText = document.createTextNode( numberText );
                prepend( deets[0], newText );
            }
        }
        if ( JCN_LOG_TO_CONSOLE ) console.log( 'JCN: number_comments done' );
    }

    /* insert numbers and set an interval to repeat it. This is because triggering
     * number_comments on the click event of show-more-comments didn't always work.
     */
    function number_comments_persistently( )
    {
        setInterval( number_comments, 2000 );
        number_comments( );
    }

    // for non-tampermonkey bootstrapping
    // once the DOM is loaded, start the modifications.
    document.addEventListener( "DOMContentLoaded", number_comments_persistently, false );
    // for tampermonkey bootstrapping:
    //number_comments_persistently();
})();
</script>
<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict';

const chalk = require('chalk');
const {readJson} = require('fs-extra');
const {join} = require('path');
const semver = require('semver');
const {execRead, execUnlessDry, logPromise} = require('../utils');
const {projects} = require('../config');

const push = async ({cwd, dry, version}) => {
  const errors = [];
  const isPrerelease = semver.prerelease(version);
  const tag = isPrerelease ? 'next' : 'latest';

  const publishProject = async project => {
    try {
      const path = join(cwd, 'build', 'packages', project);
      await execUnlessDry(`npm publish --tag ${tag}`, {cwd: path, dry});

      const packagePath = join(
        cwd,
        'build',
        'packages',
        project,
        'package.json'
      );
      const packageJSON = await readJson(packagePath);
      const packageVersion = packageJSON.version;

      if (!dry) {
        // Wait a couple of seconds before querying NPM for status;
        // Anecdotally, querying too soon can result in a false negative.
        await new Promise(resolve => setTimeout(resolve, 5000));

        const status = JSON.parse(
          await execRead(`npm info ${project} dist-tags --json`)
        );
        const remoteVersion = status[tag];

        // Compare remote version to package.json version,
        // To better handle the case of pre-release versions.
        if (remoteVersion !== packageVersion) {
          throw Error(
            chalk`Published version {yellow.bold ${packageVersion}} for ` +
              chalk`{bold ${project}} but NPM shows {yellow.bold ${
                remoteVersion
              }}`
          );
        }

        // If we've just published a stable release,
        // Update the @next tag to also point to it (so @next doens't lag behind).
        if (!isPrerelease) {
          await execUnlessDry(
            `npm dist-tag add ${project}@${packageVersion} next`
          );
        }
      }
    } catch (error) {
      errors.push(error.message);
    }
  };

  await Promise.all(projects.map(publishProject));

  if (errors.length > 0) {
    throw Error(
      chalk`
      Failure publishing to NPM

      {white ${errors.join('\n')}}`
    );
  }
};

module.exports = async params => {
  return logPromise(push(params), 'Publishing packages to NPM');
};
<commit_msg>Fix the release script<commit_after>#!/usr/bin/env node

'use strict';

const chalk = require('chalk');
const {readJson} = require('fs-extra');
const {join} = require('path');
const semver = require('semver');
const {execRead, execUnlessDry, logPromise} = require('../utils');
const {projects} = require('../config');

const push = async ({cwd, dry, version}) => {
  const errors = [];
  const isPrerelease = semver.prerelease(version);
  const tag = isPrerelease ? 'next' : 'latest';

  const publishProject = async project => {
    try {
      const path = join(cwd, 'build', 'packages', project);
      await execUnlessDry(`npm publish --tag ${tag}`, {cwd: path, dry});

      const packagePath = join(
        cwd,
        'build',
        'packages',
        project,
        'package.json'
      );
      const packageJSON = await readJson(packagePath);
      const packageVersion = packageJSON.version;

      if (!dry) {
        // Wait a couple of seconds before querying NPM for status;
        // Anecdotally, querying too soon can result in a false negative.
        await new Promise(resolve => setTimeout(resolve, 5000));

        const status = JSON.parse(
          await execRead(`npm info ${project} dist-tags --json`)
        );
        const remoteVersion = status[tag];

        // Compare remote version to package.json version,
        // To better handle the case of pre-release versions.
        if (remoteVersion !== packageVersion) {
          throw Error(
            chalk`Published version {yellow.bold ${packageVersion}} for ` +
              chalk`{bold ${project}} but NPM shows {yellow.bold ${
                remoteVersion
              }}`
          );
        }

        // If we've just published a stable release,
        // Update the @next tag to also point to it (so @next doens't lag behind).
        if (!isPrerelease) {
          await execUnlessDry(
            `npm dist-tag add ${project}@${packageVersion} next`,
            {cwd: path, dry}
          );
        }
      }
    } catch (error) {
      errors.push(error.message);
    }
  };

  await Promise.all(projects.map(publishProject));

  if (errors.length > 0) {
    throw Error(
      chalk`
      Failure publishing to NPM

      {white ${errors.join('\n')}}`
    );
  }
};

module.exports = async params => {
  return logPromise(push(params), 'Publishing packages to NPM');
};
<|endoftext|>


<commit_before>/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

// MODULES //

var getKeys = require( '@stdlib/utils/keys' );
var copy = require( '@stdlib/utils/copy' );
var isError = require( '@stdlib/assert/is-error' );
var typeName = require( './type.js' );

// MAIN //

/**
* Returns a JSON representation of an error object.
*
* @param {(Error|TypeError|SyntaxError|URIError|ReferenceError|RangeError|EvalError)} err - error to serialize
* @throws {TypeError} first argument must be an error object
* @returns {Object} JSON representation
*
* @example
* var err = new Error( 'beep' );
* var json = toJSON( err );
* // returns <Object>
*/
function toJSON( err ) {
	var keys;
	var out;
	var i;
	if ( !isError( err ) ) {
		throw new TypeError( 'invalid argument. Must provide an error object. Value: `' + err + '`.' );
	}
	out = {};

	// Guaranteed properties:
	out.type = typeName( err );
	out.message = err.message;

	// Possible general error properties...
	if ( err.name ) {
		out.name = err.name;
	}
	if ( err.stack ) {
		out.stack = err.stack;
	}
	// Possible Node.js (system error) properties...
	if ( err.code ) {
		out.code = err.code;
	}
	if ( err.errno ) {
		out.errno = err.errno;
	}
	if ( err.syscall ) {
		out.syscall = err.syscall;
	}
	// Any enumerable properties...
	keys = getKeys( err );
	for ( i = 0; i < keys.length; i++ ) {
		out[ keys[i] ] = copy( err[ keys[i] ] );
	}
	return out;
}

// EXPORTS //

module.exports = toJSON;
<commit_msg>Update doctest<commit_after>/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

// MODULES //

var getKeys = require( '@stdlib/utils/keys' );
var copy = require( '@stdlib/utils/copy' );
var isError = require( '@stdlib/assert/is-error' );
var typeName = require( './type.js' );

// MAIN //

/**
* Returns a JSON representation of an error object.
*
* @param {(Error|TypeError|SyntaxError|URIError|ReferenceError|RangeError|EvalError)} err - error to serialize
* @throws {TypeError} first argument must be an error object
* @returns {Object} JSON representation
*
* @example
* var err = new Error( 'beep' );
* var json = toJSON( err );
* // returns {...}
*/
function toJSON( err ) {
	var keys;
	var out;
	var i;
	if ( !isError( err ) ) {
		throw new TypeError( 'invalid argument. Must provide an error object. Value: `' + err + '`.' );
	}
	out = {};

	// Guaranteed properties:
	out.type = typeName( err );
	out.message = err.message;

	// Possible general error properties...
	if ( err.name ) {
		out.name = err.name;
	}
	if ( err.stack ) {
		out.stack = err.stack;
	}
	// Possible Node.js (system error) properties...
	if ( err.code ) {
		out.code = err.code;
	}
	if ( err.errno ) {
		out.errno = err.errno;
	}
	if ( err.syscall ) {
		out.syscall = err.syscall;
	}
	// Any enumerable properties...
	keys = getKeys( err );
	for ( i = 0; i < keys.length; i++ ) {
		out[ keys[i] ] = copy( err[ keys[i] ] );
	}
	return out;
}

// EXPORTS //

module.exports = toJSON;
<|endoftext|>


<commit_before>'use strict';

angular.module('emission.controllers', [])

.controller('RootCtrl', function($scope) {})

.controller('DashCtrl', function($scope) {})

.controller('SplashCtrl', function($scope, $ionicPlatform, $state, $interval, $rootScope) {
  console.log('SplashCtrl invoked');
  // Currently loads main or intro based on whether onboarding is complete.
  // But easily extensible to storing the last screen that the user was on,
  // or the users' preferred screen
  var changeState = function(destState) {
    console.log("loading "+destState);
    $state.go(destState);
    $interval.cancel(currPromise);
  };

  var loadPreferredScreen = function() {
    console.log("Checking to see whether we are ready to load the screen");
    if (window.plugins && window.plugins.appPreferences) {
      var prefs = plugins.appPreferences;
        prefs.fetch('setup_complete').then(function(value) {
          console.log('setup_complete result '+value);
          if (value == true) {
              changeState('root.main.diary');
          } else {
              changeState('root.intro');
          }
        }, function(error) {
          console.log("error "+error+" loading root.intro");
          changeState('root.intro');
        });
    } else {
      console.log("appPreferences plugin not installed, waiting...");
    }
  }
  var currPromise = $interval(loadPreferredScreen, 1000);
  $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams){
    console.log("Finished changing state from "+JSON.stringify(fromState)
        + " to "+JSON.stringify(toState));
  });
  console.log('SplashCtrl invoke finished');
})

.controller('ChatsCtrl', function($scope, Chats) {
  // With the new view caching in Ionic, Controllers are only called
  // when they are recreated or on app start, instead of every page change.
  // To listen for when this page is active (for example, to refresh data),
  // listen for the $ionicView.enter event:
  //
  //$scope.$on('$ionicView.enter', function(e) {
  //});

  $scope.chats = Chats.all();
  $scope.remove = function(chat) {
    Chats.remove(chat);
  };
})

.controller('ChatDetailCtrl', function($scope, $stateParams, Chats) {
  $scope.chat = Chats.get($stateParams.chatId);
})

.controller('AccountCtrl', function($scope) {
  $scope.settings = {
    enableFriends: true
  };
});
<commit_msg>Add an alert at the beginning of the splash screen<commit_after>'use strict';

angular.module('emission.controllers', [])

.controller('RootCtrl', function($scope) {})

.controller('DashCtrl', function($scope) {})

.controller('SplashCtrl', function($scope, $ionicPlatform, $state, $interval, $rootScope) {
  console.log('SplashCtrl invoked');
  alert("attach debugger!");
  // Currently loads main or intro based on whether onboarding is complete.
  // But easily extensible to storing the last screen that the user was on,
  // or the users' preferred screen
  var changeState = function(destState) {
    console.log("loading "+destState);
    $state.go(destState);
    $interval.cancel(currPromise);
  };

  var loadPreferredScreen = function() {
    console.log("Checking to see whether we are ready to load the screen");
    if (window.plugins && window.plugins.appPreferences) {
      var prefs = plugins.appPreferences;
        prefs.fetch('setup_complete').then(function(value) {
          console.log('setup_complete result '+value);
          if (value == true) {
              changeState('root.main.diary');
          } else {
              changeState('root.intro');
          }
        }, function(error) {
          console.log("error "+error+" loading root.intro");
          changeState('root.intro');
        });
    } else {
      console.log("appPreferences plugin not installed, waiting...");
    }
  }
  var currPromise = $interval(loadPreferredScreen, 1000);
  $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams){
    console.log("Finished changing state from "+JSON.stringify(fromState)
        + " to "+JSON.stringify(toState));
  });
  console.log('SplashCtrl invoke finished');
})

.controller('ChatsCtrl', function($scope, Chats) {
  // With the new view caching in Ionic, Controllers are only called
  // when they are recreated or on app start, instead of every page change.
  // To listen for when this page is active (for example, to refresh data),
  // listen for the $ionicView.enter event:
  //
  //$scope.$on('$ionicView.enter', function(e) {
  //});

  $scope.chats = Chats.all();
  $scope.remove = function(chat) {
    Chats.remove(chat);
  };
})

.controller('ChatDetailCtrl', function($scope, $stateParams, Chats) {
  $scope.chat = Chats.get($stateParams.chatId);
})

.controller('AccountCtrl', function($scope) {
  $scope.settings = {
    enableFriends: true
  };
});
<|endoftext|>


<commit_before>module.exports = {
    "source": ['body.catalog'],
    "required": true,
    "validation": {
        "type": "object",
        "required": true,
        "properties": {
            "name": { "type": "string", "required": true },
            "type": { "type": "string", "required": true, "enum": [ "service", "daemon", "mongo", "es" ] },
            "description": { "type": "string", "required": true },
            "recipe": {
                "type": "object",
                "required": true,
                "properties": {
                    "deployOptions": {
                        "type": "object",
                        "required": true,
                        "properties": {
                            "image": {
                                "type": "object",
                                "required": true,
                                "properties": {
                                    "prefix": { "type": "string", "required": false },
                                    "name": { "type": "string", "required": true },
                                    "tag": { "type": "string", "required": true },
                                    "pullPolicy": { "type": "string", "required": false }
                                }
                            },
                            "readinessProbe": {
                                "type": "object",
                                "required": false,
                                "properties": {
                                    "httpGet": {
                                        "type": "object",
                                        "required": true,
                                        "properties": {
                                            "path": { "type": "string", "required": true },
                                            "port": { "type": "string", "required": true }
                                        }
                                    },
                                    "initialDelaySeconds": { "type": "number", "required": true },
                                    "timeoutSeconds": { "type": "number", "required": true },
                                    "periodSeconds": { "type": "number", "required": true },
                                    "successThreshold": { "type": "number", "required": true },
                                    "failureThreshold": { "type": "number", "required": true }
                                }
                            },
                            "ports": { //NOTE: only applicable for nginx
                                "type": "object",
                                "required": false,
                                "properties": {
                                    "http": {
                                        "type": "object",
                                        "required": true,
                                        "properties": {
                                            "exposed": { "type": "string", "required": false },
                                            "target": { "type": "string", "required": false }
                                        }
                                    },
                                    "https": {
                                        "type": "object",
                                        "required": true,
                                        "properties": {
                                            "exposed": { "type": "string", "required": false },
                                            "target": { "type": "string", "required": false }
                                        }
                                    }
                                }
                            },
                            "volumes": {
                                "type": "array",
                                "required": false,
                                "properties": {
                                    //TODO: finalize schema for volumes
                                }
                            }
                        }
                    },
                    "buildOptions": {
                        "type": "object",
                        "required": false,
                        "properties": {
                            "env": {
                                "type": "object",
                                "required": false
                            },
                            "cmd": {
                                "type": "object",
                                "required": false,
                                "properties": {
                                    "pre_install": { "type": "string", "required": false },
                                    "install": { "type": "string", "required": false },
                                    "post_install": { "type": "string", "required": false },
                                    "pre_deploy": { "type": "string", "required": false },
                                    "deploy": { "type": "string", "required": false },
                                    "post_deploy": { "type": "string", "required": false }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
};
<commit_msg>Updated IMFV validation of catalog recipes<commit_after>module.exports = {
    "source": ['body.catalog'],
    "required": true,
    "validation": {
        "type": "object",
        "required": true,
        "properties": {
            "name": { "type": "string", "required": true },
            "type": { "type": "string", "required": true, "enum": [ "service", "daemon", "mongo", "es" ] },
            "description": { "type": "string", "required": true },
            "recipe": {
                "type": "object",
                "required": true,
                "properties": {
                    "deployOptions": {
                        "type": "object",
                        "required": true,
                        "properties": {
                            "image": {
                                "type": "object",
                                "required": true,
                                "properties": {
                                    "prefix": { "type": "string", "required": false },
                                    "name": { "type": "string", "required": true },
                                    "tag": { "type": "string", "required": true },
                                    "pullPolicy": { "type": "string", "required": false }
                                }
                            },
                            "readinessProbe": {
                                "type": "object",
                                "required": false,
                                "properties": {
                                    "httpGet": {
                                        "type": "object",
                                        "required": true,
                                        "properties": {
                                            "path": { "type": "string", "required": true },
                                            "port": { "type": "string", "required": true }
                                        }
                                    },
                                    "initialDelaySeconds": { "type": "number", "required": true },
                                    "timeoutSeconds": { "type": "number", "required": true },
                                    "periodSeconds": { "type": "number", "required": true },
                                    "successThreshold": { "type": "number", "required": true },
                                    "failureThreshold": { "type": "number", "required": true }
                                }
                            },
                            "ports": { //NOTE: only applicable for nginx
                                "type": "object",
                                "required": false,
                                "properties": {
                                    "http": {
                                        "type": "object",
                                        "required": true,
                                        "properties": {
                                            "exposed": { "type": "string", "required": false },
                                            "target": { "type": "string", "required": false }
                                        }
                                    },
                                    "https": {
                                        "type": "object",
                                        "required": true,
                                        "properties": {
                                            "exposed": { "type": "string", "required": false },
                                            "target": { "type": "string", "required": false }
                                        }
                                    }
                                }
                            },
                            "volumes": {
                                "type": "array",
                                "required": false,
                                "properties": {
                                    //TODO: finalize schema for volumes
                                }
                            }
                        }
                    },
                    "buildOptions": {
                        "type": "object",
                        "required": false,
                        "properties": {
                            "env": {
                                "type": "object",
                                "required": false
                            },
                            "cmd": {
                                "type": "object",
                                "required": false,
                                "properties": {
                                    "pre_install": {
                                        "type": "array",
                                        "required": false,
                                        "items": { "type": "string", "required": true }
                                    },
                                    "install": {
                                        "type": "array",
                                        "required": false,
                                        "items": { "type": "string", "required": true }
                                    },
                                    "post_install": {
                                        "type": "array",
                                        "required": false,
                                        "items": { "type": "string", "required": true }
                                    },
                                    "pre_deploy": {
                                        "type": "array",
                                        "required": false,
                                        "items": { "type": "string", "required": true }
                                    },
                                    "deploy": {
                                        "type": "array",
                                        "required": false,
                                        "items": { "type": "string", "required": true }
                                    },
                                    "post_deploy": {
                                        "type": "array",
                                        "required": false,
                                        "items": { "type": "string", "required": true }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
};
<|endoftext|>


<commit_before>(function () {
    'use strict';

    angular
        .module('everquizApp')
        .controller('PassingQuizController', PassingQuizController);

    PassingQuizController.$inject = ['quizService', 'scrollFactory'];

    function PassingQuizController(quizService, scrollFactory) {

        var vm = this;
        vm.checkResult = checkResult;

        quizService.get(quizService.activeQuiz).then(
            function (data) {
                vm.quiz = data;
                quizService.getQuestions(quizService.activeQuiz).then(
                    function (data) {
                        vm.quiz.questions = data.shuffle(data).splice(0,10);
                    }
                )
            }
        );

        function checkResult() {
            quizService.checkResult(vm.quiz);
            quizService.activeQuiz = null;
            goToElement('result');
        }

        function goToElement (elemID) {
            scrollFactory.scroll(elemID);
        }
    }
})();
<commit_msg>removed random 10 quizzes selection<commit_after>(function () {
    'use strict';

    angular
        .module('everquizApp')
        .controller('PassingQuizController', PassingQuizController);

    PassingQuizController.$inject = ['quizService', 'scrollFactory'];

    function PassingQuizController(quizService, scrollFactory) {

        var vm = this;
        vm.checkResult = checkResult;

        quizService.get(quizService.activeQuiz).then(
            function (data) {
                vm.quiz = data;
                quizService.getQuestions(quizService.activeQuiz).then(
                    function (data) {
                        vm.quiz.questions = data;
                    }
                )
            }
        );

        function checkResult() {
            quizService.checkResult(vm.quiz);
            quizService.activeQuiz = null;
            goToElement('result');
        }

        function goToElement (elemID) {
            scrollFactory.scroll(elemID);
        }
    }
})();
<|endoftext|>


<commit_before>import React from 'react';
import Immutable from 'immutable';
import classNames from 'classnames/bind';
import {NavLink} from 'fluxible-router';
import TreeUtil from './util/TreeUtil';
import {DragSource, DropTarget} from 'react-dnd';
import TreeNodeList from './TreeNodeList';
import TreeNodeTarget from './TreeNodeTarget';
import cheerio from 'cheerio';
import AttachSubdeck from '../ContentPanel/AttachSubdeck/AttachSubdeckModal';

const findAllDescendants = (node) => Immutable.Set.of(node).union(node.get('children') ? node.get('children').flatMap(findAllDescendants) : Immutable.List());

const treeNodeSource = {
    beginDrag(props) {
        return {
            item: props.item,
            parentNode: props.parentNode,
            nodeIndex: props.nodeIndex,
            allDescendants: findAllDescendants(props.item)
        };
    },
    canDrag(props, monitor){
        return props.parentNode.get('children').size > 1;
    }
};

class TreeNode extends React.Component {
    constructor(props) {
        super(props);
        this.state = {mouseover: 0};
    }

    //do not re-render in case of same props wihtout selector and if mode has not changed
    shouldComponentUpdate(nextProps, nextState) {
        return !(Immutable.is(nextProps.item, this.props.item) && nextProps === this.props.mode);
    }

    componentDidMount() {

    }

    componentDidUpdate() {

    }

    handleExpandIconClick(selector, e) {
        this.props.onToggleNode(selector);
        e.stopPropagation();
    }

    handleAddClick(selector, nodeSpec, e) {
        this.props.onAddNode(selector, nodeSpec);
        e.stopPropagation();
    }

    handleMouseOver(e) {
        this.setState({mouseover: 1});
        e.stopPropagation();
    }

    handleMouseOut(e) {
        this.setState({mouseover: 0});
        e.stopPropagation();
    }

    handleDeleteClick(selector, e) {
        this.props.onDeleteNode(selector);
        e.stopPropagation();
    }

    handleRenameClick(selector, e) {
        //only if user is logged in and has the rights
        if (this.props.username !== '' && (this.props.permissions.admin || this.props.permissions.edit)) {
            this.props.onRename(selector);
            e.stopPropagation();
        }
    }

    handleUndoRenameClick(selector, e) {
        this.props.onDoRename(selector);
        e.stopPropagation();
    }

    handleMenuClick(selector, e) {
        this.props.onSwitchOnAction(selector);
        e.stopPropagation();
    }

    handleEditFocus(e) {
        //select all content
        e.target.select();
    }

    handleNameChange(e) {
        //console.log(e.target.value);
    }

    handleKeyDown(selector, e) {
        switch (e.keyCode) {
            //case 9: // Tab
            case 13: // Enter
                if (e.target.value.trim()) {
                    this.props.onSave(selector, this.props.item.get('title'), e.target.value.trim());
                }
                break;
            case 27: // escape
                this.props.onUndoRename(selector);
                break;
        }
    }

    render() {
        let self = this;
        const {isDragging, connectDragSource, nodeIndex} = this.props;
        //adapt URLs based on the current page
        let nodeSelector = {
            id: this.props.rootNode.id,
            stype: this.props.item.get('type'),
            sid: this.props.item.get('id'),
            spath: this.props.item.get('path')
        };
        let nodeURL = TreeUtil.makeNodeURL(nodeSelector, this.props.page, this.props.mode);
        let childNodesDIV = '';
        let actionSigClass;
        let actionBtnsClass;
        if (this.props.item.get('type') === 'deck') {
            childNodesDIV = <TreeNodeList parentNode={self.props.item} onToggleNode={self.props.onToggleNode}
                                          onSwitchOnAction={self.props.onSwitchOnAction} onRename={self.props.onRename}
                                          onUndoRename={self.props.onUndoRename} onSave={self.props.onSave}
                                          onAddNode={self.props.onAddNode} onDeleteNode={self.props.onDeleteNode}
                                          onMoveNode={self.props.onMoveNode} mode={self.props.mode}
                                          page={self.props.page} rootNode={self.props.rootNode}
                                          username={self.props.username} permissions={self.props.permissions}/>;
        }
        actionSigClass = classNames({
            'hide-element': !this.props.item.get('selected') && !this.state.mouseover
        });
        let actionSignifier = ;
        actionBtnsClass = classNames({
            'hide-element': !this.props.item.get('onAction'),
            'ui right aligned': true
        });
        const duplicateItemClass = classNames({
            'ui button': true,
            'disabled': this.props.item.get('type') === 'deck'
        });
        let buttonStyle = {
            classNames : classNames({
                'ui':true,
                'disabled': !(this.props.PermissionsStore.permissions.admin || this.props.PermissionsStore.permissions.edit)
            }),
            iconSize : 'small',
            attached : '',
            disabled: this.props.item.get('type') === 'deck'
        };
        let actionBtns = (

                    <button className="ui button"
                            onClick={this.handleAddClick.bind(this, nodeSelector, {type: 'slide', id: 0})}
                            aria-label="Add Slide"
                            data-tooltip="Add Slide">

                    </button>
                    <button className="ui button"
                            onClick={this.handleAddClick.bind(this, nodeSelector, {type: 'deck', id: 0})}
                            aria-label="Add deck"
                            data-tooltip="Add deck">

                    </button>
                    <AttachSubdeck buttonStyle={buttonStyle} selector={nodeSelector}/>
                    <button className={duplicateItemClass} title="Duplicate"
                            onClick={this.handleAddClick.bind(this, nodeSelector, {
                                type: this.props.item.get('type'),
                                id: this.props.item.get('id')
                            })}
                            aria-label="Duplicate"
                            data-tooltip="Duplicate">

                    </button>
                    <button className="ui button" onClick={this.handleDeleteClick.bind(this, nodeSelector)}
                          aria-label="Delete"
                          data-tooltip="Delete">

                    </button>
                    {/*
                     <button className="ui disabled button" aria-label="Settings" data-tooltip="Settings">

                     </button>
                     */}

        );
        //change the node title style if it is selected
        const nodeTitle = cheerio.load(this.props.item.get('title')).text();
        let nodeTitleDIV = nodeTitle;
        if (this.props.item.get('selected')) {
            nodeTitleDIV =  {nodeTitle} ;
        }
        let nodeDIV = '';
        if (this.props.item.get('editable')) {
            nodeDIV = <input autoFocus onFocus={this.handleEditFocus} type="text" defaultValue={nodeTitle}
                             onChange={this.handleNameChange} onKeyDown={this.handleKeyDown.bind(this, nodeSelector)}/>;
            actionSignifier = '';
        } else {
            nodeDIV = <NavLink href={nodeURL} onDoubleClick={this.handleRenameClick.bind(this, nodeSelector)} >
                {nodeTitleDIV}</NavLink>;
        }
        //change the node icon based on the type of node and its expanded state
        let iconClass = classNames({
            'ui icon': true,
            'grey file text': (this.props.item.get('type') === 'slide'),
            'yellow folder link': (this.props.item.get('type') === 'deck'),
            'open': this.props.item.get('expanded')
        });
        //hide focused outline
        let compStyle = {
            outline: 'none',
            opacity: isDragging ? '0.4' : '1',
            position: 'relative'
        };

        return connectDragSource(

                {nodeIndex === 0 ? <TreeNodeTarget parentNode={self.props.parentNode} nodeIndex={nodeIndex}
                                               onMoveNode={self.props.onMoveNode} isAfterNode={false}/> : null }

                    {nodeDIV}
                    {(this.props.username === '' || !(this.props.permissions.admin || this.props.permissions.edit)) ? '' : actionSignifier}

                {actionBtns}
                {childNodesDIV}
                <TreeNodeTarget parentNode={self.props.parentNode} onMoveNode={self.props.onMoveNode}
                                nodeIndex={nodeIndex + 1} isAfterNode={true}/>

        );
    }
}

let TreeNodeWrapped = DragSource('tree-node', treeNodeSource, (connect, monitor) => ({
    connectDragSource: connect.dragSource(),
    isDragging: monitor.isDragging()
}))(TreeNode);

export default TreeNodeWrapped;
<commit_msg>Fix bug with undefined access in TreeNode<commit_after>import React from 'react';
import Immutable from 'immutable';
import classNames from 'classnames/bind';
import {NavLink} from 'fluxible-router';
import TreeUtil from './util/TreeUtil';
import {DragSource, DropTarget} from 'react-dnd';
import TreeNodeList from './TreeNodeList';
import TreeNodeTarget from './TreeNodeTarget';
import cheerio from 'cheerio';
import AttachSubdeck from '../ContentPanel/AttachSubdeck/AttachSubdeckModal';

const findAllDescendants = (node) => Immutable.Set.of(node).union(node.get('children') ? node.get('children').flatMap(findAllDescendants) : Immutable.List());

const treeNodeSource = {
    beginDrag(props) {
        return {
            item: props.item,
            parentNode: props.parentNode,
            nodeIndex: props.nodeIndex,
            allDescendants: findAllDescendants(props.item)
        };
    },
    canDrag(props, monitor){
        return props.parentNode.get('children').size > 1;
    }
};

class TreeNode extends React.Component {
    constructor(props) {
        super(props);
        this.state = {mouseover: 0};
    }

    //do not re-render in case of same props wihtout selector and if mode has not changed
    shouldComponentUpdate(nextProps, nextState) {
        return !(Immutable.is(nextProps.item, this.props.item) && nextProps === this.props.mode);
    }

    componentDidMount() {

    }

    componentDidUpdate() {

    }

    handleExpandIconClick(selector, e) {
        this.props.onToggleNode(selector);
        e.stopPropagation();
    }

    handleAddClick(selector, nodeSpec, e) {
        this.props.onAddNode(selector, nodeSpec);
        e.stopPropagation();
    }

    handleMouseOver(e) {
        this.setState({mouseover: 1});
        e.stopPropagation();
    }

    handleMouseOut(e) {
        this.setState({mouseover: 0});
        e.stopPropagation();
    }

    handleDeleteClick(selector, e) {
        this.props.onDeleteNode(selector);
        e.stopPropagation();
    }

    handleRenameClick(selector, e) {
        //only if user is logged in and has the rights
        if (this.props.username !== '' && (this.props.permissions.admin || this.props.permissions.edit)) {
            this.props.onRename(selector);
            e.stopPropagation();
        }
    }

    handleUndoRenameClick(selector, e) {
        this.props.onDoRename(selector);
        e.stopPropagation();
    }

    handleMenuClick(selector, e) {
        this.props.onSwitchOnAction(selector);
        e.stopPropagation();
    }

    handleEditFocus(e) {
        //select all content
        e.target.select();
    }

    handleNameChange(e) {
        //console.log(e.target.value);
    }

    handleKeyDown(selector, e) {
        switch (e.keyCode) {
            //case 9: // Tab
            case 13: // Enter
                if (e.target.value.trim()) {
                    this.props.onSave(selector, this.props.item.get('title'), e.target.value.trim());
                }
                break;
            case 27: // escape
                this.props.onUndoRename(selector);
                break;
        }
    }

    render() {
        let self = this;
        const {isDragging, connectDragSource, nodeIndex} = this.props;
        //adapt URLs based on the current page
        let nodeSelector = {
            id: this.props.rootNode.id,
            stype: this.props.item.get('type'),
            sid: this.props.item.get('id'),
            spath: this.props.item.get('path')
        };
        let nodeURL = TreeUtil.makeNodeURL(nodeSelector, this.props.page, this.props.mode);
        let childNodesDIV = '';
        let actionSigClass;
        let actionBtnsClass;
        if (this.props.item.get('type') === 'deck') {
            childNodesDIV = <TreeNodeList parentNode={self.props.item} onToggleNode={self.props.onToggleNode}
                                          onSwitchOnAction={self.props.onSwitchOnAction} onRename={self.props.onRename}
                                          onUndoRename={self.props.onUndoRename} onSave={self.props.onSave}
                                          onAddNode={self.props.onAddNode} onDeleteNode={self.props.onDeleteNode}
                                          onMoveNode={self.props.onMoveNode} mode={self.props.mode}
                                          page={self.props.page} rootNode={self.props.rootNode}
                                          username={self.props.username} permissions={self.props.permissions}/>;
        }
        actionSigClass = classNames({
            'hide-element': !this.props.item.get('selected') && !this.state.mouseover
        });
        let actionSignifier = ;
        actionBtnsClass = classNames({
            'hide-element': !this.props.item.get('onAction'),
            'ui right aligned': true
        });
        const duplicateItemClass = classNames({
            'ui button': true,
            'disabled': this.props.item.get('type') === 'deck'
        });
        let buttonStyle = {
            classNames : classNames({
                'ui':true,
                'disabled': !(this.props.permissions.admin || this.props.permissions.edit)
            }),
            iconSize : 'small',
            attached : '',
            disabled: this.props.item.get('type') === 'deck'
        };
        let actionBtns = (

                    <button className="ui button"
                            onClick={this.handleAddClick.bind(this, nodeSelector, {type: 'slide', id: 0})}
                            aria-label="Add Slide"
                            data-tooltip="Add Slide">

                    </button>
                    <button className="ui button"
                            onClick={this.handleAddClick.bind(this, nodeSelector, {type: 'deck', id: 0})}
                            aria-label="Add deck"
                            data-tooltip="Add deck">

                    </button>
                    <AttachSubdeck buttonStyle={buttonStyle} selector={nodeSelector}/>
                    <button className={duplicateItemClass} title="Duplicate"
                            onClick={this.handleAddClick.bind(this, nodeSelector, {
                                type: this.props.item.get('type'),
                                id: this.props.item.get('id')
                            })}
                            aria-label="Duplicate"
                            data-tooltip="Duplicate">

                    </button>
                    <button className="ui button" onClick={this.handleDeleteClick.bind(this, nodeSelector)}
                          aria-label="Delete"
                          data-tooltip="Delete">

                    </button>
                    {/*
                     <button className="ui disabled button" aria-label="Settings" data-tooltip="Settings">

                     </button>
                     */}

        );
        //change the node title style if it is selected
        const nodeTitle = cheerio.load(this.props.item.get('title')).text();
        let nodeTitleDIV = nodeTitle;
        if (this.props.item.get('selected')) {
            nodeTitleDIV =  {nodeTitle} ;
        }
        let nodeDIV = '';
        if (this.props.item.get('editable')) {
            nodeDIV = <input autoFocus onFocus={this.handleEditFocus} type="text" defaultValue={nodeTitle}
                             onChange={this.handleNameChange} onKeyDown={this.handleKeyDown.bind(this, nodeSelector)}/>;
            actionSignifier = '';
        } else {
            nodeDIV = <NavLink href={nodeURL} onDoubleClick={this.handleRenameClick.bind(this, nodeSelector)} >
                {nodeTitleDIV}</NavLink>;
        }
        //change the node icon based on the type of node and its expanded state
        let iconClass = classNames({
            'ui icon': true,
            'grey file text': (this.props.item.get('type') === 'slide'),
            'yellow folder link': (this.props.item.get('type') === 'deck'),
            'open': this.props.item.get('expanded')
        });
        //hide focused outline
        let compStyle = {
            outline: 'none',
            opacity: isDragging ? '0.4' : '1',
            position: 'relative'
        };

        return connectDragSource(

                {nodeIndex === 0 ? <TreeNodeTarget parentNode={self.props.parentNode} nodeIndex={nodeIndex}
                                               onMoveNode={self.props.onMoveNode} isAfterNode={false}/> : null }

                    {nodeDIV}
                    {(this.props.username === '' || !(this.props.permissions.admin || this.props.permissions.edit)) ? '' : actionSignifier}

                {actionBtns}
                {childNodesDIV}
                <TreeNodeTarget parentNode={self.props.parentNode} onMoveNode={self.props.onMoveNode}
                                nodeIndex={nodeIndex + 1} isAfterNode={true}/>

        );
    }
}

let TreeNodeWrapped = DragSource('tree-node', treeNodeSource, (connect, monitor) => ({
    connectDragSource: connect.dragSource(),
    isDragging: monitor.isDragging()
}))(TreeNode);

export default TreeNodeWrapped;
<|endoftext|>


<commit_before>const { getWorkspaces } = require('bolt');
const execa = require('execa');
const fs = require('fs-extra');
const path = require('path');

function exec(...args) {
  return execa(...args)
    .then(r => console.log(r.stdout))
    .catch(console.log);
}

function need(val, msg) {
  if (!val) {
    throw new Error(msg);
  }
}

async function babel({ envs }) {
  need(envs, 'Please specify at least one environment.');
  for (const w of await getWorkspaces()) {
    for (const env of envs.split(',')) {
      if ((w.config.files || []).indexOf(`dist/${env}`) === -1) continue;
      const src = path.join(w.dir, 'src');
      const dst = path.join(w.dir, 'dist', env);
      exec('babel', [src, '--out-dir', dst], {
        env: { BABEL_ENV: env }
      }).then(() =>
        exec('flow-copy-source', [
          '-i',
          '**/__tests__/**',
          'src',
          `dist/${env}`
        ])
      );
    }
  }
}

async function release({ packages, type }) {
  need(packages, 'Please specify at least one package.');
  need(type, 'Please specify a release type (or version number).');
  await exec('bolt', ['build']);
  const ws = await getWorkspaces();
  for (const pkg of packages.split(',')) {
    const name = pkg.trim();
    const w = ws.filter(w => w.name === name)[0];
    if (!w) continue;
    const cwd = w.dir;
    await exec('npm', ['--no-git-tag-version', 'version', type], { cwd });
    const ver = require(path.join(cwd, 'package.json')).version;
    const tag = `${name}-${ver}`;
    await exec('git', ['commit', '-am', tag], { cwd });
    await exec('git', ['tag', '-a', tag, '-m', tag], { cwd });
    await exec('npm', ['publish'], { cwd });
  }
  await exec('git', ['push']);
}

module.exports = {
  babel,
  release
};
<commit_msg>Ensure tags are pushed after release.<commit_after>const { getWorkspaces } = require('bolt');
const execa = require('execa');
const fs = require('fs-extra');
const path = require('path');

function exec(...args) {
  return execa(...args)
    .then(r => console.log(r.stdout))
    .catch(console.log);
}

function need(val, msg) {
  if (!val) {
    throw new Error(msg);
  }
}

async function babel({ envs }) {
  need(envs, 'Please specify at least one environment.');
  for (const w of await getWorkspaces()) {
    for (const env of envs.split(',')) {
      if ((w.config.files || []).indexOf(`dist/${env}`) === -1) continue;
      const src = path.join(w.dir, 'src');
      const dst = path.join(w.dir, 'dist', env);
      exec('babel', [src, '--out-dir', dst], {
        env: { BABEL_ENV: env }
      }).then(() =>
        exec('flow-copy-source', [
          '-i',
          '**/__tests__/**',
          'src',
          `dist/${env}`
        ])
      );
    }
  }
}

async function release({ packages, type }) {
  need(packages, 'Please specify at least one package.');
  need(type, 'Please specify a release type (or version number).');
  await exec('bolt', ['build']);
  const ws = await getWorkspaces();
  for (const pkg of packages.split(',')) {
    const name = pkg.trim();
    const w = ws.filter(w => w.name === name)[0];
    if (!w) continue;
    const cwd = w.dir;
    await exec('npm', ['--no-git-tag-version', 'version', type], { cwd });
    const ver = require(path.join(cwd, 'package.json')).version;
    const tag = `${name}-${ver}`;
    await exec('git', ['commit', '-am', tag], { cwd });
    await exec('git', ['tag', '-a', tag, '-m', tag], { cwd });
    await exec('npm', ['publish'], { cwd });
  }
  await exec('git', ['push', '--follow-tags']);
}

module.exports = {
  babel,
  release
};
<|endoftext|>