<commit_before>// @flow

import Icon from '@conveyal/woonerf/components/icon'
import React, { Component } from 'react'
import {
  Button,
  ButtonToolbar,
  Panel,
  Label
} from 'react-bootstrap'

import * as deploymentActions from '../actions/deployments'
import { formatTimestamp } from '../../common/util/date-time'
import { getActiveInstanceCount, getServerForId } from '../util/deployment'
import DeploymentPreviewButton from './DeploymentPreviewButton'
import EC2InstanceCard from '../../common/components/EC2InstanceCard'

import type { Deployment, DeploySummary, EC2InstanceSummary, OtpServer, ServerJob } from '../../types'

type Props = {
  deployJobs: Array<ServerJob>,
  deployment: Deployment,
  downloadBuildArtifact: typeof deploymentActions.downloadBuildArtifact,
  fetchDeployment: typeof deploymentActions.fetchDeployment,
  server: ?OtpServer,
  terminateEC2InstanceForDeployment: typeof deploymentActions.terminateEC2InstanceForDeployment
}

type State = {
  activeSummaryIndex: number
}

export default class CurrentDeploymentPanel extends Component<Props, State> {
  state = {
    activeSummaryIndex: 0
  }

  componentWillReceiveProps (nextProps: Props) {
    // If status message changes while deployment job is in progress, re-fetch
    // deployment to update EC2 instance status.
    const oldDeployJob = this._getDeployJob()
    const newDeployJob = this._getDeployJob(nextProps)
    if (newDeployJob && oldDeployJob) {
      if (newDeployJob.status.message !== oldDeployJob.status.message) {
        this._fetchDeploymentEC2Instances()
      }
    }
  }

  _getDeployJob = (props: Props = this.props) => props.deployJobs.find(job => job.type === 'DEPLOY_TO_OTP')

  _fetchDeploymentEC2Instances = () => this.props.fetchDeployment(this.props.deployment.id)

  _getServer = () => {
    if (this.props.server) return this.props.server
    const deployJob = this._getDeployJob()
    const serverId = deployJob && deployJob.serverId
    return getServerForId(serverId, this.props.deployment.project)
  }

  handleDecrementSummary = () => {
    const {activeSummaryIndex} = this.state
    if (activeSummaryIndex > 0) this.setState({activeSummaryIndex: activeSummaryIndex - 1})
  }

  handleIncrementSummary = () => {
    const {deployJobSummaries} = this.props.deployment
    const {activeSummaryIndex} = this.state
    if (activeSummaryIndex < deployJobSummaries.length - 1) this.setState({activeSummaryIndex: activeSummaryIndex + 1})
  }

  _onClickDownloadUserData = (instance: EC2InstanceSummary) => {
    const {deployment, downloadBuildArtifact} = this.props
    const {instanceId, jobId} = instance
    downloadBuildArtifact(deployment, `${instanceId}.log`, jobId)
  }

  render () {
    const { deployJobs, deployment } = this.props
    const { activeSummaryIndex } = this.state
    const deployJob = this._getDeployJob()
    const server = this._getServer()
    const ec2Info = server && server.ec2Info
    const hasPreviouslyDeployed = deployment.deployJobSummaries.length > 0
    return (
      <Panel header={
<Icon type='server' /> Current deployment
}>

          Deployment in progress...

            <Icon type='spinner' className='fa-pulse' />{' '}
            {deployJob.status.message}

        {hasPreviouslyDeployed &&

            <Button
              disabled={activeSummaryIndex === 0}
              bsSize='xsmall'
              onClick={this.handleDecrementSummary}>
              <Icon type='chevron-left' /> More recent
            </Button>

              {activeSummaryIndex  === 0 ? 'Latest' : `${activeSummaryIndex + 1} / ${deployment.deployJobSummaries.length}`}

            <Button
              disabled={activeSummaryIndex === deployment.deployJobSummaries.length - 1}
              bsSize='xsmall'
              onClick={this.handleIncrementSummary}>
              <Icon type='chevron-right' /> Older
            </Button>

        }
        {hasPreviouslyDeployed
          ? <DeployJobSummary
            summary={deployment.deployJobSummaries[activeSummaryIndex]}
            downloadBuildArtifact={this.props.downloadBuildArtifact}
            server={server}
            deployment={deployment} />
          : 'No current deployment found'
        }
        {ec2Info || deployment.ec2Instances.length > 0
          ?

              EC2 instances ({getActiveInstanceCount(deployment.ec2Instances)} active)
              <Button
                bsSize='xsmall'
                className='pull-right'
                onClick={this._fetchDeploymentEC2Instances}>
                <Icon type='refresh' />
              </Button>

            {deployment.ec2Instances.length > 0
              ?

                {deployment.ec2Instances.map(instance => {
                  return (
                    <EC2InstanceCard
                      key={instance.instanceId}
                      onClickDownloadUserData={this._onClickDownloadUserData}
                      job={deployJobs.find(job => job.instanceId === instance.instanceId)}
                      instance={instance}
                      terminateEC2InstanceForDeployment={this.props.terminateEC2InstanceForDeployment} />
                  )
                })}

              : null
            }

          : null
        }
      </Panel>
    )
  }
}

class DeployJobSummary extends Component<{
  downloadBuildArtifact: typeof deploymentActions.downloadBuildArtifact,
  summary: ?DeploySummary,
  server: ?OtpServer,
  deployment: Deployment
}> {
  _getLabelForServer = (server: ?OtpServer, deployment: Deployment) => {
    return (

        {server
          ? server.name
          : deployment.deployedTo
        }

    )
  }

  _getJobId = () => this.props.summary ? this.props.summary.jobId : undefined

  _onClickDownloadBundle = () => {
    this.props.downloadBuildArtifact(this.props.deployment, 'bundle.zip', this._getJobId())
  }

  _onClickDownloadLogs = () => {
    this.props.downloadBuildArtifact(this.props.deployment, 'otp-build.log', this._getJobId())
  }

  _onClickDownloadGraph = () => {
    this.props.downloadBuildArtifact(this.props.deployment, 'Graph.obj', this._getJobId())
  }

  render () {
    const {deployment, server, summary} = this.props
    const serverLabel = this._getLabelForServer(server, deployment)
    let deploymentType = 'unknown'
    if (server) {
      deploymentType = server.ec2Info
        ? 'ELB'
        : server.internalUrl
          ? 'Build graph over wire'
          : server.s3Bucket
            ? 'S3 only'
            : 'unknown'
    }
    if (!summary) {
      throw new Error('Could not find latest deploy job summary!')
    }
    return (

            Deployed to: {serverLabel}

Status: {summary.status.error ? 'Failure' : 'Success'}

Type: {deploymentType}

Deploy time: {formatTimestamp(summary.finishTime)}

          {summary
            ?
otp version: {summary.otpVersion}

            : null
          }

        <ButtonToolbar>
          <Button
            bsSize='xsmall'
            onClick={this._onClickDownloadGraph}>
            <Icon type='ellipsis-h' /> Graph.obj
          </Button>
          <Button
            bsSize='xsmall'
            onClick={this._onClickDownloadLogs}>
            <Icon type='file-text-o' /> Build log
          </Button>
          <Button
            bsSize='xsmall'
            onClick={this._onClickDownloadBundle}>
            <Icon type='file-zip-o' /> Bundle
          </Button>
        </ButtonToolbar>

          <DeploymentPreviewButton block deployment={deployment} />

    )
  }
}
<commit_msg>refactor(deploy): fix undefined deployJob bug<commit_after>// @flow

import Icon from '@conveyal/woonerf/components/icon'
import React, { Component } from 'react'
import {
  Button,
  ButtonToolbar,
  Panel,
  Label
} from 'react-bootstrap'

import * as deploymentActions from '../actions/deployments'
import { formatTimestamp } from '../../common/util/date-time'
import { getActiveInstanceCount, getServerForId } from '../util/deployment'
import DeploymentPreviewButton from './DeploymentPreviewButton'
import EC2InstanceCard from '../../common/components/EC2InstanceCard'

import type { Deployment, DeploySummary, EC2InstanceSummary, OtpServer, ServerJob } from '../../types'

type Props = {
  deployJobs: Array<ServerJob>,
  deployment: Deployment,
  downloadBuildArtifact: typeof deploymentActions.downloadBuildArtifact,
  fetchDeployment: typeof deploymentActions.fetchDeployment,
  server: ?OtpServer,
  terminateEC2InstanceForDeployment: typeof deploymentActions.terminateEC2InstanceForDeployment
}

type State = {
  activeSummaryIndex: number
}

export default class CurrentDeploymentPanel extends Component<Props, State> {
  state = {
    activeSummaryIndex: 0
  }

  componentWillReceiveProps (nextProps: Props) {
    // If status message changes while deployment job is in progress, re-fetch
    // deployment to update EC2 instance status.
    const oldDeployJob = this._getDeployJob()
    const newDeployJob = this._getDeployJob(nextProps)
    if (newDeployJob && oldDeployJob) {
      if (newDeployJob.status.message !== oldDeployJob.status.message) {
        this._fetchDeploymentEC2Instances()
      }
    }
  }

  _getDeployJob = (props: Props = this.props) => props.deployJobs.find(job => job.type === 'DEPLOY_TO_OTP')

  _fetchDeploymentEC2Instances = () => this.props.fetchDeployment(this.props.deployment.id)

  _getServer = () => {
    if (this.props.server) return this.props.server
    const deployJob = this._getDeployJob()
    const serverId = deployJob && deployJob.serverId
    return getServerForId(serverId, this.props.deployment.project)
  }

  handleDecrementSummary = () => {
    const {activeSummaryIndex} = this.state
    if (activeSummaryIndex > 0) this.setState({activeSummaryIndex: activeSummaryIndex - 1})
  }

  handleIncrementSummary = () => {
    const {deployJobSummaries} = this.props.deployment
    const {activeSummaryIndex} = this.state
    if (activeSummaryIndex < deployJobSummaries.length - 1) this.setState({activeSummaryIndex: activeSummaryIndex + 1})
  }

  _onClickDownloadUserData = (instance: EC2InstanceSummary) => {
    const {deployment, downloadBuildArtifact} = this.props
    const {instanceId, jobId} = instance
    downloadBuildArtifact(deployment, `${instanceId}.log`, jobId)
  }

  render () {
    const { deployJobs, deployment } = this.props
    const { activeSummaryIndex } = this.state
    const deployJob = this._getDeployJob()
    const server = this._getServer()
    const ec2Info = server && server.ec2Info
    const hasPreviouslyDeployed = deployment.deployJobSummaries.length > 0
    return (
      <Panel header={
<Icon type='server' /> Current deployment
}>
        {deployJob &&

            Deployment in progress...

              <Icon type='spinner' className='fa-pulse' />{' '}
              {deployJob.status.message}

        }
        {hasPreviouslyDeployed &&

            <Button
              disabled={activeSummaryIndex === 0}
              bsSize='xsmall'
              onClick={this.handleDecrementSummary}>
              <Icon type='chevron-left' /> More recent
            </Button>

              {activeSummaryIndex  === 0 ? 'Latest' : `${activeSummaryIndex + 1} / ${deployment.deployJobSummaries.length}`}

            <Button
              disabled={activeSummaryIndex === deployment.deployJobSummaries.length - 1}
              bsSize='xsmall'
              onClick={this.handleIncrementSummary}>
              <Icon type='chevron-right' /> Older
            </Button>

        }
        {hasPreviouslyDeployed
          ? <DeployJobSummary
            summary={deployment.deployJobSummaries[activeSummaryIndex]}
            downloadBuildArtifact={this.props.downloadBuildArtifact}
            server={server}
            deployment={deployment} />
          : 'No current deployment found'
        }
        {ec2Info || deployment.ec2Instances.length > 0
          ?

              EC2 instances ({getActiveInstanceCount(deployment.ec2Instances)} active)
              <Button
                bsSize='xsmall'
                className='pull-right'
                onClick={this._fetchDeploymentEC2Instances}>
                <Icon type='refresh' />
              </Button>

            {deployment.ec2Instances.length > 0
              ?

                {deployment.ec2Instances.map(instance => {
                  return (
                    <EC2InstanceCard
                      key={instance.instanceId}
                      onClickDownloadUserData={this._onClickDownloadUserData}
                      job={deployJobs.find(job => job.instanceId === instance.instanceId)}
                      instance={instance}
                      terminateEC2InstanceForDeployment={this.props.terminateEC2InstanceForDeployment} />
                  )
                })}

              : null
            }

          : null
        }
      </Panel>
    )
  }
}

class DeployJobSummary extends Component<{
  downloadBuildArtifact: typeof deploymentActions.downloadBuildArtifact,
  summary: ?DeploySummary,
  server: ?OtpServer,
  deployment: Deployment
}> {
  _getLabelForServer = (server: ?OtpServer, deployment: Deployment) => {
    return (

        {server
          ? server.name
          : deployment.deployedTo
        }

    )
  }

  _getJobId = () => this.props.summary ? this.props.summary.jobId : undefined

  _onClickDownloadBundle = () => {
    this.props.downloadBuildArtifact(this.props.deployment, 'bundle.zip', this._getJobId())
  }

  _onClickDownloadLogs = () => {
    this.props.downloadBuildArtifact(this.props.deployment, 'otp-build.log', this._getJobId())
  }

  _onClickDownloadGraph = () => {
    this.props.downloadBuildArtifact(this.props.deployment, 'Graph.obj', this._getJobId())
  }

  render () {
    const {deployment, server, summary} = this.props
    const serverLabel = this._getLabelForServer(server, deployment)
    let deploymentType = 'unknown'
    if (server) {
      deploymentType = server.ec2Info
        ? 'ELB'
        : server.internalUrl
          ? 'Build graph over wire'
          : server.s3Bucket
            ? 'S3 only'
            : 'unknown'
    }
    if (!summary) {
      throw new Error('Could not find latest deploy job summary!')
    }
    return (

            Deployed to: {serverLabel}

Status: {summary.status.error ? 'Failure' : 'Success'}

Type: {deploymentType}

Deploy time: {formatTimestamp(summary.finishTime)}

          {summary
            ?
otp version: {summary.otpVersion}

            : null
          }

        <ButtonToolbar>
          <Button
            bsSize='xsmall'
            onClick={this._onClickDownloadGraph}>
            <Icon type='ellipsis-h' /> Graph.obj
          </Button>
          <Button
            bsSize='xsmall'
            onClick={this._onClickDownloadLogs}>
            <Icon type='file-text-o' /> Build log
          </Button>
          <Button
            bsSize='xsmall'
            onClick={this._onClickDownloadBundle}>
            <Icon type='file-zip-o' /> Bundle
          </Button>
        </ButtonToolbar>

          <DeploymentPreviewButton block deployment={deployment} />

    )
  }
}
<|endoftext|>


<commit_before>// Copyright 2014-2020, University of Colorado Boulder

/**
 * Shows a readout of the elapsed time, with play and pause buttons.  By default there are no units (which could be used
 * if all of a simulations time units are in 'seconds'), or you can specify a selection of units to choose from.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Anton Ulyanov (Mlearner)
 */

import StringProperty from '../../axon/js/StringProperty.js';
import Bounds2 from '../../dot/js/Bounds2.js';
import Utils from '../../dot/js/Utils.js';
import InstanceRegistry from '../../phet-core/js/documentation/InstanceRegistry.js';
import inherit from '../../phet-core/js/inherit.js';
import merge from '../../phet-core/js/merge.js';
import StringUtils from '../../phetcommon/js/util/StringUtils.js';
import DragListener from '../../scenery/js/listeners/DragListener.js';
import Circle from '../../scenery/js/nodes/Circle.js';
import HBox from '../../scenery/js/nodes/HBox.js';
import Node from '../../scenery/js/nodes/Node.js';
import Path from '../../scenery/js/nodes/Path.js';
import VBox from '../../scenery/js/nodes/VBox.js';
import BooleanRectangularToggleButton from '../../sun/js/buttons/BooleanRectangularToggleButton.js';
import RectangularPushButton from '../../sun/js/buttons/RectangularPushButton.js';
import Tandem from '../../tandem/js/Tandem.js';
import DragBoundsProperty from './DragBoundsProperty.js';
import NumberDisplay from './NumberDisplay.js';
import PauseIconShape from './PauseIconShape.js';
import PlayIconShape from './PlayIconShape.js';
import sceneryPhet from './sceneryPhet.js';
import sceneryPhetStrings from './sceneryPhetStrings.js';
import ShadedRectangle from './ShadedRectangle.js';
import Stopwatch from './Stopwatch.js';
import UTurnArrowShape from './UTurnArrowShape.js';

/**
 * @param {Stopwatch} stopwatch
 * @param {Object} [options]
 */
function StopwatchNode( stopwatch, options ) {
  assert && assert( stopwatch instanceof Stopwatch, `invalid stopwatch: ${stopwatch}` );

  options = merge( {

    cursor: 'pointer',
    iconHeight: 10,
    numberDisplayRange: Stopwatch.ZERO_TO_ALMOST_SIXTY, // Just for sizing the display, sized for 59:59.99 (mm:ss) or 3599.99 (decimal)
    iconFill: 'black',
    iconLineWidth: 1,
    backgroundBaseColor: 'rgb( 80, 130, 230 )',
    buttonBaseColor: '#DFE0E1',
    xSpacing: 6, // horizontal space between the buttons
    ySpacing: 6, // vertical space between readout and buttons
    xMargin: 8,
    yMargin: 8,

    // options propagated to the NumberDisplay
    numberDisplayOptions: {
      numberFormatter: StopwatchNode.richNumberFormatter,
      useRichText: true,
      align: 'right',
      cornerRadius: 4,
      xMargin: 4,
      yMargin: 2,
      pickable: false // allow dragging by the number display
    },

    visibleBoundsProperty: null, // {Property.<Bounds2>|null} if provided, the node is draggable within the bounds

    // Tandem is required to make sure the buttons are instrumented
    tandem: Tandem.REQUIRED,

    // options propagated to the DragListener.
    dragListenerOptions: null
  }, options );
  assert && assert( !options.hasOwnProperty( 'maxValue' ), 'options.maxValue no longer supported' );

  assert && assert( options.xSpacing >= 0, 'Buttons cannot overlap' );
  assert && assert( options.ySpacing >= 0, 'Buttons cannot overlap the readout' );

  // Create the StopwatchReadoutNode.
  this.numberDisplay = new NumberDisplay( stopwatch.timeProperty, options.numberDisplayRange, options.numberDisplayOptions );

  // Buttons ----------------------------------------------------------------------------

  const resetPath = new Path( new UTurnArrowShape( options.iconHeight ), {
    fill: options.iconFill
  } );

  const playIconHeight = resetPath.height;
  const playIconWidth = 0.8 * playIconHeight;
  const playPath = new Path( new PlayIconShape( playIconWidth, playIconHeight ), {
    fill: options.iconFill
  } );

  const pausePath = new Path( new PauseIconShape( 0.75 * playIconWidth, playIconHeight ), {
    fill: options.iconFill
  } );

  const playPauseButton = new BooleanRectangularToggleButton( pausePath, playPath, stopwatch.isRunningProperty, {
    baseColor: options.buttonBaseColor,
    tandem: options.tandem.createTandem( 'playPauseButton' )
  } );

  const resetButton = new RectangularPushButton( {
    listener: function resetTimer() {
      stopwatch.isRunningProperty.set( false );
      stopwatch.timeProperty.set( 0 );
    },
    content: resetPath,
    baseColor: options.buttonBaseColor,
    tandem: options.tandem.createTandem( 'resetButton' )
  } );

  const contents = new VBox( {
    spacing: options.ySpacing,
    children: [
      this.numberDisplay,
      new HBox( {
        spacing: options.xSpacing,
        children: [ resetButton, playPauseButton ]
      } )
    ]
  } );

  // Background panel ----------------------------------------------------------------------------

  const backgroundNode = new ShadedRectangle( new Bounds2( 0, 0,
    contents.width + 2 * options.xMargin, contents.height + 2 * options.yMargin ), {
    baseColor: options.backgroundBaseColor
  } );
  contents.center = backgroundNode.center;

  assert && assert( !options.children, 'StopwatchNode sets children' );
  options.children = [ backgroundNode, contents ];

  Node.call( this, options );

  // @public (read-only) - Target for drag listeners
  this.dragTarget = backgroundNode;

  // Disable the reset button when time is zero, and enable the play/pause button when not at the max time
  const timeListener = time => {
    resetButton.enabled = time > 0;
    playPauseButton.enabled = time < stopwatch.timeProperty.range.max;
  };
  stopwatch.timeProperty.link( timeListener );

  // Put a red dot at the origin, for debugging layout.
  if ( phet.chipper.queryParameters.dev ) {
    this.addChild( new Circle( 3, { fill: 'red' } ) );
  }

  const stopwatchVisibleListener = visible => {
    this.visible = visible;
    if ( visible ) {
      this.moveToFront();
    }
    else {

      // interrupt user interactions when the stopwatch is made invisible
      this.interruptSubtreeInput();
    }
  };
  stopwatch.isVisibleProperty.link( stopwatchVisibleListener );

  // Move to the stopwatch's position
  const stopwatchPositionListener = position => this.setTranslation( position );
  stopwatch.positionProperty.link( stopwatchPositionListener );

  // @public (read-only) {DragListener|null} -- reassigned below, if draggable.  Can be used for forwarding press
  // events when dragging out of a toolbox.
  this.dragListener = null;

  let dragBoundsProperty = null;

  if ( options.visibleBoundsProperty ) {

    // drag bounds, adjusted to keep this entire Node inside visible bounds
    dragBoundsProperty = new DragBoundsProperty( this, options.visibleBoundsProperty );

    // interrupt user interactions when the visible bounds changes, such as a device orientation change or window resize
    options.visibleBoundsProperty.link( () => this.interruptSubtreeInput() );

    // If the stopwatch is outside the drag bounds, move it inside.
    dragBoundsProperty.link( dragBounds => {
      if ( !dragBounds.containsPoint( stopwatch.positionProperty ) ) {
        stopwatch.positionProperty.value = dragBounds.closestPointTo( stopwatch.positionProperty.value );
      }
    } );

    // dragging, added to background so that other UI components get input events on touch devices
    const dragListenerOptions = merge( {
      targetNode: this,
      positionProperty: stopwatch.positionProperty,
      dragBoundsProperty: dragBoundsProperty,
      tandem: options.tandem.createTandem( 'dragListener' )
    }, options.dragListenerOptions );

    // The StopwatchNode always calls moveToFront on drag start
    const startOption = dragListenerOptions.start;
    dragListenerOptions.start = () => {
      this.moveToFront();
      startOption && startOption();
    };

    this.dragListener = new DragListener( dragListenerOptions );
    this.dragTarget.addInputListener( this.dragListener );
  }

  this.addLinkedElement( stopwatch, {
    tandem: options.tandem.createTandem( 'stopwatch' )
  } );

  // @private
  this.disposeStopwatchNode = function() {
    stopwatch.isVisibleProperty.unlink( stopwatchVisibleListener );
    stopwatch.timeProperty.unlink( timeListener );
    stopwatch.positionProperty.unlink( stopwatchPositionListener );

    this.numberDisplay.dispose();
    resetButton.dispose();
    playPauseButton.dispose();

    if ( this.dragListener ) {
      this.dragTarget.removeInputListener( this.dragListener );
      this.dragListener.dispose();
    }

    dragBoundsProperty && dragBoundsProperty.dispose();
  };

  // support for binder documentation, stripped out in builds and only runs when ?binder is specified
  assert && phet.chipper.queryParameters.binder && InstanceRegistry.registerDataURL( 'scenery-phet', 'StopwatchNode', this );
}

sceneryPhet.register( 'StopwatchNode', StopwatchNode );

inherit( Node, StopwatchNode, {

  // @public - redraw the text when something other than the numberProperty changes (such as units, formatter, etc).
  redrawNumberDisplay() {
    this.numberDisplay.recomputeText();
  },

  /**
   * Release resources when no longer be used.
   * @public
   * @override
   */
  dispose: function() {
    this.disposeStopwatchNode();
    Node.prototype.dispose.call( this );
  }
} );

// the full-sized minutes and seconds string
const toMinutesAndSeconds = time => {

  // Round to the nearest centi-part (if time is in seconds, this would be centiseconds), (compatible with timeToSmallString).
  // see https://github.com/phetsims/masses-and-springs/issues/156
  time = Utils.roundSymmetric( time * 100 ) / 100;

  // When showing units, don't show the "00:" prefix, see https://github.com/phetsims/scenery-phet/issues/378
  const timeInSeconds = time;

  // If no units are provided, then we assume the time is in seconds, and should be shown in mm:ss.cs
  let minutes = Math.floor( timeInSeconds / 60 );
  let seconds = Math.floor( timeInSeconds ) % 60;

  if ( seconds < 10 ) {
    seconds = '0' + seconds;
  }
  if ( minutes < 10 ) {
    minutes = '0' + minutes;
  }
  return minutes + ':' + seconds;
};

// the smaller hundredths-of-a-second string
const toCentiseconds = time => {

  // Round to the nearest centisecond (compatible with timeToSmallString).
  // see https://github.com/phetsims/masses-and-springs/issues/156
  time = Utils.roundSymmetric( time * 100 ) / 100;

  // Rounding after mod, in case there is floating-point error
  let centitime = Utils.roundSymmetric( time % 1 * 100 );
  if ( centitime < 10 ) {
    centitime = '0' + centitime;
  }
  return '.' + centitime;
};

// @public - for NumberDisplay, shows 12:34.56
StopwatchNode.numberFormatter = x => {
  const minutesAndSeconds = toMinutesAndSeconds( x );
  const centiseconds = toCentiseconds( x );
  return minutesAndSeconds + centiseconds;
};

// Try for a monospace font so that the numbers don't change alignment.  Fallback to Arial as determined in
// https://github.com/phetsims/wave-interference/issues/239
const numberFontFamily = 'font-family: Lucida Console,Arial,Bitstream Vera Sans Mono,monospace;';

// @public - for NumberDisplay, shows 12:34.56, but the ".56" is smaller
StopwatchNode.richNumberFormatter = x => {
  const minutesAndSeconds = toMinutesAndSeconds( x );
  const centiseconds = toCentiseconds( x );

  return `<span style="font-size: 20px;${numberFontFamily}">${minutesAndSeconds}</span><span style="font-size: 14px;${numberFontFamily}">${centiseconds}</span>`;
};

// @public - for NumberDisplay, more customizable
StopwatchNode.getRichNumberFormatter = options => {
  options = merge( {
    showAsDecimal: false,
    bigNumberFont: 20,
    smallNumberFont: 14,
    unitsFont: 14,
    unitsProperty: new StringProperty( 'units' ),

    // Units cannot be baked into the i18n string because they can change independently
    valueUnitsPattern: sceneryPhetStrings.stopwatchValueUnitsPattern
  }, options );

  return x => {
    const minutesAndSeconds = options.showAsDecimal ? Math.floor( x ) : toMinutesAndSeconds( x );
    const centiseconds = toCentiseconds( x );

    return StringUtils.fillIn( options.valueUnitsPattern, {
      value: `<span style="font-size: ${options.bigNumberFont}px;${numberFontFamily}">${minutesAndSeconds}</span><span style="font-size: ${options.smallNumberFont}px;${numberFontFamily}">${centiseconds}</span>`,
      units: `<span style="font-size: ${options.unitsFont}px;${numberFontFamily}">${options.unitsProperty.value}</span>`
    } );
  };
};

export default StopwatchNode;<commit_msg>Specify Trebuchet MS and Lucida Grande, see https://github.com/phetsims/scenery-phet/issues/616<commit_after>// Copyright 2014-2020, University of Colorado Boulder

/**
 * Shows a readout of the elapsed time, with play and pause buttons.  By default there are no units (which could be used
 * if all of a simulations time units are in 'seconds'), or you can specify a selection of units to choose from.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Anton Ulyanov (Mlearner)
 */

import StringProperty from '../../axon/js/StringProperty.js';
import Bounds2 from '../../dot/js/Bounds2.js';
import Utils from '../../dot/js/Utils.js';
import InstanceRegistry from '../../phet-core/js/documentation/InstanceRegistry.js';
import inherit from '../../phet-core/js/inherit.js';
import merge from '../../phet-core/js/merge.js';
import StringUtils from '../../phetcommon/js/util/StringUtils.js';
import DragListener from '../../scenery/js/listeners/DragListener.js';
import Circle from '../../scenery/js/nodes/Circle.js';
import HBox from '../../scenery/js/nodes/HBox.js';
import Node from '../../scenery/js/nodes/Node.js';
import Path from '../../scenery/js/nodes/Path.js';
import VBox from '../../scenery/js/nodes/VBox.js';
import BooleanRectangularToggleButton from '../../sun/js/buttons/BooleanRectangularToggleButton.js';
import RectangularPushButton from '../../sun/js/buttons/RectangularPushButton.js';
import Tandem from '../../tandem/js/Tandem.js';
import DragBoundsProperty from './DragBoundsProperty.js';
import NumberDisplay from './NumberDisplay.js';
import PauseIconShape from './PauseIconShape.js';
import PlayIconShape from './PlayIconShape.js';
import sceneryPhet from './sceneryPhet.js';
import sceneryPhetStrings from './sceneryPhetStrings.js';
import ShadedRectangle from './ShadedRectangle.js';
import Stopwatch from './Stopwatch.js';
import UTurnArrowShape from './UTurnArrowShape.js';

/**
 * @param {Stopwatch} stopwatch
 * @param {Object} [options]
 */
function StopwatchNode( stopwatch, options ) {
  assert && assert( stopwatch instanceof Stopwatch, `invalid stopwatch: ${stopwatch}` );

  options = merge( {

    cursor: 'pointer',
    iconHeight: 10,
    numberDisplayRange: Stopwatch.ZERO_TO_ALMOST_SIXTY, // Just for sizing the display, sized for 59:59.99 (mm:ss) or 3599.99 (decimal)
    iconFill: 'black',
    iconLineWidth: 1,
    backgroundBaseColor: 'rgb( 80, 130, 230 )',
    buttonBaseColor: '#DFE0E1',
    xSpacing: 6, // horizontal space between the buttons
    ySpacing: 6, // vertical space between readout and buttons
    xMargin: 8,
    yMargin: 8,

    // options propagated to the NumberDisplay
    numberDisplayOptions: {
      numberFormatter: StopwatchNode.richNumberFormatter,
      useRichText: true,
      align: 'right',
      cornerRadius: 4,
      xMargin: 4,
      yMargin: 2,
      pickable: false // allow dragging by the number display
    },

    visibleBoundsProperty: null, // {Property.<Bounds2>|null} if provided, the node is draggable within the bounds

    // Tandem is required to make sure the buttons are instrumented
    tandem: Tandem.REQUIRED,

    // options propagated to the DragListener.
    dragListenerOptions: null
  }, options );
  assert && assert( !options.hasOwnProperty( 'maxValue' ), 'options.maxValue no longer supported' );

  assert && assert( options.xSpacing >= 0, 'Buttons cannot overlap' );
  assert && assert( options.ySpacing >= 0, 'Buttons cannot overlap the readout' );

  // Create the StopwatchReadoutNode.
  this.numberDisplay = new NumberDisplay( stopwatch.timeProperty, options.numberDisplayRange, options.numberDisplayOptions );

  // Buttons ----------------------------------------------------------------------------

  const resetPath = new Path( new UTurnArrowShape( options.iconHeight ), {
    fill: options.iconFill
  } );

  const playIconHeight = resetPath.height;
  const playIconWidth = 0.8 * playIconHeight;
  const playPath = new Path( new PlayIconShape( playIconWidth, playIconHeight ), {
    fill: options.iconFill
  } );

  const pausePath = new Path( new PauseIconShape( 0.75 * playIconWidth, playIconHeight ), {
    fill: options.iconFill
  } );

  const playPauseButton = new BooleanRectangularToggleButton( pausePath, playPath, stopwatch.isRunningProperty, {
    baseColor: options.buttonBaseColor,
    tandem: options.tandem.createTandem( 'playPauseButton' )
  } );

  const resetButton = new RectangularPushButton( {
    listener: function resetTimer() {
      stopwatch.isRunningProperty.set( false );
      stopwatch.timeProperty.set( 0 );
    },
    content: resetPath,
    baseColor: options.buttonBaseColor,
    tandem: options.tandem.createTandem( 'resetButton' )
  } );

  const contents = new VBox( {
    spacing: options.ySpacing,
    children: [
      this.numberDisplay,
      new HBox( {
        spacing: options.xSpacing,
        children: [ resetButton, playPauseButton ]
      } )
    ]
  } );

  // Background panel ----------------------------------------------------------------------------

  const backgroundNode = new ShadedRectangle( new Bounds2( 0, 0,
    contents.width + 2 * options.xMargin, contents.height + 2 * options.yMargin ), {
    baseColor: options.backgroundBaseColor
  } );
  contents.center = backgroundNode.center;

  assert && assert( !options.children, 'StopwatchNode sets children' );
  options.children = [ backgroundNode, contents ];

  Node.call( this, options );

  // @public (read-only) - Target for drag listeners
  this.dragTarget = backgroundNode;

  // Disable the reset button when time is zero, and enable the play/pause button when not at the max time
  const timeListener = time => {
    resetButton.enabled = time > 0;
    playPauseButton.enabled = time < stopwatch.timeProperty.range.max;
  };
  stopwatch.timeProperty.link( timeListener );

  // Put a red dot at the origin, for debugging layout.
  if ( phet.chipper.queryParameters.dev ) {
    this.addChild( new Circle( 3, { fill: 'red' } ) );
  }

  const stopwatchVisibleListener = visible => {
    this.visible = visible;
    if ( visible ) {
      this.moveToFront();
    }
    else {

      // interrupt user interactions when the stopwatch is made invisible
      this.interruptSubtreeInput();
    }
  };
  stopwatch.isVisibleProperty.link( stopwatchVisibleListener );

  // Move to the stopwatch's position
  const stopwatchPositionListener = position => this.setTranslation( position );
  stopwatch.positionProperty.link( stopwatchPositionListener );

  // @public (read-only) {DragListener|null} -- reassigned below, if draggable.  Can be used for forwarding press
  // events when dragging out of a toolbox.
  this.dragListener = null;

  let dragBoundsProperty = null;

  if ( options.visibleBoundsProperty ) {

    // drag bounds, adjusted to keep this entire Node inside visible bounds
    dragBoundsProperty = new DragBoundsProperty( this, options.visibleBoundsProperty );

    // interrupt user interactions when the visible bounds changes, such as a device orientation change or window resize
    options.visibleBoundsProperty.link( () => this.interruptSubtreeInput() );

    // If the stopwatch is outside the drag bounds, move it inside.
    dragBoundsProperty.link( dragBounds => {
      if ( !dragBounds.containsPoint( stopwatch.positionProperty ) ) {
        stopwatch.positionProperty.value = dragBounds.closestPointTo( stopwatch.positionProperty.value );
      }
    } );

    // dragging, added to background so that other UI components get input events on touch devices
    const dragListenerOptions = merge( {
      targetNode: this,
      positionProperty: stopwatch.positionProperty,
      dragBoundsProperty: dragBoundsProperty,
      tandem: options.tandem.createTandem( 'dragListener' )
    }, options.dragListenerOptions );

    // The StopwatchNode always calls moveToFront on drag start
    const startOption = dragListenerOptions.start;
    dragListenerOptions.start = () => {
      this.moveToFront();
      startOption && startOption();
    };

    this.dragListener = new DragListener( dragListenerOptions );
    this.dragTarget.addInputListener( this.dragListener );
  }

  this.addLinkedElement( stopwatch, {
    tandem: options.tandem.createTandem( 'stopwatch' )
  } );

  // @private
  this.disposeStopwatchNode = function() {
    stopwatch.isVisibleProperty.unlink( stopwatchVisibleListener );
    stopwatch.timeProperty.unlink( timeListener );
    stopwatch.positionProperty.unlink( stopwatchPositionListener );

    this.numberDisplay.dispose();
    resetButton.dispose();
    playPauseButton.dispose();

    if ( this.dragListener ) {
      this.dragTarget.removeInputListener( this.dragListener );
      this.dragListener.dispose();
    }

    dragBoundsProperty && dragBoundsProperty.dispose();
  };

  // support for binder documentation, stripped out in builds and only runs when ?binder is specified
  assert && phet.chipper.queryParameters.binder && InstanceRegistry.registerDataURL( 'scenery-phet', 'StopwatchNode', this );
}

sceneryPhet.register( 'StopwatchNode', StopwatchNode );

inherit( Node, StopwatchNode, {

  // @public - redraw the text when something other than the numberProperty changes (such as units, formatter, etc).
  redrawNumberDisplay() {
    this.numberDisplay.recomputeText();
  },

  /**
   * Release resources when no longer be used.
   * @public
   * @override
   */
  dispose: function() {
    this.disposeStopwatchNode();
    Node.prototype.dispose.call( this );
  }
} );

// the full-sized minutes and seconds string
const toMinutesAndSeconds = time => {

  // Round to the nearest centi-part (if time is in seconds, this would be centiseconds), (compatible with timeToSmallString).
  // see https://github.com/phetsims/masses-and-springs/issues/156
  time = Utils.roundSymmetric( time * 100 ) / 100;

  // When showing units, don't show the "00:" prefix, see https://github.com/phetsims/scenery-phet/issues/378
  const timeInSeconds = time;

  // If no units are provided, then we assume the time is in seconds, and should be shown in mm:ss.cs
  let minutes = Math.floor( timeInSeconds / 60 );
  let seconds = Math.floor( timeInSeconds ) % 60;

  if ( seconds < 10 ) {
    seconds = '0' + seconds;
  }
  if ( minutes < 10 ) {
    minutes = '0' + minutes;
  }
  return minutes + ':' + seconds;
};

// the smaller hundredths-of-a-second string
const toCentiseconds = time => {

  // Round to the nearest centisecond (compatible with timeToSmallString).
  // see https://github.com/phetsims/masses-and-springs/issues/156
  time = Utils.roundSymmetric( time * 100 ) / 100;

  // Rounding after mod, in case there is floating-point error
  let centitime = Utils.roundSymmetric( time % 1 * 100 );
  if ( centitime < 10 ) {
    centitime = '0' + centitime;
  }
  return '.' + centitime;
};

// @public - for NumberDisplay, shows 12:34.56
StopwatchNode.numberFormatter = x => {
  const minutesAndSeconds = toMinutesAndSeconds( x );
  const centiseconds = toCentiseconds( x );
  return minutesAndSeconds + centiseconds;
};

// We used to use Lucida Console, Arial, but Arial has smaller number width for "11" and hence was causing jitter.
// Neither Trebuchet MS and Lucida Grande is a monospace font, but the digits all appear to be monospace.
// Use Trebuchet first, since it has broader cross-platform support.
// Another advantage of using a non-monospace font (that has monospace digits) is that the : and . symbols aren't as
// wide as the numerals.  @ariel-phet and @samreid tested this combination of families on Mac/Chrome and Windows/Chrome
// and it seemed to work nicely, with no jitter.
const numberFontFamily = 'font-family:Trebuchet MS,Lucida Grande,monospace;';

// @public - for NumberDisplay, shows 12:34.56, but the ".56" is smaller
StopwatchNode.richNumberFormatter = x => {
  const minutesAndSeconds = toMinutesAndSeconds( x );
  const centiseconds = toCentiseconds( x );

  return `<span style="font-size: 20px;${numberFontFamily}">${minutesAndSeconds}</span><span style="font-size: 14px;${numberFontFamily}">${centiseconds}</span>`;
};

// @public - for NumberDisplay, more customizable
StopwatchNode.getRichNumberFormatter = options => {
  options = merge( {
    showAsDecimal: false,
    bigNumberFont: 20,
    smallNumberFont: 14,
    unitsFont: 14,
    unitsProperty: new StringProperty( 'units' ),

    // Units cannot be baked into the i18n string because they can change independently
    valueUnitsPattern: sceneryPhetStrings.stopwatchValueUnitsPattern
  }, options );

  return x => {
    const minutesAndSeconds = options.showAsDecimal ? Math.floor( x ) : toMinutesAndSeconds( x );
    const centiseconds = toCentiseconds( x );

    return StringUtils.fillIn( options.valueUnitsPattern, {
      value: `<span style="font-size: ${options.bigNumberFont}px;${numberFontFamily}">${minutesAndSeconds}</span><span style="font-size: ${options.smallNumberFont}px;${numberFontFamily}">${centiseconds}</span>`,
      units: `<span style="font-size: ${options.unitsFont}px;${numberFontFamily}">${options.unitsProperty.value}</span>`
    } );
  };
};

export default StopwatchNode;<|endoftext|>


<commit_before>/**
* @license Apache-2.0
*
* Copyright (c) 2021 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';

/*
* When adding modules to the namespace, ensure that they are added in alphabetical order according to module name.
*/

// MODULES //

var setReadOnly = require( '@stdlib/utils/define-read-only-property' );

// MAIN //

/**
* Namespace.
*
* @namespace ns
*/
var ns = {};

/**
* @name arraylike2object
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/array/base/arraylike2object}
*/
setReadOnly( ns, 'arraylike2object', require( '@stdlib/array/base/arraylike2object' ) );

/**
* @name filled
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/array/base/filled}
*/
setReadOnly( ns, 'filled', require( '@stdlib/array/base/filled' ) );

/**
* @name filledBy
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/array/base/filled-by}
*/
setReadOnly( ns, 'filledBy', require( '@stdlib/array/base/filled-by' ) );

/**
* @name incrspace
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/array/base/incrspace}
*/
setReadOnly( ns, 'incrspace', require( '@stdlib/array/base/incrspace' ) );

/**
* @name linspace
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/array/base/linspace}
*/
setReadOnly( ns, 'linspace', require( '@stdlib/array/base/linspace' ) );

/**
* @name logspace
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/array/base/logspace}
*/
setReadOnly( ns, 'logspace', require( '@stdlib/array/base/logspace' ) );

/**
* @name ones
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/array/base/ones}
*/
setReadOnly( ns, 'ones', require( '@stdlib/array/base/ones' ) );

/**
* @name zeros
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/array/base/zeros}
*/
setReadOnly( ns, 'zeros', require( '@stdlib/array/base/zeros' ) );

// EXPORTS //

module.exports = ns;
<commit_msg>Update namespace<commit_after>/**
* @license Apache-2.0
*
* Copyright (c) 2021 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';

/*
* When adding modules to the namespace, ensure that they are added in alphabetical order according to module name.
*/

// MODULES //

var setReadOnly = require( '@stdlib/utils/define-read-only-property' );

// MAIN //

/**
* Namespace.
*
* @namespace ns
*/
var ns = {};

/**
* @name arraylike2object
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/array/base/arraylike2object}
*/
setReadOnly( ns, 'arraylike2object', require( '@stdlib/array/base/arraylike2object' ) );

/**
* @name copy
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/array/base/copy}
*/
setReadOnly( ns, 'copy', require( '@stdlib/array/base/copy' ) );

/**
* @name filled
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/array/base/filled}
*/
setReadOnly( ns, 'filled', require( '@stdlib/array/base/filled' ) );

/**
* @name filledBy
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/array/base/filled-by}
*/
setReadOnly( ns, 'filledBy', require( '@stdlib/array/base/filled-by' ) );

/**
* @name incrspace
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/array/base/incrspace}
*/
setReadOnly( ns, 'incrspace', require( '@stdlib/array/base/incrspace' ) );

/**
* @name linspace
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/array/base/linspace}
*/
setReadOnly( ns, 'linspace', require( '@stdlib/array/base/linspace' ) );

/**
* @name logspace
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/array/base/logspace}
*/
setReadOnly( ns, 'logspace', require( '@stdlib/array/base/logspace' ) );

/**
* @name ones
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/array/base/ones}
*/
setReadOnly( ns, 'ones', require( '@stdlib/array/base/ones' ) );

/**
* @name zeros
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/array/base/zeros}
*/
setReadOnly( ns, 'zeros', require( '@stdlib/array/base/zeros' ) );

// EXPORTS //

module.exports = ns;
<|endoftext|>


<commit_before>import {List, Map, fromJS} from 'immutable';
import {expect} from 'chai';

import reducer from '../js/reducer';

describe('reducer', () => {
  it('handles ADD_TIMER', () => {
    const initialState = List();
    const action = {
      type: 'ADD_TIMER',
      state: Map({
        name: 'My timer',
        count: 0
      })
    };
    const nextState = reducer(initialState, action);

    expect(nextState).to.equal(fromJS(
      [{name: 'My timer', count: 0}]
    ));

  });
});
<commit_msg>Now test timer is inactive<commit_after>import {List, Map, fromJS} from 'immutable';
import {expect} from 'chai';

import reducer from '../js/reducer';

describe('reducer', () => {
  it('handles ADD_TIMER', () => {
    const initialState = List();
    const action = {
      type: 'ADD_TIMER',
      state: Map({
        name: 'My timer',
        active: false,
        count: 0
      })
    };
    const nextState = reducer(initialState, action);

    expect(nextState).to.equal(fromJS(
      [{name: 'My timer', active: false, count: 0}]
    ));

  });
});
<|endoftext|>


<commit_before>import PromiseRouter from '../PromiseRouter';
import * as middleware from "../middlewares";
import { Parse } from "parse/node";

export class PushRouter extends PromiseRouter {

  mountRoutes() {
    this.route("POST", "/push", middleware.promiseEnforceMasterKeyAccess, PushRouter.handlePOST);
  }

  static handlePOST(req) {
    const pushController = req.config.pushController;
    if (!pushController) {
      throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED, 'Push controller is not set');
    }

    let where = PushRouter.getQueryCondition(req);
    pushController.sendPush(req.body, where, req.config, req.auth);
    return Promise.resolve({
      response: {
        'result': true
      }
    });
  }

  /**
   * Get query condition from the request body.
   * @param {Object} req A request object
   * @returns {Object} The query condition, the where field in a query api call
   */
  static getQueryCondition(req) {
    let body = req.body || {};
    let hasWhere = typeof body.where !== 'undefined';
    let hasChannels = typeof body.channels !== 'undefined';

    let where;
    if (hasWhere && hasChannels) {
      throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
        'Channels and query can not be set at the same time.');
    } else if (hasWhere) {
      where = body.where;
    } else if (hasChannels) {
      where = {
        "channels": {
          "$in": body.channels
        }
      }
    } else {
      throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
        'Channels and query should be set at least one.');
    }
    return where;
  }
}

export default PushRouter;
<commit_msg>Update error message<commit_after>import PromiseRouter   from '../PromiseRouter';
import * as middleware from "../middlewares";
import { Parse }       from "parse/node";

export class PushRouter extends PromiseRouter {

  mountRoutes() {
    this.route("POST", "/push", middleware.promiseEnforceMasterKeyAccess, PushRouter.handlePOST);
  }

  static handlePOST(req) {
    const pushController = req.config.pushController;
    if (!pushController) {
      throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED, 'Push controller is not set');
    }

    let where = PushRouter.getQueryCondition(req);
    pushController.sendPush(req.body, where, req.config, req.auth);
    return Promise.resolve({
      response: {
        'result': true
      }
    });
  }

  /**
   * Get query condition from the request body.
   * @param {Object} req A request object
   * @returns {Object} The query condition, the where field in a query api call
   */
  static getQueryCondition(req) {
    let body = req.body || {};
    let hasWhere = typeof body.where !== 'undefined';
    let hasChannels = typeof body.channels !== 'undefined';

    let where;
    if (hasWhere && hasChannels) {
      throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
        'Channels and query can not be set at the same time.');
    } else if (hasWhere) {
      where = body.where;
    } else if (hasChannels) {
      where = {
        "channels": {
          "$in": body.channels
        }
      }
    } else {
      throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED, 'Sending a push requires either "channels" or a "where" query.');
    }
    return where;
  }
}

export default PushRouter;
<|endoftext|>


<commit_before>const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var gAustralis = false;
try {
  Cu.import("resource://app/modules/CustomizableUI.jsm");
  gAustralis = true;
} catch (e) {}

const XMLHttpRequest = Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1");

XPCOMUtils.defineLazyGetter(this, "sss", function () {
  return Cc["@mozilla.org/content/style-sheet-service;1"]
           .getService(Ci.nsIStyleSheetService);
});

var prefsPrefix = "extensions.cck2wizard.";
var idPrefix = "cck2wizard-";

function showInstallPanel(doc, callback) {
  var panel = doc.getElementById(idPrefix + "install-panel");
  if (panel) {
    callback(panel);
    return;
  }
  var panel = doc.createElement("panel");
  panel.setAttribute("id", idPrefix + "login-panel");
  panel.setAttribute("width", "300");
  panel.setAttribute("orient", "vertical");
  panel.setAttribute("type", "arrow");
  var popupset = doc.getElementById("mainPopupSet");
  popupset.appendChild(panel);
  var request = new XMLHttpRequest();
  request.mozBackgroundRequest = true;
  request.open("GET", "chrome://cck2wizard/content/install-panel.xul");
  request.onload = function() {
    var installDialog = doc.importNode(request.responseXML.documentElement, true);
    panel.appendChild(installDialog);
    callback(panel);
  };
  request.send();

}

function install(aData, aReason) {
  Services.prefs.setCharPref(prefsPrefix + "toolbarID", "nav-bar");
}

function uninstall(aData, aReason) {
  Services.prefs.clearUserPref(prefsPrefix + "toolbarID");
  Services.prefs.clearUserPref(prefsPrefix + "nextSiblingID");
  // CCK2 configs are NOT removed
}

function startup(aData, aReason) {
  sss.loadAndRegisterSheet(Services.io.newURI("chrome://cck2wizard/skin/toolbar.css", null, null), sss.USER_SHEET);
  var win = Services.wm.getMostRecentWindow("navigator:browser");
  var url;
  switch (aReason) {
    case 5: // ADDON_INSTALL
      showInstallPanel(win.document, function(panel) {
        panel.addEventListener("click", function(event) {
          win.document.getElementById(idPrefix + "button").click();
          win.document.getElementById(idPrefix + "install-panel").hidePopup();
        }, false);
        panel.openPopup(win.document.getElementById(idPrefix + "button"), "", 0, 0, false, false, null);
      });
      url = "http://mike.kaply.com/addons/cck2/install/";
      break;
    case 7: // ADDON_UPGRADE
      url = "http://mike.kaply.com/addons/cck2/upgrade/";
      break;
  }
  if (url) {
    win.openUILinkIn(url, "tab");
  }

  let enumerator = Services.wm.getEnumerator("navigator:browser");
  while (enumerator.hasMoreElements()) {
    let win = enumerator.getNext().QueryInterface(Ci.nsIDOMWindow);
    if (win.document.readyState === "complete")
      loadIntoWindow(win);
    else
      win.addEventListener("load", function() {
        win.removeEventListener("load", arguments.callee, false);
        loadIntoWindow(win);
      })
  }
  Services.wm.addListener(windowListener);
}

function shutdown(data, reason) {
  sss.unregisterSheet(Services.io.newURI("chrome://cck2wizard/skin/toolbar.css", null, null), sss.USER_SHEET);
  Services.wm.removeListener(windowListener);

  let enumerator = Services.wm.getEnumerator("navigator:browser");
  while (enumerator.hasMoreElements()) {
    let win = enumerator.getNext().QueryInterface(Ci.nsIDOMWindow);
    unloadFromWindow(win);
  }
}

/* Save the current toolbarbutton position in preferences
*/
function saveButtonPosition(event) {
  var button = event.target.ownerDocument.getElementById(idPrefix + "button");
  var buttonPlacement = null;
  if (gAustralis) {
    buttonPlacement = CustomizableUI.getPlacementOfWidget(idPrefix + "button");
  }
  if (!button || (gAustralis && !buttonPlacement)) {
    Services.prefs.clearUserPref(prefsPrefix + "toolbarID");
    Services.prefs.clearUserPref(prefsPrefix + "nextSiblingID");
    return;
  }
  if (buttonPlacement) {
    Services.prefs.setCharPref(prefsPrefix + "toolbarID", buttonPlacement.area);
  } else {
    Services.prefs.setCharPref(prefsPrefix + "toolbarID", button.parentNode.id);
  }
  Services.prefs.setCharPref(prefsPrefix + "nextSiblingID", button.nextSibling.id);
}

function onCommand(event) {
  if (event.target.id != idPrefix + "button")
    return;
  var win = Services.ww.getWindowByName("cck2wizard", null);
  if (win) {
    win.focus();
    return;
  }
  Services.ww.openWindow(null, "chrome://cck2wizard/content/cck2wizard.xul", "cck2wizard", "chrome=yes,resizable=yes,dialog,centerscreen", null);
}

function loadIntoWindow(window) {
  if (!window || window.document.documentElement.getAttribute("windowtype") != "navigator:browser")
    return;

  let doc = window.document;
  let toolbox = doc.getElementById("navigator-toolbox");
  if (toolbox) {
    let button = doc.createElement("toolbarbutton");
    button.setAttribute("id", idPrefix + "button");
    button.setAttribute("label", "CCK2 Wizard");
    button.setAttribute("class", "toolbarbutton-1");
    toolbox.palette.appendChild(button);
    if (Services.prefs.prefHasUserValue(prefsPrefix + "toolbarID")) {
      var toolbarID = Services.prefs.getCharPref(prefsPrefix + "toolbarID");
      var nextSibling = null;
      try {
        nextSibling = doc.getElementById(Services.prefs.getCharPref(prefsPrefix + "nextSiblingID"));
      } catch (ex) {}
      if (gAustralis) {
        var buttonPlacement = null;
        if (nextSibling) {
          buttonPlacement = CustomizableUI.getPlacementOfWidget(nextSibling.id);
        }
        if (!nextSibling || !buttonPlacement) {
          CustomizableUI.addWidgetToArea(idPrefix + "button", toolbarID);
        } else {
          CustomizableUI.addWidgetToArea(idPrefix + "button",  toolbarID, buttonPlacement.position);

        }
      } else {
        var toolbar = doc.getElementById(toolbarID);
        var nextSibling = null;
        try {
          nextSibling = doc.getElementById(Services.prefs.getCharPref(prefsPrefix + "nextSiblingID"));
        } catch (ex) {}
        toolbar.insertItem(idPrefix + "button", nextSibling);
      }
    }
    window.addEventListener("aftercustomization", saveButtonPosition, false);
    window.addEventListener("command", onCommand, false);
  }
}

function unloadFromWindow(window) {
  var doc = window.document;
  if (!window || doc.documentElement.getAttribute("windowtype") != "navigator:browser")
    return;
  let button = doc.getElementById(idPrefix + "button");
  if (button) {
    button.parentNode.removeChild(button);
  } else {
    var toolbox = doc.getElementById("navigator-toolbox");
    if (toolbox && toolbox.palette) {
      var element = toolbox.palette.querySelector("#" + buttonID);
      if (element) {
        element.parentNode.removeChild(element);
      }
    }
  }
  let panel = doc.getElementById(idPrefix + "install-panel");
  if (panel) {
    panel.parentNode.removeChild(panel);
  }

  window.removeEventListener("aftercustomization", saveButtonPosition, false);
  window.removeEventListener("command", onCommand, false);
}

var windowListener = {
  onOpenWindow: function(window) {
    let domWindow = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
    domWindow.addEventListener("load", function() {
      domWindow.removeEventListener("load", arguments.callee, false);
      loadIntoWindow(domWindow);
    }, false);
  },
  onCloseWindow: function(window) {
    let domWindow = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
    unloadFromWindow(domWindow);
  },
  onWindowTitleChange: function(window) {}
};
<commit_msg>Make button work on Australis panel<commit_after>const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var gAustralis = false;
try {
  Cu.import("resource://app/modules/CustomizableUI.jsm");
  gAustralis = true;
} catch (e) {}

const XMLHttpRequest = Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1");

XPCOMUtils.defineLazyGetter(this, "sss", function () {
  return Cc["@mozilla.org/content/style-sheet-service;1"]
           .getService(Ci.nsIStyleSheetService);
});

var prefsPrefix = "extensions.cck2wizard.";
var idPrefix = "cck2wizard-";

function showInstallPanel(doc, callback) {
  var panel = doc.getElementById(idPrefix + "install-panel");
  if (panel) {
    callback(panel);
    return;
  }
  var panel = doc.createElement("panel");
  panel.setAttribute("id", idPrefix + "login-panel");
  panel.setAttribute("width", "300");
  panel.setAttribute("orient", "vertical");
  panel.setAttribute("type", "arrow");
  var popupset = doc.getElementById("mainPopupSet");
  popupset.appendChild(panel);
  var request = new XMLHttpRequest();
  request.mozBackgroundRequest = true;
  request.open("GET", "chrome://cck2wizard/content/install-panel.xul");
  request.onload = function() {
    var installDialog = doc.importNode(request.responseXML.documentElement, true);
    panel.appendChild(installDialog);
    callback(panel);
  };
  request.send();

}

function install(aData, aReason) {
  if (aReason != 5) {
    return;
  }
  Services.prefs.setCharPref(prefsPrefix + "toolbarID", "nav-bar");
}

function uninstall(aData, aReason) {
  Services.prefs.clearUserPref(prefsPrefix + "toolbarID");
  Services.prefs.clearUserPref(prefsPrefix + "nextSiblingID");
  // CCK2 configs are NOT removed
}

function startup(aData, aReason) {
  sss.loadAndRegisterSheet(Services.io.newURI("chrome://cck2wizard/skin/toolbar.css", null, null), sss.USER_SHEET);
  var win = Services.wm.getMostRecentWindow("navigator:browser");
  var url;
  switch (aReason) {
    case 5: // ADDON_INSTALL
      showInstallPanel(win.document, function(panel) {
        panel.addEventListener("click", function(event) {
          win.document.getElementById(idPrefix + "button").click();
          win.document.getElementById(idPrefix + "install-panel").hidePopup();
        }, false);
        panel.openPopup(win.document.getElementById(idPrefix + "button"), "", 0, 0, false, false, null);
      });
      url = "http://mike.kaply.com/addons/cck2/install/";
      break;
    case 7: // ADDON_UPGRADE
      url = "http://mike.kaply.com/addons/cck2/upgrade/";
      break;
  }
  if (url) {
    win.openUILinkIn(url, "tab");
  }

  let enumerator = Services.wm.getEnumerator("navigator:browser");
  while (enumerator.hasMoreElements()) {
    let win = enumerator.getNext().QueryInterface(Ci.nsIDOMWindow);
    if (win.document.readyState === "complete")
      loadIntoWindow(win);
    else
      win.addEventListener("load", function() {
        win.removeEventListener("load", arguments.callee, false);
        loadIntoWindow(win);
      })
  }
  Services.wm.addListener(windowListener);
}

function shutdown(data, reason) {
  sss.unregisterSheet(Services.io.newURI("chrome://cck2wizard/skin/toolbar.css", null, null), sss.USER_SHEET);
  Services.wm.removeListener(windowListener);

  let enumerator = Services.wm.getEnumerator("navigator:browser");
  while (enumerator.hasMoreElements()) {
    let win = enumerator.getNext().QueryInterface(Ci.nsIDOMWindow);
    unloadFromWindow(win);
  }
}

/* Save the current toolbarbutton position in preferences
*/
function saveButtonPosition(event) {
  var button = event.target.ownerDocument.getElementById(idPrefix + "button");
  var buttonPlacement = null;
  if (gAustralis) {
    buttonPlacement = CustomizableUI.getPlacementOfWidget(idPrefix + "button");
  }
  if (!button || (gAustralis && !buttonPlacement)) {
    Services.prefs.clearUserPref(prefsPrefix + "toolbarID");
    Services.prefs.clearUserPref(prefsPrefix + "nextSiblingID");
    return;
  }
  if (buttonPlacement) {
    Services.prefs.setCharPref(prefsPrefix + "toolbarID", buttonPlacement.area);
  } else {
    Services.prefs.setCharPref(prefsPrefix + "toolbarID", button.parentNode.id);
  }
  if (button.nextSibling) {
    Services.prefs.setCharPref(prefsPrefix + "nextSiblingID", button.nextSibling.id);
  }
}

function onCommand(event) {
  if (event.target.id != idPrefix + "button")
    return;
  var win = Services.ww.getWindowByName("cck2wizard", null);
  if (win) {
    win.focus();
    return;
  }
  Services.ww.openWindow(null, "chrome://cck2wizard/content/cck2wizard.xul", "cck2wizard", "chrome=yes,resizable=yes,dialog,centerscreen", null);
}

function loadIntoWindow(window) {
  if (!window || window.document.documentElement.getAttribute("windowtype") != "navigator:browser")
    return;

  let doc = window.document;
  let toolbox = doc.getElementById("navigator-toolbox");
  if (toolbox) {
    let button = doc.createElement("toolbarbutton");
    button.setAttribute("id", idPrefix + "button");
    button.setAttribute("label", "CCK2 Wizard");
    button.setAttribute("class", "toolbarbutton-1");
    toolbox.palette.appendChild(button);
    if (Services.prefs.prefHasUserValue(prefsPrefix + "toolbarID")) {
      var toolbarID = Services.prefs.getCharPref(prefsPrefix + "toolbarID");
      var nextSibling = null;
      try {
        nextSibling = doc.getElementById(Services.prefs.getCharPref(prefsPrefix + "nextSiblingID"));
      } catch (ex) {}
      if (gAustralis) {
        var buttonPlacement = null;
        if (nextSibling) {
          buttonPlacement = CustomizableUI.getPlacementOfWidget(nextSibling.id);
          if (buttonPlacement.area != toolbarID) {
            buttonPlacement = null;
          }
        }
        if (!nextSibling || !buttonPlacement) {
          CustomizableUI.addWidgetToArea(idPrefix + "button", toolbarID);
        } else {
          CustomizableUI.addWidgetToArea(idPrefix + "button",  toolbarID, buttonPlacement.position);

        }
      } else {
        var toolbar = doc.getElementById(toolbarID);
        var nextSibling = null;
        try {
          nextSibling = doc.getElementById(Services.prefs.getCharPref(prefsPrefix + "nextSiblingID"));
        } catch (ex) {}
        toolbar.insertItem(idPrefix + "button", nextSibling);
      }
      button.addEventListener("command", onCommand, false);
    }
    window.addEventListener("aftercustomization", saveButtonPosition, false);
  }
}

function unloadFromWindow(window) {
  var doc = window.document;
  if (!window || doc.documentElement.getAttribute("windowtype") != "navigator:browser")
    return;
  let button = doc.getElementById(idPrefix + "button");
  if (button) {
    button.parentNode.removeChild(button);
    button.removeEventListener("command", onCommand, false);
  } else {
    var toolbox = doc.getElementById("navigator-toolbox");
    if (toolbox && toolbox.palette) {
      var element = toolbox.palette.querySelector("#" + buttonID);
      if (element) {
        element.parentNode.removeChild(element);
        element.removeEventListener("command", onCommand, false);
      }
    }
  }
  let panel = doc.getElementById(idPrefix + "install-panel");
  if (panel) {
    panel.parentNode.removeChild(panel);
  }

  window.removeEventListener("aftercustomization", saveButtonPosition, false);
}

var windowListener = {
  onOpenWindow: function(window) {
    let domWindow = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
    domWindow.addEventListener("load", function() {
      domWindow.removeEventListener("load", arguments.callee, false);
      loadIntoWindow(domWindow);
    }, false);
  },
  onCloseWindow: function(window) {
    let domWindow = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
    unloadFromWindow(domWindow);
  },
  onWindowTitleChange: function(window) {}
};
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

// Note that these are webpack requires, not CommonJS node requiring requires
const React = require('react')
const Immutable = require('immutable')
const messages = require('../constants/messages')
const {ADBLOCK_CUSTOM_RULES} = require('../constants/settings')
const getSetting = require('../settings').getSetting
const aboutActions = require('./aboutActions')
const ImmutableComponent = require('../components/immutableComponent')
const SwitchControl = require('../components/switchControl')

const ipc = window.chrome.ipc

// Stylesheets
require('../../less/switchControls.less')
require('../../less/about/itemList.less')
require('../../less/about/adblock.less')

class AdBlockItem extends ImmutableComponent {
  constructor () {
    super()
    this.onClick = this.onClick.bind(this)
  }
  onClick (e) {
    aboutActions.updateAdblockDataFiles(this.props.resource.get('uuid'), e.target.value)
  }
  get prefKey () {
    return `adblock.${this.props.resource.get('uuid')}.enabled`
  }
  render () {
    return

      <SwitchControl id={this.props.resource.get('uuid')}
        rightText={this.props.resource.get('title')}
        className={`switch-${this.props.resource.get('uuid')}`}
        disabled={this.props.disabled}
        onClick={this.onClick}
        checkedOn={getSetting(this.prefKey, this.props.settings)} />

  }
}

class AboutAdBlock extends React.Component {
  constructor () {
    super()
    this.onChangeCustomFilters = this.onChangeCustomFilters.bind(this)
    this.state = {
      adblock: Immutable.Map(),
      resources: Immutable.List()
    }
    ipc.on(messages.ADBLOCK_UPDATED, (e, detail) => {
      if (!detail) {
        return
      }
      this.setState({
        adblock: Immutable.fromJS(detail.adblock),
        settings: Immutable.fromJS(detail.settings),
        resources: Immutable.fromJS(detail.resources || [])
      })
    })
  }
  render () {
    const lastUpdateDate = new Date(this.state.adblock.get('lastCheckDate'))
    return

      <list>

 {this.state.adblock.get('count') || 0}

            {
              Number.isNaN(lastUpdateDate.getTime())
              ? null
              :
 {lastUpdateDate.toLocaleDateString()}

            }
            {
              this.state.adblock.get('etag')
              ?
 {this.state.adblock.get('etag')}

              : null
            }

              {
                this.state.resources.map((resource) =>
                  <AdBlockItem resource={resource}
                    settings={this.state.settings} />)
              }

            <textarea
              onChange={this.onChangeCustomFilters}
              value={getSetting(ADBLOCK_CUSTOM_RULES, this.state.settings) || ''}
              className='customFiltersInput'
              cols='100'
              rows='10' />

      </list>

  }
  onChangeCustomFilters (e) {
    this.setState({
      settings: this.state.settings.set(ADBLOCK_CUSTOM_RULES, e.target.value)
    })
    aboutActions.updateCustomAdblockRules(e.target.value)
  }
}

module.exports = <AboutAdBlock />
<commit_msg>Disable spellcheck for custom filters list<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

// Note that these are webpack requires, not CommonJS node requiring requires
const React = require('react')
const Immutable = require('immutable')
const messages = require('../constants/messages')
const {ADBLOCK_CUSTOM_RULES} = require('../constants/settings')
const getSetting = require('../settings').getSetting
const aboutActions = require('./aboutActions')
const ImmutableComponent = require('../components/immutableComponent')
const SwitchControl = require('../components/switchControl')

const ipc = window.chrome.ipc

// Stylesheets
require('../../less/switchControls.less')
require('../../less/about/itemList.less')
require('../../less/about/adblock.less')

class AdBlockItem extends ImmutableComponent {
  constructor () {
    super()
    this.onClick = this.onClick.bind(this)
  }
  onClick (e) {
    aboutActions.updateAdblockDataFiles(this.props.resource.get('uuid'), e.target.value)
  }
  get prefKey () {
    return `adblock.${this.props.resource.get('uuid')}.enabled`
  }
  render () {
    return

      <SwitchControl id={this.props.resource.get('uuid')}
        rightText={this.props.resource.get('title')}
        className={`switch-${this.props.resource.get('uuid')}`}
        disabled={this.props.disabled}
        onClick={this.onClick}
        checkedOn={getSetting(this.prefKey, this.props.settings)} />

  }
}

class AboutAdBlock extends React.Component {
  constructor () {
    super()
    this.onChangeCustomFilters = this.onChangeCustomFilters.bind(this)
    this.state = {
      adblock: Immutable.Map(),
      resources: Immutable.List()
    }
    ipc.on(messages.ADBLOCK_UPDATED, (e, detail) => {
      if (!detail) {
        return
      }
      this.setState({
        adblock: Immutable.fromJS(detail.adblock),
        settings: Immutable.fromJS(detail.settings),
        resources: Immutable.fromJS(detail.resources || [])
      })
    })
  }
  render () {
    const lastUpdateDate = new Date(this.state.adblock.get('lastCheckDate'))
    return

      <list>

 {this.state.adblock.get('count') || 0}

            {
              Number.isNaN(lastUpdateDate.getTime())
              ? null
              :
 {lastUpdateDate.toLocaleDateString()}

            }
            {
              this.state.adblock.get('etag')
              ?
 {this.state.adblock.get('etag')}

              : null
            }

              {
                this.state.resources.map((resource) =>
                  <AdBlockItem resource={resource}
                    settings={this.state.settings} />)
              }

            <textarea
              onChange={this.onChangeCustomFilters}
              value={getSetting(ADBLOCK_CUSTOM_RULES, this.state.settings) || ''}
              className='customFiltersInput'
              cols='100'
              rows='10'
              spellCheck='false' />

      </list>

  }
  onChangeCustomFilters (e) {
    this.setState({
      settings: this.state.settings.set(ADBLOCK_CUSTOM_RULES, e.target.value)
    })
    aboutActions.updateCustomAdblockRules(e.target.value)
  }
}

module.exports = <AboutAdBlock />
<|endoftext|>


<commit_before>/**
* @license Apache-2.0
*
* Copyright (c) 2021 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';

/*
* When adding modules to the namespace, ensure that they are added in alphabetical order according to module name.
*/

// MODULES //

var setReadOnly = require( '@stdlib/utils/define-read-only-property' );

// MAIN //

/**
* Top-level namespace.
*
* @namespace ns
*/
var ns = {};

/**
* @name unary
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/math/tools/unary}
*/
setReadOnly( ns, 'unary', require( '@stdlib/math/tools/unary' ) );

// EXPORTS //

module.exports = ns;
<commit_msg>Update namespace<commit_after>/**
* @license Apache-2.0
*
* Copyright (c) 2021 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';

/*
* When adding modules to the namespace, ensure that they are added in alphabetical order according to module name.
*/

// MODULES //

var setReadOnly = require( '@stdlib/utils/define-read-only-property' );

// MAIN //

/**
* Top-level namespace.
*
* @namespace ns
*/
var ns = {};

/**
* @name metaDataProps
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/math/tools/meta-data-props}
*/
setReadOnly( ns, 'metaDataProps', require( '@stdlib/math/tools/meta-data-props' ) );

/**
* @name unary
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/math/tools/unary}
*/
setReadOnly( ns, 'unary', require( '@stdlib/math/tools/unary' ) );

// EXPORTS //

module.exports = ns;
<|endoftext|>


<commit_before>const tap = require('tap');
const fastly = require('../lib/fastly.js');
const content_url = process.env.CONTENT_URL || '';
const service_id = process.env.SERVICE_ID || '';
const fastly_api_key = process.env.FASTLY_API_KEY || '';

var flib = new fastly(fastly_api_key);

// --> .request.send(< GET >) => .then(object)
tap.test('request GET -> then()', (t) => {
    t.type(flib.request, 'object', 'request => object');
    flib.request.options.method = 'GET';
    flib.request.options.path = `/content/edge_check?url=${content_url}`;
    flib.request.send()
        .then((res) => {
            t.type(res, 'object', '.then(object)');
            t.end();
        })
        .catch((err) => {
            t.fail(err);
            t.end();
        })
});
// --> .request.send() => .catch(object)
tap.test('request GET -> catch()', (t) => {
    flib.request.options.method = 'POST';
    var wrong_url = 'sorry for the inconveniences, im testing a new library :(';
    flib.request.options.path = `/content/edge_check?url=${wrong_url}`;
    flib.request.send()
        .then((res) => {
            t.fail(res);
            t.end();
        })
        .catch((err) => {
            t.type(err, 'object', '.catch(object)');
            t.end();
        })
});

tap.test('request.purge -> return object', (t) => {
    flib.request.options.path = '/' + content_url;
    flib.request.purge()
        .then((res) => {
            t.type(res, 'object', '.then(object)');
            t.end();
        })
        .catch((err) => {
            t.fail(res);
            t.end();
        })
})

tap.test('request.delete -> return string as error', (t) => {
    flib.request.options.path = '/'
    flib.request.delete()
        .then((res) => {
            t.type(res, 'string', '.then(string)');
            t.end();
        })
        .catch((err) => {
            t.fail(res);
            t.end();
        })
})<commit_msg>Update request.js<commit_after>const tap = require('tap');
const fastly = require('../lib/fastly.js');
const content_url = process.env.CONTENT_URL || '';
const service_id = process.env.SERVICE_ID || '';
const fastly_api_key = process.env.FASTLY_API_KEY || '';

var flib = new fastly(fastly_api_key);

tap.test('request', (t) => {
    t.type(flib.request, 'object', 'it exposes an object');
    t.type(flib.request.send, 'function', 'method exists');
    t.type(flib.request.get, 'function', 'method exists');
    t.type(flib.request.post, 'function', 'method exists');
    t.type(flib.request.purge, 'function', 'method exists');
    t.type(flib.request.delete, 'function', 'method exists');
    t.end();
});
<|endoftext|>


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

    var moduleName = angular
      .module('angAccordion', ['collapsibleItem'])
      .controller('angAccordionController', ['$scope', function($scope){
        var collapsibleItems = [];

        $scope.$watch('collapseAll', function () {
          if ($scope.collapseAll == true) {
              angular.forEach(collapsibleItems, function (collapsibleItem) {
                collapsibleItem.isOpenned = false;
                collapsibleItem.icon = collapsibleItem.closeIcon;
              });
          }
          // dynamically expand all panels
          else { // expand all
              angular.forEach(collapsibleItems, function (collapsibleItem) {
                  collapsibleItem.isOpenned = true;
                  collapsibleItem.icon = collapsibleItem.openIcon;
              });
          }
        });

          this.openCollapsibleItem = function(collapsibleItemToOpen) {
            if( $scope.oneAtATime ) {
              angular.forEach(collapsibleItems, function(collapsibleItem) {
                collapsibleItem.isOpenned = false;
                collapsibleItem.icon = collapsibleItem.closeIcon;
              });
            }
            collapsibleItemToOpen.isOpenned = true;
          };

          this.addCollapsibleItem = function(collapsibleItem) {
            collapsibleItems.push(collapsibleItem);

            if ( $scope.closeIconClass !== undefined || $scope.openIconClass !== undefined ) {
              collapsibleItem.iconsType = 'class';
              collapsibleItem.closeIcon = $scope.closeIconClass;
              collapsibleItem.openIcon = $scope.openIconClass;
            }
            else if ( $scope.closeIconUrl !== undefined || $scope.openIconUrl !== undefined ) {
              collapsibleItem.iconsType = 'url';
              collapsibleItem.closeIcon = $scope.closeIconUrl;
              collapsibleItem.openIcon = $scope.openIconUrl;
            }

            collapsibleItem.iconIsOnLeft = $scope.iconPosition == 'left' ? true: false;
          };

      }])
      .directive('angAccordion', function() {
      return {
        restrict: 'EA',
        transclude: true,
        replace: true,
        scope: {
          oneAtATime: '@',
          closeIconUrl: '@',
          openIconUrl: '@',
          closeIconClass: '@',
          openIconClass: '@',
          iconPosition: '@',
          collapseAll: '='
        },
        controller: 'angAccordionController',
        template: '

'
      };
    })
    .name;

    angular.module('collapsibleItem', []).directive('collapsibleItem', function() {
      return {
        require: '^angAccordion',
        restrict: 'EA',
        transclude: true,
        replace: true,
        scope: {
          itemTitle: '@',
          itemDisabled: '=',
          initiallyOpen: '='
        },
        link: function(scope, element, attrs, accordionController) {
          scope.isOpenned = (scope.initiallyOpen) ? true : false;
          accordionController.addCollapsibleItem(scope);

          if(scope.isOpenned)
            scope.icon = scope.openIcon;
          else
            scope.icon = scope.closeIcon;

          scope.toggleCollapsibleItem = function () {
            if(scope.itemDisabled)
              return;

            if(!scope.isOpenned) {
              accordionController.openCollapsibleItem(this);
              scope.icon = scope.openIcon;
            }
            else {
              scope.isOpenned = false;
              scope.icon = scope.closeIcon;
            }
          };

          scope.getIconUrl = function ( type ) {
            return type == 'url' ? scope.icon : null;
          };
        },
        template: '

{{itemTitle}}

'
      };
    });

    return moduleName;
})();
<commit_msg>fix collapse opening all when it's not wanted<commit_after>
(function() {
    'use strict';

    var moduleName = angular
      .module('angAccordion', ['collapsibleItem'])
      .controller('angAccordionController', ['$scope', function($scope){
        var collapsibleItems = [];

        $scope.$watch('collapseAll', function () {
          if ($scope.collapseAll == true) {
              angular.forEach(collapsibleItems, function (collapsibleItem) {
                collapsibleItem.isOpenned = false;
                collapsibleItem.icon = collapsibleItem.closeIcon;
              });
          }
          // dynamically expand all panels
          else if( $scope.collapseAll === false) { // expand all is set to false
              angular.forEach(collapsibleItems, function (collapsibleItem) {
                  collapsibleItem.isOpenned = true;
                  collapsibleItem.icon = collapsibleItem.openIcon;
              });
          }
        });

          this.openCollapsibleItem = function(collapsibleItemToOpen) {
            if( $scope.oneAtATime ) {
              angular.forEach(collapsibleItems, function(collapsibleItem) {
                collapsibleItem.isOpenned = false;
                collapsibleItem.icon = collapsibleItem.closeIcon;
              });
            }
            collapsibleItemToOpen.isOpenned = true;
          };

          this.addCollapsibleItem = function(collapsibleItem) {
            collapsibleItems.push(collapsibleItem);

            if ( $scope.closeIconClass !== undefined || $scope.openIconClass !== undefined ) {
              collapsibleItem.iconsType = 'class';
              collapsibleItem.closeIcon = $scope.closeIconClass;
              collapsibleItem.openIcon = $scope.openIconClass;
            }
            else if ( $scope.closeIconUrl !== undefined || $scope.openIconUrl !== undefined ) {
              collapsibleItem.iconsType = 'url';
              collapsibleItem.closeIcon = $scope.closeIconUrl;
              collapsibleItem.openIcon = $scope.openIconUrl;
            }

            collapsibleItem.iconIsOnLeft = $scope.iconPosition == 'left' ? true: false;
          };

      }])
      .directive('angAccordion', function() {
      return {
        restrict: 'EA',
        transclude: true,
        replace: true,
        scope: {
          oneAtATime: '@',
          closeIconUrl: '@',
          openIconUrl: '@',
          closeIconClass: '@',
          openIconClass: '@',
          iconPosition: '@',
          collapseAll: '='
        },
        controller: 'angAccordionController',
        template: '

'
      };
    })
    .name;

    angular.module('collapsibleItem', []).directive('collapsibleItem', function() {
      return {
        require: '^angAccordion',
        restrict: 'EA',
        transclude: true,
        replace: true,
        scope: {
          itemTitle: '@',
          itemDisabled: '=',
          initiallyOpen: '='
        },
        link: function(scope, element, attrs, accordionController) {
          scope.isOpenned = (scope.initiallyOpen) ? true : false;
          accordionController.addCollapsibleItem(scope);

          if(scope.isOpenned)
            scope.icon = scope.openIcon;
          else
            scope.icon = scope.closeIcon;

          scope.toggleCollapsibleItem = function () {
            if(scope.itemDisabled)
              return;

            if(!scope.isOpenned) {
              accordionController.openCollapsibleItem(this);
              scope.icon = scope.openIcon;
            }
            else {
              scope.isOpenned = false;
              scope.icon = scope.closeIcon;
            }
          };

          scope.getIconUrl = function ( type ) {
            return type == 'url' ? scope.icon : null;
          };
        },
        template: '

{{itemTitle}}

'
      };
    });

    return moduleName;
})();
<|endoftext|>