<commit_before>// Copyright 2017, University of Colorado Boulder

/**
 * A trait for subtypes of Node, used to make the node behave like a 'number' input with assistive technology.
 * An accessible number spinner behaves like:
 *
 * - Arrow keys increment/decrement the value by a specified step size.
 * - Page Up and Page Down increments/decrements value by an alternative step size, usually larger than default.
 * - Home key sets value to its minimum.
 * - End key sets value to its maximum.
 *
 * This number spinner is different than typical 'number' inputs because it does not support number key control. All
 * changes go through the arrow, page up, page down, home, and end keys. For that reason, the HTML representation should
 * ideally look like: (values removed for readability)
 *
 *
Accessible Name

 *

 *
 * @author Jesse Greenberg (PhET Interactive Simulations)
 * @author Michael Barlow (PhET Interactive Simulations)
 */

define( function( require ) {
  'use strict';

  // modules
  var AccessiblePeer = require( 'SCENERY/accessibility/AccessiblePeer' );
  var CallbackTimer = require( 'SUN/CallbackTimer' );
  var Emitter = require( 'AXON/Emitter' );
  var extend = require( 'PHET_CORE/extend' );
  var inheritance = require( 'PHET_CORE/inheritance' );
  var KeyboardUtil = require( 'SCENERY/accessibility/KeyboardUtil' );
  var Node = require( 'SCENERY/nodes/Node' );
  var StringUtils = require( 'PHETCOMMON/util/StringUtils' );
  var sun = require( 'SUN/sun' );
  var Util = require( 'DOT/Util' );

  var AccessibleNumberSpinner = {

    /**
     * Implement functionality for a number spinner.
     * @public
     * @trait
     *
     * @param {function} type - The type (constructor) whose prototype we'll modify.
     */
    mixInto: function( type ) {
      assert && assert( _.includes( inheritance( type ), Node ) );

      var proto = type.prototype;

      extend( proto, {

        /**
         * This should be called in the constructor to initialize the accessible input features for the node.
         *
         * @param {Property.<number>} valueProperty
         * @param {Property.<Range>} enabledRangeProperty
         * @param {Property.<boolean>} enabledProperty
         * @param {Object} [options]
         *
         * @protected
         */
        initializeAccessibleNumberSpinner: function( valueProperty, enabledRangeProperty, enabledProperty, options ) {
          var self = this;

          var defaults = {
            a11yValueDelta: 1, // {number} - value changes by this quantity when using arrow keys
            a11yPageValueDelta: 2, // {number} - value changes by this quantity when using page up/page down

            a11yUseTimer: true, // if false value will change every native DOM keydown event, following options meaningless
            timerDelay: 400, // start to fire continuously after pressing for this long (milliseconds)
            timerInterval: 100, // fire continuously at this frequency (milliseconds),

            // a11y
            focusable: true,
            a11yValuePattern: '{{value}}', // {string} if you want units or additional content, add to pattern
            a11yDecimalPlaces: 0, // number of decimal places for the value read by assistive technology

            // set labelContent to give this AccessibleNumberSpinner an accessible name, required for spinbuttons
            // see https://github.com/phetsims/gravity-force-lab-basics/issues/62
            labelTagName: 'p',

            // Converts a value to a string, for extra formatting for the value read by the screen reader
            a11yFormatValue: function( value ) {
              return Util.toFixed( value, options.a11yDecimalPlaces );
            }
          };
          options = _.extend( {}, defaults, options );

          // Some options were already mutated in the constructor, only apply the accessibility-specific options here
          // so options are not doubled up, see https://github.com/phetsims/sun/issues/330
          var optionsToMutate = _.pick( options, _.keys( defaults ) );

          // cannot be set by client
          assert && assert( options.tagName === undefined, 'AccessibleNumberSpinner sets tagName' );
          optionsToMutate.tagName = 'div';

          assert && assert( options.ariaRole === undefined, 'AccessibleNumberSpinner sets ariaRole' );
          optionsToMutate.ariaRole = 'spinbutton';

          this.mutate( optionsToMutate );

          // this number spinner is "aria-labelledby" its own label, meaning that the label element content will be
          // read on focus
          this.addAriaLabelledbyAssociation( {
            thisElementName: AccessiblePeer.PRIMARY_SIBLING,
            otherNode: this,
            otherElementName: AccessiblePeer.LABEL_SIBLING
          } );

          // @private {Property.<number>}
          this._valueProperty = valueProperty;

          // @private {Property.<Range>}
          this._enabledRangeProperty = enabledRangeProperty;

          // @private{Property.<boolean>}
          this._enabledProperty = enabledProperty;

          // @private {number}
          this._a11yValueDelta = options.a11yValueDelta;

          // @private {number}
          this._a11yPageValueDelta = options.a11yPageValueDelta;

          // @private {boolean}
          this._a11yUseTimer = options.a11yUseTimer;

          // @private - manages timing must be disposed
          this._callbackTimer = new CallbackTimer( {
            delay: options.timerDelay,
            interval: options.timerInterval
          } );

          // @private {Emitter} emit events when increment/decrement keys are pressed down/up
          this.incrementDownEmitter = new Emitter();
          this.decrementDownEmitter = new Emitter();

          // @private {Emitter} - emit events when value is incremented/decremented
          this.valueIncrementEmitter = new Emitter();
          this.valueDecrementEmitter = new Emitter();

          // update enabled number range for AT, required for screen reader events to behave correctly, must be disposed
          var enabledRangeObserver = function( enabledRange ) {
            self.setAccessibleAttribute( 'min', enabledRange.min );
            self.setAccessibleAttribute( 'max', enabledRange.max );
          };
          this._enabledRangeProperty.link( enabledRangeObserver );

          // a callback that is added and removed from the timer depending on keystate
          var downCallback = null;

          // handle all accessible event input
          var accessibleInputListener = {
            keydown: function( event ) {
              // check for relevant keys here
              if ( KeyboardUtil.isRangeKey( event.keyCode ) ) {
                self.emitKeyState( event, true );

                // if using the timer, handle update at interval
                if ( self._a11yUseTimer ) {
                  if ( !self._callbackTimer.isRunning() ) {
                    self.handleKeyDown( event );

                    downCallback = self.handleKeyDown.bind( self, event );
                    self._callbackTimer.addCallback( downCallback );
                    self._callbackTimer.start();
                  }
                }
                else {
                  self.handleKeyDown( event );
                }
              }
            },
            keyup: function( event ) {
              if ( KeyboardUtil.isRangeKey( event.keyCode ) ) {
                self.emitKeyState( event, false );

                if ( self._a11yUseTimer ) {
                  self._callbackTimer.stop( false );
                  self._callbackTimer.removeCallback( downCallback );
                }
              }
            }
          };
          this.addAccessibleInputListener( accessibleInputListener );

          // when the property changes, be sure to update the accessible input value
          var accessiblePropertyListener = function( value ) {

            // format the value to a certain number of decimal places
            var formattedValue = options.a11yFormatValue( value );

            // wrap the value with optional string pattern
            formattedValue = StringUtils.fillIn( options.a11yValuePattern, {
              value: formattedValue
            } );

            self.setAccessibleAttribute( 'aria-valuetext', formattedValue );
            self.setAccessibleAttribute( 'aria-valuenow', value );
          };
          this._valueProperty.link( accessiblePropertyListener );

          // @private - called by disposeAccessibleNumberSpinner to prevent memory leaks
          this._disposeAccessibleNumberSpinner = function() {
            self._valueProperty.unlink( accessiblePropertyListener );
            self._enabledRangeProperty.unlink( enabledRangeObserver );
            self._callbackTimer.dispose();

            // emitters owned by this instance, can be disposed here
            self.incrementDownEmitter.dispose();
            self.decrementDownEmitter.dispose();
            self.valueIncrementEmitter.dispose();
            self.valueDecrementEmitter.dispose();

            self.removeAccessibleInputListener( accessibleInputListener );
          };
        },

        /**
         * Emit events related to the keystate of the spinner. Typically used to style the spinner during keyboard
         * interaction.
         * @private
         *
         * @param {UIEvent} event - the DOM event that was triggered
         * @param {boolean} isDown - whether or not event was triggered from down or up keys
         */
        emitKeyState: function( event, isDown ) {
          if ( event.keyCode === KeyboardUtil.KEY_UP_ARROW || event.keyCode === KeyboardUtil.KEY_RIGHT_ARROW ) {
            this.incrementDownEmitter.emit1( isDown );
          }
          else if ( event.keyCode === KeyboardUtil.KEY_DOWN_ARROW || event.keyCode === KeyboardUtil.KEY_LEFT_ARROW ) {
            this.decrementDownEmitter.emit1( isDown );
          }
        },

        /**
         * Handle the keydown event so that this node behaves like an accessible number input.
         * @private
         *
         * @param {DOMEvent} event
         */
        handleKeyDown: function( event ) {
          var code = event.keyCode;

          if ( this._enabledProperty.get() ) {

            // prevent user from changing value with number or the space keys, handle arrow keys on our own
            if ( KeyboardUtil.isArrowKey( code ) || KeyboardUtil.isNumberKey( code ) || code === KeyboardUtil.KEY_SPACE ) {
              event.preventDefault();
            }

            // handle the event
            if ( code === KeyboardUtil.KEY_RIGHT_ARROW || code === KeyboardUtil.KEY_UP_ARROW ) {
              this.valueIncrementEmitter.emit();
              this._valueProperty.set( Math.min( this._valueProperty.get() + this._a11yValueDelta, this._enabledRangeProperty.get().max ) );
            }
            else if ( code === KeyboardUtil.KEY_LEFT_ARROW || code === KeyboardUtil.KEY_DOWN_ARROW ) {
              this.valueDecrementEmitter.emit();
              this._valueProperty.set( Math.max( this._valueProperty.get() - this._a11yValueDelta, this._enabledRangeProperty.get().min ) );
            }
            else if ( code === KeyboardUtil.KEY_PAGE_UP ) {
              this._valueProperty.set( Math.min( this._valueProperty.get() + this._a11yPageValueDelta, this._enabledRangeProperty.get().max ) );
            }
            else if ( code === KeyboardUtil.KEY_PAGE_DOWN ) {
              this._valueProperty.set( Math.max( this._valueProperty.get() - this._a11yPageValueDelta, this._enabledRangeProperty.get().min ) );
            }
            else if ( code === KeyboardUtil.KEY_HOME ) {
              this._valueProperty.set( this._enabledRangeProperty.get().min );
            }
            else if ( code === KeyboardUtil.KEY_END ) {
              this._valueProperty.set( this._enabledRangeProperty.get().max );
            }
          }
        },

        /**
         * Make the accessibility related aspects of this node eligible for garbage collection. Call when disposing
         * the type that this trait is mixed into.
         * @public
         */
        disposeAccessibleNumberSpinner: function() {
          this._disposeAccessibleNumberSpinner();
        }
      } );
    }
  };

  sun.register( 'AccessibleNumberSpinner', AccessibleNumberSpinner );

  return AccessibleNumberSpinner;
} );
<commit_msg>update call to increment/decrement emitters, wrap timer stop in keycode check, see #408<commit_after>// Copyright 2017, University of Colorado Boulder

/**
 * A trait for subtypes of Node, used to make the node behave like a 'number' input with assistive technology.
 * An accessible number spinner behaves like:
 *
 * - Arrow keys increment/decrement the value by a specified step size.
 * - Page Up and Page Down increments/decrements value by an alternative step size, usually larger than default.
 * - Home key sets value to its minimum.
 * - End key sets value to its maximum.
 *
 * This number spinner is different than typical 'number' inputs because it does not support number key control. All
 * changes go through the arrow, page up, page down, home, and end keys. For that reason, the HTML representation should
 * ideally look like: (values removed for readability)
 *
 *
Accessible Name

 *

 *
 * @author Jesse Greenberg (PhET Interactive Simulations)
 * @author Michael Barlow (PhET Interactive Simulations)
 */

define( function( require ) {
  'use strict';

  // modules
  var AccessiblePeer = require( 'SCENERY/accessibility/AccessiblePeer' );
  var CallbackTimer = require( 'SUN/CallbackTimer' );
  var Emitter = require( 'AXON/Emitter' );
  var extend = require( 'PHET_CORE/extend' );
  var inheritance = require( 'PHET_CORE/inheritance' );
  var KeyboardUtil = require( 'SCENERY/accessibility/KeyboardUtil' );
  var Node = require( 'SCENERY/nodes/Node' );
  var StringUtils = require( 'PHETCOMMON/util/StringUtils' );
  var sun = require( 'SUN/sun' );
  var Util = require( 'DOT/Util' );

  var AccessibleNumberSpinner = {

    /**
     * Implement functionality for a number spinner.
     * @public
     * @trait
     *
     * @param {function} type - The type (constructor) whose prototype we'll modify.
     */
    mixInto: function( type ) {
      assert && assert( _.includes( inheritance( type ), Node ) );

      var proto = type.prototype;

      extend( proto, {

        /**
         * This should be called in the constructor to initialize the accessible input features for the node.
         *
         * @param {Property.<number>} valueProperty
         * @param {Property.<Range>} enabledRangeProperty
         * @param {Property.<boolean>} enabledProperty
         * @param {Object} [options]
         *
         * @protected
         */
        initializeAccessibleNumberSpinner: function( valueProperty, enabledRangeProperty, enabledProperty, options ) {
          var self = this;

          var defaults = {
            a11yValueDelta: 1, // {number} - value changes by this quantity when using arrow keys
            a11yPageValueDelta: 2, // {number} - value changes by this quantity when using page up/page down

            a11yUseTimer: true, // if false value will change every native DOM keydown event, following options meaningless
            timerDelay: 400, // start to fire continuously after pressing for this long (milliseconds)
            timerInterval: 100, // fire continuously at this frequency (milliseconds),

            // a11y
            focusable: true,
            a11yValuePattern: '{{value}}', // {string} if you want units or additional content, add to pattern
            a11yDecimalPlaces: 0, // number of decimal places for the value read by assistive technology

            // set labelContent to give this AccessibleNumberSpinner an accessible name, required for spinbuttons
            // see https://github.com/phetsims/gravity-force-lab-basics/issues/62
            labelTagName: 'p',

            // Converts a value to a string, for extra formatting for the value read by the screen reader
            a11yFormatValue: function( value ) {
              return Util.toFixed( value, options.a11yDecimalPlaces );
            }
          };
          options = _.extend( {}, defaults, options );

          // Some options were already mutated in the constructor, only apply the accessibility-specific options here
          // so options are not doubled up, see https://github.com/phetsims/sun/issues/330
          var optionsToMutate = _.pick( options, _.keys( defaults ) );

          // cannot be set by client
          assert && assert( options.tagName === undefined, 'AccessibleNumberSpinner sets tagName' );
          optionsToMutate.tagName = 'div';

          assert && assert( options.ariaRole === undefined, 'AccessibleNumberSpinner sets ariaRole' );
          optionsToMutate.ariaRole = 'spinbutton';

          this.mutate( optionsToMutate );

          // this number spinner is "aria-labelledby" its own label, meaning that the label element content will be
          // read on focus
          this.addAriaLabelledbyAssociation( {
            thisElementName: AccessiblePeer.PRIMARY_SIBLING,
            otherNode: this,
            otherElementName: AccessiblePeer.LABEL_SIBLING
          } );

          // @private {Property.<number>}
          this._valueProperty = valueProperty;

          // @private {Property.<Range>}
          this._enabledRangeProperty = enabledRangeProperty;

          // @private{Property.<boolean>}
          this._enabledProperty = enabledProperty;

          // @private {number}
          this._a11yValueDelta = options.a11yValueDelta;

          // @private {number}
          this._a11yPageValueDelta = options.a11yPageValueDelta;

          // @private {boolean}
          this._a11yUseTimer = options.a11yUseTimer;

          // @private - manages timing must be disposed
          this._callbackTimer = new CallbackTimer( {
            delay: options.timerDelay,
            interval: options.timerInterval
          } );

          // @private {Emitter} emit events when increment/decrement keys are pressed down/up
          this.incrementDownEmitter = new Emitter();
          this.decrementDownEmitter = new Emitter();

          // @private {Emitter} - emit events when value is incremented/decremented
          this.valueIncrementEmitter = new Emitter();
          this.valueDecrementEmitter = new Emitter();

          // update enabled number range for AT, required for screen reader events to behave correctly, must be disposed
          var enabledRangeObserver = function( enabledRange ) {
            self.setAccessibleAttribute( 'min', enabledRange.min );
            self.setAccessibleAttribute( 'max', enabledRange.max );
          };
          this._enabledRangeProperty.link( enabledRangeObserver );

          // a callback that is added and removed from the timer depending on keystate
          var downCallback = null;
          var runningTimerCallbackKeyCode = null;

          // handle all accessible event input
          var accessibleInputListener = {
            keydown: function( event ) {
              // check for relevant keys here
              if ( KeyboardUtil.isRangeKey( event.keyCode ) ) {
                // self.emitKeyState( event, true );

                // if using the timer, handle update at interval
                if ( self._a11yUseTimer ) {
                  if ( !self._callbackTimer.isRunning() ) {
                    self.handleKeyDown( event );

                    downCallback = self.handleKeyDown.bind( self, event );
                    runningTimerCallbackKeyCode = event.keyCode;
                    self._callbackTimer.addCallback( downCallback );
                    self._callbackTimer.start();
                  }
                }
                else {
                  self.handleKeyDown( event );
                }
              }
            },
            keyup: function( event ) {
              if ( KeyboardUtil.isRangeKey( event.keyCode ) ) {
                if ( self._a11yUseTimer ) {
                  if ( event.keyCode === runningTimerCallbackKeyCode ) {
                    self.emitKeyState( event, false );
                    self._callbackTimer.stop( false );
                    self._callbackTimer.removeCallback( downCallback );
                  }
                }
              }
            }
          };
          this.addAccessibleInputListener( accessibleInputListener );

          // when the property changes, be sure to update the accessible input value
          var accessiblePropertyListener = function( value ) {

            // format the value to a certain number of decimal places
            var formattedValue = options.a11yFormatValue( value );

            // wrap the value with optional string pattern
            formattedValue = StringUtils.fillIn( options.a11yValuePattern, {
              value: formattedValue
            } );

            self.setAccessibleAttribute( 'aria-valuetext', formattedValue );
            self.setAccessibleAttribute( 'aria-valuenow', value );
          };
          this._valueProperty.link( accessiblePropertyListener );

          // @private - called by disposeAccessibleNumberSpinner to prevent memory leaks
          this._disposeAccessibleNumberSpinner = function() {
            self._valueProperty.unlink( accessiblePropertyListener );
            self._enabledRangeProperty.unlink( enabledRangeObserver );
            self._callbackTimer.dispose();

            // emitters owned by this instance, can be disposed here
            self.incrementDownEmitter.dispose();
            self.decrementDownEmitter.dispose();
            self.valueIncrementEmitter.dispose();
            self.valueDecrementEmitter.dispose();

            self.removeAccessibleInputListener( accessibleInputListener );
          };
        },

        /**
         * Emit events related to the keystate of the spinner. Typically used to style the spinner during keyboard
         * interaction.
         * @private
         *
         * @param {UIEvent} event - the DOM event that was triggered
         * @param {boolean} isDown - whether or not event was triggered from down or up keys
         */
        emitKeyState: function( event, isDown ) {
          if ( event.keyCode === KeyboardUtil.KEY_UP_ARROW || event.keyCode === KeyboardUtil.KEY_RIGHT_ARROW ) {
            this.incrementDownEmitter.emit1( isDown );
          }
          else if ( event.keyCode === KeyboardUtil.KEY_DOWN_ARROW || event.keyCode === KeyboardUtil.KEY_LEFT_ARROW ) {
            this.decrementDownEmitter.emit1( isDown );
          }
        },

        /**
         * Handle the keydown event so that this node behaves like an accessible number input.
         * @private
         *
         * @param {DOMEvent} event
         */
        handleKeyDown: function( event ) {
          var code = event.keyCode;

          if ( this._enabledProperty.get() ) {
            this.emitKeyState( event, true );
            // prevent user from changing value with number or the space keys, handle arrow keys on our own
            if ( KeyboardUtil.isArrowKey( code ) || KeyboardUtil.isNumberKey( code ) || code === KeyboardUtil.KEY_SPACE ) {
              event.preventDefault();
            }

            // handle the event
            if ( code === KeyboardUtil.KEY_RIGHT_ARROW || code === KeyboardUtil.KEY_UP_ARROW ) {
              this.valueIncrementEmitter.emit();
              this._valueProperty.set( Math.min( this._valueProperty.get() + this._a11yValueDelta, this._enabledRangeProperty.get().max ) );
            }
            else if ( code === KeyboardUtil.KEY_LEFT_ARROW || code === KeyboardUtil.KEY_DOWN_ARROW ) {
              this.valueDecrementEmitter.emit();
              this._valueProperty.set( Math.max( this._valueProperty.get() - this._a11yValueDelta, this._enabledRangeProperty.get().min ) );
            }
            else if ( code === KeyboardUtil.KEY_PAGE_UP ) {
              this._valueProperty.set( Math.min( this._valueProperty.get() + this._a11yPageValueDelta, this._enabledRangeProperty.get().max ) );
            }
            else if ( code === KeyboardUtil.KEY_PAGE_DOWN ) {
              this._valueProperty.set( Math.max( this._valueProperty.get() - this._a11yPageValueDelta, this._enabledRangeProperty.get().min ) );
            }
            else if ( code === KeyboardUtil.KEY_HOME ) {
              this._valueProperty.set( this._enabledRangeProperty.get().min );
            }
            else if ( code === KeyboardUtil.KEY_END ) {
              this._valueProperty.set( this._enabledRangeProperty.get().max );
            }
          }
        },

        /**
         * Make the accessibility related aspects of this node eligible for garbage collection. Call when disposing
         * the type that this trait is mixed into.
         * @public
         */
        disposeAccessibleNumberSpinner: function() {
          this._disposeAccessibleNumberSpinner();
        }
      } );
    }
  };

  sun.register( 'AccessibleNumberSpinner', AccessibleNumberSpinner );

  return AccessibleNumberSpinner;
} );
<|endoftext|>


<commit_before>var fs = require('fs')
  , exec = require('child_process').exec

var test = require('tape')
  , through = require('through')
  , phantom = require('phantomjs')
  , concat = require('concat-stream')

var lib = require('../lib')

test('executes simple js', function(t) {
  t.plan(2)

  var js = fs.createReadStream('./test/fixtures/simple.js')
    , p = makeProcessObject()

  var options = {
      input: js
    , html: './test/fixtures/test.html'
    , phantomPath: phantom.path
    , timeout: 10
  }

  p.stdout.pipe(concat(function(data) {
    t.equal(data.toString(), 'simple test\n', 'output should match')
  }))

  lib(options, p, done)

  function done(code) {
    t.equal(code, 0, 'exit code should be clean')
  }
})

test('executes fibonacci for some reason', function(t) {
  t.plan(2)

  var js = fs.createReadStream('./test/fixtures/fibonacci.js')
    , p = makeProcessObject()

  var options = {
      input: js
    , html: './test/fixtures/test.html'
    , phantomPath: phantom.path
    , timeout: 10
  }

  p.stdout.pipe(concat(function(data) {
    t.equal(data.toString(), '1.1043307057295211e+125\n', 'output should match')
  }))

  lib(options, p, done)

  function done(code) {
    t.equal(code, 0, 'exit code should be clean')
  }
})

test('fails and sets correct exit code', function(t) {
  t.plan(3)

  var js = fs.createReadStream('./test/fixtures/fail.js')
    , p = makeProcessObject()

  var options = {
      input: js
    , html: './test/fixtures/test.html'
    , phantomPath: phantom.path
    , timeout: 100
  }

  p.stdout.pipe(concat(function(data) {
    t.equal(data.length, 0)
  }))

  p.stderr.pipe(concat(function(data) {
    t.equal(
        data.toString()
      , '\n\nTypeError: \'undefined\' is not a function (evaluating ' +
          '\'console.derp(\'whoops\')\')\n    at :1\n'
      , 'output should match'
    )
  }))

  lib(options, p, done)

  function done(code) {
    p.stderr.write(null)
    t.equal(code, 1, 'exit code should not be clean')
  }
})

test('handles timeouts when set', function(t) {
  t.plan(2)

  var js = fs.createReadStream('./test/fixtures/timeout.js')
    , p = makeProcessObject()

  var options = {
      input: js
    , html: './test/fixtures/test.html'
    , phantomPath: phantom.path
    , timeout: 50
  }

  var now = Date.now()

  p.stdout.pipe(concat(function() {
    t.ok(approx(600, Date.now() - now, 100), 'should take approximately 600ms')
  }))

  lib(options, p, done)

  function done(code) {
    t.equal(code, 0, 'exit code should be clean')
  }
})

test('handles source maps when available', function(t) {
  t.plan(2)

  var js = fs.createReadStream('./test/fixtures/browserify.js')
    , p = makeProcessObject()

  var options = {
      input: js
    , html: './test/fixtures/test.html'
    , phantomPath: phantom.path
    , timeout: 50
  }

  var expected = '\n\nTypeError: \'undefined\' is not a function ' +
    '(evaluating \'console.whoops(\'i am terrible\')\')\n' +
    '    at lib/something.js:2\n' +
    '    at index.js:4\n' +
    '    at ../../../../../../.nvm/v0.10.37/lib/node_modules/browserify/node_modules/browser-pack/_prelude.js:1\n' +
    '    at ../../../../../../.nvm/v0.10.37/lib/node_modules/browserify/node_modules/browser-pack/_prelude.js:1\n' +
    '    at null:0\n'

  p.stderr.pipe(concat(function(data) {
    t.equal(data.toString(), expected)
  }))

  lib(options, p, done)

  function done(code) {
    p.stderr.write(null)
    t.equal(code, 1, 'exit code should not be clean')
  }
})

function makeProcessObject() {
  var p = {
      stdin: through()
    , stdout: through()
    , stderr: through()
  }

  return p
}

function approx(num, equal, skew) {
  return Math.abs(num - equal) < skew
}
<commit_msg>Fixes prone-to-fail timing test<commit_after>var fs = require('fs')
  , exec = require('child_process').exec

var test = require('tape')
  , through = require('through')
  , phantom = require('phantomjs')
  , concat = require('concat-stream')

var lib = require('../lib')

test('executes simple js', function(t) {
  t.plan(2)

  var js = fs.createReadStream('./test/fixtures/simple.js')
    , p = makeProcessObject()

  var options = {
      input: js
    , html: './test/fixtures/test.html'
    , phantomPath: phantom.path
    , timeout: 10
  }

  p.stdout.pipe(concat(function(data) {
    t.equal(data.toString(), 'simple test\n', 'output should match')
  }))

  lib(options, p, done)

  function done(code) {
    t.equal(code, 0, 'exit code should be clean')
  }
})

test('executes fibonacci for some reason', function(t) {
  t.plan(2)

  var js = fs.createReadStream('./test/fixtures/fibonacci.js')
    , p = makeProcessObject()

  var options = {
      input: js
    , html: './test/fixtures/test.html'
    , phantomPath: phantom.path
    , timeout: 10
  }

  p.stdout.pipe(concat(function(data) {
    t.equal(data.toString(), '1.1043307057295211e+125\n', 'output should match')
  }))

  lib(options, p, done)

  function done(code) {
    t.equal(code, 0, 'exit code should be clean')
  }
})

test('fails and sets correct exit code', function(t) {
  t.plan(3)

  var js = fs.createReadStream('./test/fixtures/fail.js')
    , p = makeProcessObject()

  var options = {
      input: js
    , html: './test/fixtures/test.html'
    , phantomPath: phantom.path
    , timeout: 100
  }

  p.stdout.pipe(concat(function(data) {
    t.equal(data.length, 0)
  }))

  p.stderr.pipe(concat(function(data) {
    t.equal(
        data.toString()
      , '\n\nTypeError: \'undefined\' is not a function (evaluating ' +
          '\'console.derp(\'whoops\')\')\n    at :1\n'
      , 'output should match'
    )
  }))

  lib(options, p, done)

  function done(code) {
    p.stderr.write(null)
    t.equal(code, 1, 'exit code should not be clean')
  }
})

test('handles timeouts when set', function(t) {
  t.plan(2)

  var js = fs.createReadStream('./test/fixtures/timeout.js')
    , p = makeProcessObject()

  var options = {
      input: js
    , html: './test/fixtures/test.html'
    , phantomPath: phantom.path
    , timeout: 50
  }

  var now = Date.now()

  p.stdout.pipe(concat(function() {
    t.ok(approx(600, Date.now() - now, 150), 'should take approximately 600ms')
  }))

  lib(options, p, done)

  function done(code) {
    t.equal(code, 0, 'exit code should be clean')
  }
})

test('handles source maps when available', function(t) {
  t.plan(2)

  var js = fs.createReadStream('./test/fixtures/browserify.js')
    , p = makeProcessObject()

  var options = {
      input: js
    , html: './test/fixtures/test.html'
    , phantomPath: phantom.path
    , timeout: 50
  }

  var expected = '\n\nTypeError: \'undefined\' is not a function ' +
    '(evaluating \'console.whoops(\'i am terrible\')\')\n' +
    '    at lib/something.js:2\n' +
    '    at index.js:4\n' +
    '    at ../../../../../../.nvm/v0.10.37/lib/node_modules/browserify/node_modules/browser-pack/_prelude.js:1\n' +
    '    at ../../../../../../.nvm/v0.10.37/lib/node_modules/browserify/node_modules/browser-pack/_prelude.js:1\n' +
    '    at null:0\n'

  p.stderr.pipe(concat(function(data) {
    t.equal(data.toString(), expected)
  }))

  lib(options, p, done)

  function done(code) {
    p.stderr.write(null)
    t.equal(code, 1, 'exit code should not be clean')
  }
})

function makeProcessObject() {
  var p = {
      stdin: through()
    , stdout: through()
    , stderr: through()
  }

  return p
}

function approx(number, compare, skew) {
  var lower = compare - skew
    , upper = compare + skew

  return lower < number && number < upper
}
<|endoftext|>


<commit_before>/*:
 * @plugindesc <RS_LoadTimeChecker>
 * @author biud436
 *
 * @param Text Option
 *
 * @param Day
 * @parent Text Option
 * @desc Specify the 'Day' text to show up on the screen.
 * @default 일
 *
 * @param Hours
 * @parent Text Option
 * @desc Specify the 'Hours' text to show up on the screen.
 * @default 시간
 *
 * @param Minutes
 * @parent Text Option
 * @desc Specify the 'Minutes' text to show up on the screen.
 * @default 분
 *
 * @param Seconds
 * @parent Text Option
 * @desc Specify the 'Seconds' text to show up on the screen.
 * @default 초
 *
 * @param Description Text
 * @parent Text Option
 * @desc Specify the description text to show up on the screen.
 * @default 만에 게임으로 돌아오셨습니다.
 *
 * @param Hide Time
 * @parent Text Option
 * @type number
 * @desc Specify the hide seconds.
 * @default 6
 *
 * @param Position
 *
 * @param X
 * @parent Position
 * @desc Specify the position.
 * (This is evaluated the javascript)
 * @default 0
 *
 * @param Y
 * @parent Position
 * @desc Specify the position of the text.
 * (This is evaluated the javascript)
 * @default 0
 *
 * @param Font Option
 *
 * @param Font Face
 * @parent Font Option
 * @desc Specify the font face.
 * @default GameFont
 *
 * @param Font Size
 * @parent Font Option
 * @type number
 * @desc Specify the font size.
 * @default 28
 *
 * @param Text Color
 * @parent Font Option
 * @desc Specify the text color.
 * @default #ffffff
 *
 * @param Outline Color
 * @parent Font Option
 * @desc Specify the outline color.
 * @default rgba(0, 0, 0, 0.5)
 *
 * @param Outline Width
 * @parent Font Option
 * @type number
 * @desc Specify the outline width.
 * @default 4
 *
 * @param Text Align
 * @parent Font Option
 * @type select
 * @desc Specify the text alignment.
 * @default center
 * @option left
 * @value left
 * @option cetner
 * @value center
 * @option right
 * @value right
 *
 * @param Bitmap Width
 * @parent Font Option
 * @desc Specify the bitmap width.
 * (This is evaluated the javascript)
 * @default Graphics.boxWidth
 *
 * @param Bitmap Height
 * @parent Font Option
 * @desc Specify the bitmap height.
 * (This is evaluated the javascript)
 * @default 64
 *
 * @param Reward Option
 *
 * @param Code Injection
 * @parent Reward Option
 * @type note
 * @desc Can evaluate a javascript code using day, hours, mins, seconds variables.
 * @default "// if(day >= 1) {\n//   $gameParty.gainGold(1000 * day);\n// }\n// if(hours >= 1) {\n//   $gameParty.gainGold(200 * hours);\n// }\n// if(mins >= 30) {\n//   $gameParty.gainGold(100 * mins);\n// }\n// if(seconds >= 45) {\n//   $gameParty.gainGold(50 * seconds);\n// }"
 *
 * @help
 * ========================================================
 * Introduction
 * ========================================================
 * This plugin allows to indicate as the time text on the screen when loaded a save file.
 * The time text is that how long it took the amount of time since last save time.
 *
 * ========================================================
 * Change Log
 * ========================================================
 * 2019.07.13 (v1.0.0) - First Release.
 */

var Imported = Imported || {};
Imported.RS_LoadTimeChecker = true;

var RS = RS || {};
RS.LoadTimeChecker = RS.LoadTimeChecker || {};

(function($) {

    var parameters = $plugins.filter(function (i) {
      return i.description.contains('<RS_LoadTimeChecker>');
    });

    parameters = (parameters.length > 0) && parameters[0].parameters;

    $.Params = {};
    $.Params.dayText = parameters["Day"] || "일";
    $.Params.hoursText = parameters["Hours"] || "시간";
    $.Params.minutesText = parameters["Minutes"] || "분";
    $.Params.secondsText = parameters["Seconds"] || "초";
    $.Params.descText = parameters["Description Text"];

    $.Params.hideTime = parseInt(parameters["Hide Time"] || 6);
    $.Params.dx = parameters["X"];
    $.Params.dy = parameters["Y"];

    $.Params.font = {
        fontFace: parameters["Font Face"] || "GameFont",
        fontSize: Number(parameters["Font Size"] || 28),
        textColor: parameters["Text Color"] || "#ffffff",
        outlineColor: parameters["Outline Color"] || "rgba(0, 0, 0, 0.5)",
        outlineWidth: Number(parameters["Outline Width"] || 4),
        textAlign: parameters["Text Align"] || "center",
    };

    $.Params.bitmap = {
        width: parameters["Bitmap Width"],
        height: parameters["Bitmap Height"],
    };

    $.Params.evaluateCode = parameters["Code Injection"];

    var alias_Game_System_initialize = Game_System.prototype.initialize;
    Game_System.prototype.initialize = function() {
        alias_Game_System_initialize.call(this);
        this._beforeDateTime = null;
        this._loadDateTime = null;
        this._refreshTime = false;
    };

    var alias_Game_System_onBeforeSave = Game_System.prototype.onBeforeSave;
    Game_System.prototype.onBeforeSave = function() {
        alias_Game_System_onBeforeSave.call(this);
        this._beforeDateTime = new Date().getTime();
    };

    var alias_Game_System_onAfterLoad = Game_System.prototype.onAfterLoad;
    Game_System.prototype.onAfterLoad = function() {
        alias_Game_System_onAfterLoad.call(this);
        this._loadDateTime = new Date().getTime();
        this.refreshTimeChecker();
    };

    Game_System.prototype.beforeTime = function() {
        return this._beforeDateTime;
    };

    Game_System.prototype.loadTime = function() {
        return this._loadDateTime;
    };

    Game_System.prototype.refreshTimeChecker = function() {
        this._refreshTime = true;
    };

    Game_System.prototype.restoreTimeChecker = function() {
        this._refreshTime = false;
    };

    var alias_Scene_Map_onMapLoaded = Scene_Map.prototype.onMapLoaded;
    Scene_Map.prototype.onMapLoaded = function() {
        alias_Scene_Map_onMapLoaded.call(this);
        if($gameSystem._refreshTime) {
            this.refreshTimeChecker($gameSystem.beforeTime(), $gameSystem.loadTime());
            $gameSystem.restoreTimeChecker();
        }
    };

    Scene_Map.prototype.refreshTimeChecker = function(before, load) {
        if(!this._spriteset) return;
        this._spriteset.refreshTimeChecker(before, load);
    };

    var alias_Spriteset_Map_createLowerLayer = Spriteset_Map.prototype.createLowerLayer;
    Spriteset_Map.prototype.createLowerLayer = function() {
        alias_Spriteset_Map_createLowerLayer.call(this);
        this.createTimeChecker();
    };

    Spriteset_Map.prototype.createTimeChecker = function() {
        this._timeChecker = new Sprite();
        var width = eval($.Params.bitmap.width);
        var height = eval($.Params.bitmap.height);
        this._timeChecker.bitmap = new Bitmap(width, height);
        this._timeChecker.visible = false;
        this.addChild(this._timeChecker);
    };

    /**
     * @param {Date} before
     * @param {Date} load
     */
    Spriteset_Map.prototype.refreshTimeChecker = function(before, load) {

        "use strict";

        if(!this._timeChecker) {
            this.createTimeChecker();
        }

        var newBeforeTime = new Date(before);
        var newLoadTime = new Date(load);

        const width = eval($.Params.bitmap.width);
        const height = eval($.Params.bitmap.height);

        const dayValue = 60 * 60 * 24 * 1000;
        const hourValue = 60 * 60 * 1000;
        const minValue = 60 * 1000;
        const secondValue = 1000;

        let loadTime = newLoadTime.getTime();
        let beforeTime = newBeforeTime.getTime();
if(day >= 1) {
  $gameParty.gainGold(1000 * day);
}
if(hours >= 1) {
  $gameParty.gainGold(200 * hours);
}
if(mins >= 30) {
  $gameParty.gainGold(100 * mins);
}
if(seconds >= 45) {
  $gameParty.gainGold(50 * seconds);
}
        let day = Math.floor(loadTime / dayValue) - Math.floor((beforeTime - newBeforeTime.getTimezoneOffset() * 60 * 1000) / dayValue );
        let hours = (Math.floor(loadTime / hourValue) - Math.floor(beforeTime/ hourValue )) % 24;
        let mins = (Math.floor(loadTime / minValue) - Math.floor(beforeTime / minValue )) % 60;
        let seconds = (Math.floor(loadTime / secondValue) - Math.floor(beforeTime / secondValue)) % 60;

        let text = `${day} ${$.Params.dayText} ${hours} ${$.Params.hoursText} ${mins} ${$.Params.minutesText} ${seconds} ${$.Params.secondsText} ${$.Params.descText}`;

        this._timeChecker.bitmap.clear();
        this._timeChecker.visible = true;
        this._timeChecker.bitmap.drawText(text, 0, 0, width, height, $.Params.font.textAlign);
        this._timeChecker.bitmap.fontFace = $.Params.font.fontFace;
        this._timeChecker.bitmap.fontSize = $.Params.font.fontSize;
        this._timeChecker.bitmap.textColor = $.Params.font.textColor;
        this._timeChecker.bitmap.outlineColor = $.Params.font.outlineColor;
        this._timeChecker.bitmap.outlineWidth = $.Params.font.outlineWidth;
        this._timeChecker.x = eval($.Params.dx);
        this._timeChecker.y = eval($.Params.dy);

        try {
            eval(JSON.parse($.Params.evaluateCode));
        } catch(e) {
            throw new Error(e.message);
        }

        setTimeout(() => {
            this._timeChecker.visible = false;
        }, 1000 * $.Params.hideTime);

    }

})(RS.LoadTimeChecker);<commit_msg>remove incorrect code.<commit_after>/*:
 * @plugindesc <RS_LoadTimeChecker>
 * @author biud436
 *
 * @param Text Option
 *
 * @param Day
 * @parent Text Option
 * @desc Specify the 'Day' text to show up on the screen.
 * @default 일
 *
 * @param Hours
 * @parent Text Option
 * @desc Specify the 'Hours' text to show up on the screen.
 * @default 시간
 *
 * @param Minutes
 * @parent Text Option
 * @desc Specify the 'Minutes' text to show up on the screen.
 * @default 분
 *
 * @param Seconds
 * @parent Text Option
 * @desc Specify the 'Seconds' text to show up on the screen.
 * @default 초
 *
 * @param Description Text
 * @parent Text Option
 * @desc Specify the description text to show up on the screen.
 * @default 만에 게임으로 돌아오셨습니다.
 *
 * @param Hide Time
 * @parent Text Option
 * @type number
 * @desc Specify the hide seconds.
 * @default 6
 *
 * @param Position
 *
 * @param X
 * @parent Position
 * @desc Specify the position.
 * (This is evaluated the javascript)
 * @default 0
 *
 * @param Y
 * @parent Position
 * @desc Specify the position of the text.
 * (This is evaluated the javascript)
 * @default 0
 *
 * @param Font Option
 *
 * @param Font Face
 * @parent Font Option
 * @desc Specify the font face.
 * @default GameFont
 *
 * @param Font Size
 * @parent Font Option
 * @type number
 * @desc Specify the font size.
 * @default 28
 *
 * @param Text Color
 * @parent Font Option
 * @desc Specify the text color.
 * @default #ffffff
 *
 * @param Outline Color
 * @parent Font Option
 * @desc Specify the outline color.
 * @default rgba(0, 0, 0, 0.5)
 *
 * @param Outline Width
 * @parent Font Option
 * @type number
 * @desc Specify the outline width.
 * @default 4
 *
 * @param Text Align
 * @parent Font Option
 * @type select
 * @desc Specify the text alignment.
 * @default center
 * @option left
 * @value left
 * @option cetner
 * @value center
 * @option right
 * @value right
 *
 * @param Bitmap Width
 * @parent Font Option
 * @desc Specify the bitmap width.
 * (This is evaluated the javascript)
 * @default Graphics.boxWidth
 *
 * @param Bitmap Height
 * @parent Font Option
 * @desc Specify the bitmap height.
 * (This is evaluated the javascript)
 * @default 64
 *
 * @param Reward Option
 *
 * @param Code Injection
 * @parent Reward Option
 * @type note
 * @desc Can evaluate a javascript code using day, hours, mins, seconds variables.
 * @default "// if(day >= 1) {\n//   $gameParty.gainGold(1000 * day);\n// }\n// if(hours >= 1) {\n//   $gameParty.gainGold(200 * hours);\n// }\n// if(mins >= 30) {\n//   $gameParty.gainGold(100 * mins);\n// }\n// if(seconds >= 45) {\n//   $gameParty.gainGold(50 * seconds);\n// }"
 *
 * @help
 * ========================================================
 * Introduction
 * ========================================================
 * This plugin allows to indicate as the time text on the screen when loaded a save file.
 * The time text is that how long it took the amount of time since last save time.
 *
 * ========================================================
 * Change Log
 * ========================================================
 * 2019.07.13 (v1.0.0) - First Release.
 */

var Imported = Imported || {};
Imported.RS_LoadTimeChecker = true;

var RS = RS || {};
RS.LoadTimeChecker = RS.LoadTimeChecker || {};

(function($) {

    var parameters = $plugins.filter(function (i) {
      return i.description.contains('<RS_LoadTimeChecker>');
    });

    parameters = (parameters.length > 0) && parameters[0].parameters;

    $.Params = {};
    $.Params.dayText = parameters["Day"] || "일";
    $.Params.hoursText = parameters["Hours"] || "시간";
    $.Params.minutesText = parameters["Minutes"] || "분";
    $.Params.secondsText = parameters["Seconds"] || "초";
    $.Params.descText = parameters["Description Text"];

    $.Params.hideTime = parseInt(parameters["Hide Time"] || 6);
    $.Params.dx = parameters["X"];
    $.Params.dy = parameters["Y"];

    $.Params.font = {
        fontFace: parameters["Font Face"] || "GameFont",
        fontSize: Number(parameters["Font Size"] || 28),
        textColor: parameters["Text Color"] || "#ffffff",
        outlineColor: parameters["Outline Color"] || "rgba(0, 0, 0, 0.5)",
        outlineWidth: Number(parameters["Outline Width"] || 4),
        textAlign: parameters["Text Align"] || "center",
    };

    $.Params.bitmap = {
        width: parameters["Bitmap Width"],
        height: parameters["Bitmap Height"],
    };

    $.Params.evaluateCode = parameters["Code Injection"];

    var alias_Game_System_initialize = Game_System.prototype.initialize;
    Game_System.prototype.initialize = function() {
        alias_Game_System_initialize.call(this);
        this._beforeDateTime = null;
        this._loadDateTime = null;
        this._refreshTime = false;
    };

    var alias_Game_System_onBeforeSave = Game_System.prototype.onBeforeSave;
    Game_System.prototype.onBeforeSave = function() {
        alias_Game_System_onBeforeSave.call(this);
        this._beforeDateTime = new Date().getTime();
    };

    var alias_Game_System_onAfterLoad = Game_System.prototype.onAfterLoad;
    Game_System.prototype.onAfterLoad = function() {
        alias_Game_System_onAfterLoad.call(this);
        this._loadDateTime = new Date().getTime();
        this.refreshTimeChecker();
    };

    Game_System.prototype.beforeTime = function() {
        return this._beforeDateTime;
    };

    Game_System.prototype.loadTime = function() {
        return this._loadDateTime;
    };

    Game_System.prototype.refreshTimeChecker = function() {
        this._refreshTime = true;
    };

    Game_System.prototype.restoreTimeChecker = function() {
        this._refreshTime = false;
    };

    var alias_Scene_Map_onMapLoaded = Scene_Map.prototype.onMapLoaded;
    Scene_Map.prototype.onMapLoaded = function() {
        alias_Scene_Map_onMapLoaded.call(this);
        if($gameSystem._refreshTime) {
            this.refreshTimeChecker($gameSystem.beforeTime(), $gameSystem.loadTime());
            $gameSystem.restoreTimeChecker();
        }
    };

    Scene_Map.prototype.refreshTimeChecker = function(before, load) {
        if(!this._spriteset) return;
        this._spriteset.refreshTimeChecker(before, load);
    };

    var alias_Spriteset_Map_createLowerLayer = Spriteset_Map.prototype.createLowerLayer;
    Spriteset_Map.prototype.createLowerLayer = function() {
        alias_Spriteset_Map_createLowerLayer.call(this);
        this.createTimeChecker();
    };

    Spriteset_Map.prototype.createTimeChecker = function() {
        this._timeChecker = new Sprite();
        var width = eval($.Params.bitmap.width);
        var height = eval($.Params.bitmap.height);
        this._timeChecker.bitmap = new Bitmap(width, height);
        this._timeChecker.visible = false;
        this.addChild(this._timeChecker);
    };

    /**
     * @param {Date} before
     * @param {Date} load
     */
    Spriteset_Map.prototype.refreshTimeChecker = function(before, load) {

        "use strict";

        if(!this._timeChecker) {
            this.createTimeChecker();
        }

        var newBeforeTime = new Date(before);
        var newLoadTime = new Date(load);

        const width = eval($.Params.bitmap.width);
        const height = eval($.Params.bitmap.height);

        const dayValue = 60 * 60 * 24 * 1000;
        const hourValue = 60 * 60 * 1000;
        const minValue = 60 * 1000;
        const secondValue = 1000;

        let loadTime = newLoadTime.getTime();
        let beforeTime = newBeforeTime.getTime();

        let day = Math.floor(loadTime / dayValue) - Math.floor((beforeTime - newBeforeTime.getTimezoneOffset() * 60 * 1000) / dayValue );
        let hours = (Math.floor(loadTime / hourValue) - Math.floor(beforeTime/ hourValue )) % 24;
        let mins = (Math.floor(loadTime / minValue) - Math.floor(beforeTime / minValue )) % 60;
        let seconds = (Math.floor(loadTime / secondValue) - Math.floor(beforeTime / secondValue)) % 60;

        let text = `${day} ${$.Params.dayText} ${hours} ${$.Params.hoursText} ${mins} ${$.Params.minutesText} ${seconds} ${$.Params.secondsText} ${$.Params.descText}`;

        this._timeChecker.bitmap.clear();
        this._timeChecker.visible = true;
        this._timeChecker.bitmap.drawText(text, 0, 0, width, height, $.Params.font.textAlign);
        this._timeChecker.bitmap.fontFace = $.Params.font.fontFace;
        this._timeChecker.bitmap.fontSize = $.Params.font.fontSize;
        this._timeChecker.bitmap.textColor = $.Params.font.textColor;
        this._timeChecker.bitmap.outlineColor = $.Params.font.outlineColor;
        this._timeChecker.bitmap.outlineWidth = $.Params.font.outlineWidth;
        this._timeChecker.x = eval($.Params.dx);
        this._timeChecker.y = eval($.Params.dy);

        try {
            eval(JSON.parse($.Params.evaluateCode));
        } catch(e) {
            throw new Error(e.message);
        }

        setTimeout(() => {
            this._timeChecker.visible = false;
        }, 1000 * $.Params.hideTime);

    }

})(RS.LoadTimeChecker);<|endoftext|>


<commit_before>/**
 * Dependencies
 */

var test = require('tape')
var Store = require('../')

/**
 * Tests
 */

test('Store()', function(assert) {
  var store = new Store()
  assert.ok(store instanceof Store, 'should be an instance')
  assert.deepEqual(store.get(), {})
  assert.end()
})

test('Store(obj)', function(t) {
  t.test('.set(key, value)', function(assert) {
    var store = new Store({ foo: 'bar' })

    store.on('set', function(key, value) {
      assert.pass('should emit')
      assert.equal(key, 'baz')
      assert.equal(value, 'qux')
    })

    var value = store.set('baz', 'qux')

    assert.equal(value, 'qux')
    assert.end()
  })

  t.test('.get([key])', function(assert) {
    var store = new Store({ foo: 'bar', baz: 'qux' })

    store.on('get', function(key, value) {
      assert.pass('should emit')
      assert.equal(key, 'foo')
      assert.equal(value, 'bar')
    })

    var values = store.get()
    var value = store.get('foo')

    assert.deepEqual(values, { foo: 'bar', baz: 'qux' })
    assert.deepEqual(value, 'bar')
    assert.end()
  })

  t.test('.del(key)', function(assert) {
    var store = new Store({ foo: 'bar', baz: 'qux' })

    store.on('del', function(key, value) {
      assert.pass('should emit')
      assert.equal(key, 'foo')
      assert.equal(value, 'bar')
    })

    var deleted = store.del('foo')
    var values = store.get()
    var value = store.get('foo')

    assert.equal(deleted, 'bar')
    assert.deepEqual(values, { baz: 'qux' })
    assert.equal(value, undefined)
    assert.end()
  })

  t.test('.close()', function(assert) {
    var store = new Store({ foo: 'bar' })

    store.on('close', function(value) {
      assert.pass('should emit')
      assert.deepEqual(value, { foo: 'bar' })
    })

    var closed = store.close()
    var values = store.get()

    assert.deepEqual(closed, { foo: 'bar' })
    assert.equal(values, undefined)
    assert.end()
  })
})

test('Store(arr)', function(t) {
  t.test('.set(value)', function(assert) {
    var store = new Store(['foo'])

    store.on('set', function(key, value) {
      assert.pass('should emit')
      assert.equal(key, 1)
      assert.equal(value, 'bar')
    })

    var value = store.set('bar')

    assert.equal(value, 'bar')
    assert.end()
  })

  t.test('.get([key])', function(assert) {
    var store = new Store(['foo', 'bar'])

    store.on('get', function(key, value) {
      assert.pass('should emit')
      assert.equal(key, 0)
      assert.equal(value, 'foo')
    })

    var values = store.get()
    var value = store.get(0)

    assert.deepEqual(values, ['foo', 'bar'])
    assert.equal(value, 'foo')
    assert.end()
  })

  t.test('.del(key)', function(assert) {
    var store = new Store(['foo', 'bar'])

    store.on('del', function(key, value) {
      assert.pass('should emit')
      assert.equal(key, 0)
      assert.equal(value, 'foo')
    })

    var deleted = store.del(0)
    var values = store.get()
    var value = store.get(0)

    assert.equal(deleted, 'foo')
    assert.deepEqual(values, ['bar'])
    assert.equal(value, 'bar')
    assert.end()
  })

  t.test('.close()', function(assert) {
    var store = new Store(['foo'])

    store.on('close', function(value) {
      assert.pass('should emit')
      assert.deepEqual(value, ['foo'])
    })

    var closed = store.close()
    var values = store.get()

    assert.deepEqual(closed, ['foo'])
    assert.equal(values, undefined)
    assert.end()
  })
})
<commit_msg>Use question marks.<commit_after>/**
 * Dependencies
 */

var test = require('tape')
var Store = require('../')

/**
 * Tests
 */

test('Store()', function(assert) {
  var store = new Store()
  assert.ok(store instanceof Store, 'should be an instance')
  assert.deepEqual(store.get(), {})
  assert.end()
})

test('Store(obj)', function(t) {
  t.test('.set(key, value)', function(assert) {
    var store = new Store({ foo: 'bar' })

    store.on('set', function(key, value) {
      assert.pass('should emit')
      assert.equal(key, 'baz')
      assert.equal(value, 'qux')
    })

    var value = store.set('baz', 'qux')

    assert.equal(value, 'qux')
    assert.end()
  })

  t.test('.get(key?)', function(assert) {
    var store = new Store({ foo: 'bar', baz: 'qux' })

    store.on('get', function(key, value) {
      assert.pass('should emit')
      assert.equal(key, 'foo')
      assert.equal(value, 'bar')
    })

    var values = store.get()
    var value = store.get('foo')

    assert.deepEqual(values, { foo: 'bar', baz: 'qux' })
    assert.deepEqual(value, 'bar')
    assert.end()
  })

  t.test('.del(key)', function(assert) {
    var store = new Store({ foo: 'bar', baz: 'qux' })

    store.on('del', function(key, value) {
      assert.pass('should emit')
      assert.equal(key, 'foo')
      assert.equal(value, 'bar')
    })

    var deleted = store.del('foo')
    var values = store.get()
    var value = store.get('foo')

    assert.equal(deleted, 'bar')
    assert.deepEqual(values, { baz: 'qux' })
    assert.equal(value, undefined)
    assert.end()
  })

  t.test('.close()', function(assert) {
    var store = new Store({ foo: 'bar' })

    store.on('close', function(value) {
      assert.pass('should emit')
      assert.deepEqual(value, { foo: 'bar' })
    })

    var closed = store.close()
    var values = store.get()

    assert.deepEqual(closed, { foo: 'bar' })
    assert.equal(values, undefined)
    assert.end()
  })
})

test('Store(arr)', function(t) {
  t.test('.set(value)', function(assert) {
    var store = new Store(['foo'])

    store.on('set', function(key, value) {
      assert.pass('should emit')
      assert.equal(key, 1)
      assert.equal(value, 'bar')
    })

    var value = store.set('bar')

    assert.equal(value, 'bar')
    assert.end()
  })

  t.test('.get(key?)', function(assert) {
    var store = new Store(['foo', 'bar'])

    store.on('get', function(key, value) {
      assert.pass('should emit')
      assert.equal(key, 0)
      assert.equal(value, 'foo')
    })

    var values = store.get()
    var value = store.get(0)

    assert.deepEqual(values, ['foo', 'bar'])
    assert.equal(value, 'foo')
    assert.end()
  })

  t.test('.del(key)', function(assert) {
    var store = new Store(['foo', 'bar'])

    store.on('del', function(key, value) {
      assert.pass('should emit')
      assert.equal(key, 0)
      assert.equal(value, 'foo')
    })

    var deleted = store.del(0)
    var values = store.get()
    var value = store.get(0)

    assert.equal(deleted, 'foo')
    assert.deepEqual(values, ['bar'])
    assert.equal(value, 'bar')
    assert.end()
  })

  t.test('.close()', function(assert) {
    var store = new Store(['foo'])

    store.on('close', function(value) {
      assert.pass('should emit')
      assert.deepEqual(value, ['foo'])
    })

    var closed = store.close()
    var values = store.get()

    assert.deepEqual(closed, ['foo'])
    assert.equal(values, undefined)
    assert.end()
  })
})
<|endoftext|>


<commit_before>// SHA-1 vectors generated by Node.js
// SHA-256/SHA-512 test vectors from:
// https://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors
// https://stackoverflow.com/questions/15593184/pbkdf2-hmac-sha-512-test-vectors
var fixtures = require('./fixtures')
var tape = require('tape')

var pVersionMajor = parseInt(process.version.split('.')[0].slice(1), 10)
if (pVersionMajor !== 4 || process.browser) {
  fixtures.invalid.push({
    'key': 'password',
    'salt': 'salt',
    'iterations': 1,
    'dkLen': -1,
    'exception': 'Bad key length'
  }, {
    'key': 'password',
    'salt': 'salt',
    'iterations': 1,
    'dkLen': 4073741824,
    'exception': 'Bad key length'
  })
}

if (pVersionMajor >= 5 || process.browser) {
  fixtures.invalid.push({
    'key': 'password',
    'salt': 'salt',
    'iterations': 1,
    'dkLen': NaN,
    'exception': 'Bad key length'
  }, {
    'key': 'password',
    'salt': 'salt',
    'iterations': 1,
    'dkLen': Infinity,
    'exception': 'Bad key length'
  })
}

if (pVersionMajor >= 6 || process.browser) {
  fixtures.valid.push({
    'description': 'Unicode salt, no truncation',
    'key': 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
    'salt': 'mnemonicメートルガバヴァぱばぐゞちぢ十人十色',
    'iterations': 2048,
    'dkLen': 64,
    'results': {
      'sha1': '7e042a2f41ba17e2235fbc794e22a150816b0f54a1dfe113919fccb7a056066a109385e538f183c92bad896ae8b7d8e0f4cd66df359c77c8c7785cd1001c9a2c',
      'sha256': '0b57118f2b6b079d9371c94da3a8315c3ada87a1e819b40c4c4e90b36ff2d3c8fd7555538b5119ac4d3da7844aa4259d92f9dd2188e78ac33c4b08d8e6b5964b',
      'sha512': 'ba553eedefe76e67e2602dc20184c564010859faada929a090dd2c57aacb204ceefd15404ab50ef3e8dbeae5195aeae64b0def4d2eead1cdc728a33ced520ffd',
      'sha224': 'd76474c525616ce2a527d23df8d6f6fcc4251cc3535cae4e955810a51ead1ec6acbe9c9619187ca5a3c4fd636de5b5fe58d031714731290bbc081dbf0fcb8fc1',
      'sha384': '15010450f456769467e834db7fa93dd9d353e8bb733b63b0621090f96599ac3316908eb64ac9366094f0787cd4bfb2fea25be41dc271a19309710db6144f9b34'
    }
  })

// 'binary' behaviour, Node 6 defaulted to 'utf-8'
} else {
  fixtures.valid.push({
    'description': 'Unicode salt, suffers from truncation',
    'key': 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
    'salt': 'mnemonicメートルガバヴァぱばぐゞちぢ十人十色',
    'iterations': 2048,
    'dkLen': 64,
    'results': {
      'sha1': 'd85d14adcb7bdb5d976160e504f520a98cf71aca4cd5fceadf37759743bd6e1d2ff78bdd4403552aef7658094384b341ede80fffd334182be076f9d988a0a40f',
      'sha256': 'b86b5b900c29ed2724359afd793e10ffc1eb0e7d6f624fc9c85b8ac1785d9a2f0575af52a2338e611f2e6cffdee544adfff6f3d4f43be2ba0e2bd7e917b38a14',
      'sha512': '3a863fa00f2e97a83fa9b18805e0047a6282cbae0ff48438b33a14475771c52d05137daa12e364cb34d84547ac07568b801c5c7f8dd4baaeee18a67a5c6a3377',
      'sha224': '95727793842437774ad9ae27b8154a6f37f208b75a03d3a4d4a2443422bb6bc85efcfa92aa4376926ea89a8f5a63118eecdb58c8ca28ab31007da79437e0a1ef',
      'sha384': '1a7e02e8ba0e357269a55642024b85738b95238d6cdc49bc440204995aefeff499e22cba76d4c7e96b7d4a9596a70e744f53fa94f3547e7dc506fcaf16ceb4a2'
    }
  })
}

function runTests (name, compat) {
  tape(name + ' defaults to sha1 and handles buffers', function (t) {
    t.plan(2)

    compat.pbkdf2(new Buffer('password'), new Buffer('salt'), 1, 32, function (err, result) {
      t.error(err)
      t.equal(result.toString('hex'), '0c60c80f961f0e71f3a9b524af6012062fe037a6e0f0eb94fe8fc46bdc637164')
    })
  })

  tape(name + ' should throw if no callback is provided', function (t) {
    t.plan(1)

    t.throws(function () {
      compat.pbkdf2('password', 'salt', 1, 32, 'sha1')
    }, /No callback provided to pbkdf2/)
  })

  var algos = ['sha1', 'sha224', 'sha256', 'sha384', 'sha512']
  algos.forEach(function (algorithm) {
    fixtures.valid.forEach(function (f) {
      var key = f.key || new Buffer(f.keyHex, 'hex')
      var salt = f.salt || new Buffer(f.saltHex, 'hex')
      var expected = f.results[algorithm]

      tape('pbkdf2 w/ ' + algorithm + ' encodes ' + key + ' (' + f.salt + ') with ' + algorithm + ' to ' + expected, function (t) {
        t.plan(2)

        compat.pbkdf2(key, salt, f.iterations, f.dkLen, algorithm, function (err, result) {
          t.error(err)
          t.equal(result.toString('hex'), expected)
        })
      })
    })

    fixtures.invalid.forEach(function (f) {
      tape('pbkdf2 w/ ' + algorithm + ' should throw ' + f.exception, function (t) {
        t.plan(1)

        t.throws(function () {
          compat.pbkdf2(f.key, f.salt, f.iterations, f.dkLen, f.algo, function () {})
        }, new RegExp(f.exception))
      })
    })

    fixtures.valid.forEach(function (f) {
      var key = f.key || new Buffer(f.keyHex, 'hex')
      var salt = f.salt || new Buffer(f.saltHex, 'hex')
      var expected = f.results[algorithm]

      tape('pbkdf2Sync w/ ' + algorithm + ' encodes ' + key + ' (' + f.salt + ') with ' + algorithm + ' to ' + expected, function (t) {
        t.plan(1)

        var result = compat.pbkdf2Sync(key, salt, f.iterations, f.dkLen, algorithm)
        t.equal(result.toString('hex'), expected)
      })
    })

    fixtures.invalid.forEach(function (f) {
      tape('pbkdf2Sync w/' + algorithm + ' should throw ' + f.exception, function (t) {
        t.plan(1)
        t.throws(function () {
          compat.pbkdf2Sync(f.key, f.salt, f.iterations, f.dkLen, f.algo)
        }, new RegExp(f.exception))
      })
    })
  })

  tape('pbkdf2Sync defaults to sha1', function (t) {
    t.plan(1)

    var result = compat.pbkdf2Sync('password', 'salt', 1, 32)
    t.equal(result.toString('hex'), '0c60c80f961f0e71f3a9b524af6012062fe037a6e0f0eb94fe8fc46bdc637164')
  })
}

runTests('JavaScript pbkdf2', require('../browser'))
if (!process.browser) {
  runTests('node pbkdf2', require('../'))
}
<commit_msg>tests: merge to avoid double up of code<commit_after>// SHA-1 vectors generated by Node.js
// SHA-256/SHA-512 test vectors from:
// https://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors
// https://stackoverflow.com/questions/15593184/pbkdf2-hmac-sha-512-test-vectors
var fixtures = require('./fixtures')
var tape = require('tape')

var pVersionMajor = parseInt(process.version.split('.')[0].slice(1), 10)
if (pVersionMajor !== 4 || process.browser) {
  fixtures.invalid.push({
    'key': 'password',
    'salt': 'salt',
    'iterations': 1,
    'dkLen': -1,
    'exception': 'Bad key length'
  }, {
    'key': 'password',
    'salt': 'salt',
    'iterations': 1,
    'dkLen': 4073741824,
    'exception': 'Bad key length'
  })
}

if (pVersionMajor >= 5 || process.browser) {
  fixtures.invalid.push({
    'key': 'password',
    'salt': 'salt',
    'iterations': 1,
    'dkLen': NaN,
    'exception': 'Bad key length'
  }, {
    'key': 'password',
    'salt': 'salt',
    'iterations': 1,
    'dkLen': Infinity,
    'exception': 'Bad key length'
  })
}

if (pVersionMajor >= 6 || process.browser) {
  fixtures.valid.push({
    'description': 'Unicode salt, no truncation',
    'key': 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
    'salt': 'mnemonicメートルガバヴァぱばぐゞちぢ十人十色',
    'iterations': 2048,
    'dkLen': 64,
    'results': {
      'sha1': '7e042a2f41ba17e2235fbc794e22a150816b0f54a1dfe113919fccb7a056066a109385e538f183c92bad896ae8b7d8e0f4cd66df359c77c8c7785cd1001c9a2c',
      'sha256': '0b57118f2b6b079d9371c94da3a8315c3ada87a1e819b40c4c4e90b36ff2d3c8fd7555538b5119ac4d3da7844aa4259d92f9dd2188e78ac33c4b08d8e6b5964b',
      'sha512': 'ba553eedefe76e67e2602dc20184c564010859faada929a090dd2c57aacb204ceefd15404ab50ef3e8dbeae5195aeae64b0def4d2eead1cdc728a33ced520ffd',
      'sha224': 'd76474c525616ce2a527d23df8d6f6fcc4251cc3535cae4e955810a51ead1ec6acbe9c9619187ca5a3c4fd636de5b5fe58d031714731290bbc081dbf0fcb8fc1',
      'sha384': '15010450f456769467e834db7fa93dd9d353e8bb733b63b0621090f96599ac3316908eb64ac9366094f0787cd4bfb2fea25be41dc271a19309710db6144f9b34'
    }
  })

// 'binary' behaviour, Node 6 defaulted to 'utf-8'
} else {
  fixtures.valid.push({
    'description': 'Unicode salt, suffers from truncation',
    'key': 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
    'salt': 'mnemonicメートルガバヴァぱばぐゞちぢ十人十色',
    'iterations': 2048,
    'dkLen': 64,
    'results': {
      'sha1': 'd85d14adcb7bdb5d976160e504f520a98cf71aca4cd5fceadf37759743bd6e1d2ff78bdd4403552aef7658094384b341ede80fffd334182be076f9d988a0a40f',
      'sha256': 'b86b5b900c29ed2724359afd793e10ffc1eb0e7d6f624fc9c85b8ac1785d9a2f0575af52a2338e611f2e6cffdee544adfff6f3d4f43be2ba0e2bd7e917b38a14',
      'sha512': '3a863fa00f2e97a83fa9b18805e0047a6282cbae0ff48438b33a14475771c52d05137daa12e364cb34d84547ac07568b801c5c7f8dd4baaeee18a67a5c6a3377',
      'sha224': '95727793842437774ad9ae27b8154a6f37f208b75a03d3a4d4a2443422bb6bc85efcfa92aa4376926ea89a8f5a63118eecdb58c8ca28ab31007da79437e0a1ef',
      'sha384': '1a7e02e8ba0e357269a55642024b85738b95238d6cdc49bc440204995aefeff499e22cba76d4c7e96b7d4a9596a70e744f53fa94f3547e7dc506fcaf16ceb4a2'
    }
  })
}

function runTests (name, compat) {
  tape(name + ' defaults to sha1 and handles buffers', function (t) {
    t.plan(2)

    compat.pbkdf2(new Buffer('password'), new Buffer('salt'), 1, 32, function (err, result) {
      t.error(err)
      t.equal(result.toString('hex'), '0c60c80f961f0e71f3a9b524af6012062fe037a6e0f0eb94fe8fc46bdc637164')
    })
  })

  tape(name + ' should throw if no callback is provided', function (t) {
    t.plan(1)

    t.throws(function () {
      compat.pbkdf2('password', 'salt', 1, 32, 'sha1')
    }, /No callback provided to pbkdf2/)
  })

  var algos = ['sha1', 'sha224', 'sha256', 'sha384', 'sha512']
  algos.forEach(function (algorithm) {
    fixtures.valid.forEach(function (f) {
      var key = f.key || new Buffer(f.keyHex, 'hex')
      var salt = f.salt || new Buffer(f.saltHex, 'hex')
      var expected = f.results[algorithm]
      var description = algorithm + ' encodes ' + key + ' (' + f.salt + ') with ' + algorithm + ' to ' + expected

      tape(name + ' async w/ ' + description, function (t) {
        t.plan(2)

        compat.pbkdf2(key, salt, f.iterations, f.dkLen, algorithm, function (err, result) {
          t.error(err)
          t.equal(result.toString('hex'), expected)
        })
      })

      tape(name + 'sync w/ ' + description, function (t) {
        t.plan(1)

        var result = compat.pbkdf2Sync(key, salt, f.iterations, f.dkLen, algorithm)
        t.equal(result.toString('hex'), expected)
      })
    })

    fixtures.invalid.forEach(function (f) {
      var description = algorithm + ' should throw ' + f.exception

      tape(name + ' async w/ ' + description, function (t) {
        t.plan(1)

        t.throws(function () {
          compat.pbkdf2(f.key, f.salt, f.iterations, f.dkLen, f.algo, function () {})
        }, new RegExp(f.exception))
      })

      tape(name + ' sync w/' + description, function (t) {
        t.plan(1)

        t.throws(function () {
          compat.pbkdf2Sync(f.key, f.salt, f.iterations, f.dkLen, f.algo)
        }, new RegExp(f.exception))
      })
    })
  })

  tape('pbkdf2Sync defaults to sha1', function (t) {
    t.plan(1)

    var result = compat.pbkdf2Sync('password', 'salt', 1, 32)
    t.equal(result.toString('hex'), '0c60c80f961f0e71f3a9b524af6012062fe037a6e0f0eb94fe8fc46bdc637164')
  })
}

runTests('JavaScript pbkdf2', require('../browser'))
if (!process.browser) {
  runTests('node pbkdf2', require('../'))
}
<|endoftext|>


<commit_before>//
// JMVC Controller to display and manipulate apps associated with an account
// on the SMArt bootstrap.
// Josh Mandel (joshua.mandel@childrens.harvard.edu)

PHAController= MVC.Controller.extend('pha', {

setup: function(params){
    PHA.get_for_account(ACCOUNT_ID, function(phas) {
    	PHAController.enabled_phas = phas;
    	PHAController.dispatch("draw_phas");

    	var enabled_phas = phas;
        var enabled_pha_ids = $.map(enabled_phas, function(e){return e.id;})
        PHA.get_all(function(phas) {

        	var disabled_phas = [];

        	$.each(phas, function(i,pha) {
        		if ($.inArray(pha.id, enabled_pha_ids) == -1) {
        			disabled_phas.push(pha);
        		}
        	});

        	disabled_phas.sort(PHA.compare);

        	PHAController.phas = phas;
        	PHAController.enabled_phas = enabled_phas;
        	PHAController.disabled_phas = disabled_phas;
        });
    });
},

 index: function(params) {
		RecordController.APP_ID = null;
		var _this = this;

    	this.render({to: 'app_content',
    				  using: {enabled_phas: PHAController.enabled_phas,
    						  disabled_phas: PHAController.disabled_phas}});

        $('#app_content_iframe').hide();
        $('#app_content').show();

        $(".manage_apps BUTTON").click(function() {
        	var command = $(this).attr('id');
        	var app = null;

        	if (command.indexOf("remove_app_") >-1) {
        		app = command.split("remove_app_")[1];
        		command = "DELETE";
        		var x = "disabled_phas";
        		var y = "enabled_phas";
        	}
        	else if (command.indexOf("add_app_")>-1) {
        		app = command.split("add_app_")[1];
        		command = "PUT";
        		var y = "disabled_phas";
        		var x = "enabled_phas";
        	}
        	else {
        		throw "Expecting add or remove command, got " + command;
        	}

        	app = $.grep(PHAController.phas, function(pha) {return (pha.safeid() === app);})[0];

        	PHAController[y] = $.grep(PHAController[y], function(pha) {return (pha.id !== app.id)});
        	PHAController[x].push(app);
        	PHAController[x].sort(PHA.compare);

        	PHA.manipulate(command, ACCOUNT_ID, app.id, function()
        	{
        		PHAController.dispatch('index');
        		PHAController.dispatch('draw_phas');
        	});
        });
},

_add_app: function(params) {
    // could use some refactoring, no?
    var pha = params.pha;
    var fire_p = params.fire_p;

    // add the tab, save the length-1 as index for removal closure
//    $('#app_selector').tabs('add', '#'+pha.safeid(), pha.data.name);
//    var len = $('#app_selector').tabs('length');

	$('#app_selector_inner').append('
'+pha.data.name+'
');//('add', '#'+pha.safeid(), pha.data.name);
    // Create a
 for each pha, with maybe an image
    var img_name =pha.safename();
    var line = '';

    $('#app_selector_inner li:last').addClass('app');
    if (!RecordController.CURRENT_RECORD)
        $('#app_selector_inner li:last').addClass('greyed_out');

    $('#app_selector_inner li:last A').prepend(line);

},
hash_change: function(event) {
	var app_id = event.value.match(/app\/(.*)/)[1];
	var app = $.grep(PHAController.phas, function(pha) {return (pha.safeid() === app_id);})[0];
	PHAController.dispatch('launch_app', app);
},
launch_app: function(pha) {

    var interpolation_args = {
    		  'record_id' : RecordController.RECORD_ID,
    		  'account_id' : ACCOUNT_ID
    		  };

  	  startURL = interpolate_url_template(pha.data.startURLTemplate, interpolation_args);
  	  RecordController.APP_ID = pha.id;

  	  SMART.register_app(	pha.id,
  			  				$('#app_content_iframe')[0],
  			  				startURL);

  	  SMART_HELPER.launch_app(
  			pha,
  			ACCOUNT_ID,
  			RecordController.CURRENT_RECORD.record_id,
  	  		function() {
  				// load and show the iframe
  				$('#app_content').hide();
  				$('#app_content_iframe').attr('src',  startURL);
  			});
},

draw_phas :function() {
	$('.app').remove();
    jQuery.each(PHAController.enabled_phas, function(i,v){
        PHAController.dispatch('_add_app', {'pha': v, 'fire_p': false})
    });
},

});
<commit_msg>prompt patient choice before app launch<commit_after>//
// JMVC Controller to display and manipulate apps associated with an account
// on the SMArt bootstrap.
// Josh Mandel (joshua.mandel@childrens.harvard.edu)

PHAController= MVC.Controller.extend('pha', {

setup: function(params){
    PHA.get_for_account(ACCOUNT_ID, function(phas) {
    	PHAController.enabled_phas = phas;
    	PHAController.dispatch("draw_phas");

    	var enabled_phas = phas;
        var enabled_pha_ids = $.map(enabled_phas, function(e){return e.id;})
        PHA.get_all(function(phas) {

        	var disabled_phas = [];

        	$.each(phas, function(i,pha) {
        		if ($.inArray(pha.id, enabled_pha_ids) == -1) {
        			disabled_phas.push(pha);
        		}
        	});

        	disabled_phas.sort(PHA.compare);

        	PHAController.phas = phas;
        	PHAController.enabled_phas = enabled_phas;
        	PHAController.disabled_phas = disabled_phas;
        });
    });
},

 index: function(params) {
		RecordController.APP_ID = null;
		var _this = this;

    	this.render({to: 'app_content',
    				  using: {enabled_phas: PHAController.enabled_phas,
    						  disabled_phas: PHAController.disabled_phas}});

        $('#app_content_iframe').hide();
        $('#app_content').show();

        $(".manage_apps BUTTON").click(function() {
        	var command = $(this).attr('id');
        	var app = null;

        	if (command.indexOf("remove_app_") >-1) {
        		app = command.split("remove_app_")[1];
        		command = "DELETE";
        		var x = "disabled_phas";
        		var y = "enabled_phas";
        	}
        	else if (command.indexOf("add_app_")>-1) {
        		app = command.split("add_app_")[1];
        		command = "PUT";
        		var y = "disabled_phas";
        		var x = "enabled_phas";
        	}
        	else {
        		throw "Expecting add or remove command, got " + command;
        	}

        	app = $.grep(PHAController.phas, function(pha) {return (pha.safeid() === app);})[0];

        	PHAController[y] = $.grep(PHAController[y], function(pha) {return (pha.id !== app.id)});
        	PHAController[x].push(app);
        	PHAController[x].sort(PHA.compare);

        	PHA.manipulate(command, ACCOUNT_ID, app.id, function()
        	{
        		PHAController.dispatch('index');
        		PHAController.dispatch('draw_phas');
        	});
        });
},

_add_app: function(params) {
    // could use some refactoring, no?
    var pha = params.pha;
    var fire_p = params.fire_p;

    // add the tab, save the length-1 as index for removal closure
//    $('#app_selector').tabs('add', '#'+pha.safeid(), pha.data.name);
//    var len = $('#app_selector').tabs('length');

	$('#app_selector_inner').append('
'+pha.data.name+'
');//('add', '#'+pha.safeid(), pha.data.name);
    // Create a
 for each pha, with maybe an image
    var img_name =pha.safename();
    var line = '';

    $('#app_selector_inner li:last').addClass('app');
    if (!RecordController.CURRENT_RECORD)
        $('#app_selector_inner li:last').addClass('greyed_out');

    $('#app_selector_inner li:last A').prepend(line);

},
hash_change: function(event) {
	var app_id = event.value.match(/app\/(.*)/)[1];
	var app = $.grep(PHAController.phas, function(pha) {return (pha.safeid() === app_id);})[0];
	PHAController.dispatch('launch_app', app);
},
launch_app: function(pha) {

	if (RecordController.RECORD_ID === undefined) {
		alert("Please choose a patient before running an app.");
	}

    var interpolation_args = {
    		  'record_id' : RecordController.RECORD_ID,
    		  'account_id' : ACCOUNT_ID
    		  };

  	  startURL = interpolate_url_template(pha.data.startURLTemplate, interpolation_args);
  	  RecordController.APP_ID = pha.id;

  	  SMART.register_app(	pha.id,
  			  				$('#app_content_iframe')[0],
  			  				startURL);

  	  SMART_HELPER.launch_app(
  			pha,
  			ACCOUNT_ID,
  			RecordController.CURRENT_RECORD.record_id,
  	  		function() {
  				// load and show the iframe
  				$('#app_content').hide();
  				$('#app_content_iframe').attr('src',  startURL);
  			});
},

draw_phas :function() {
	$('.app').remove();
    jQuery.each(PHAController.enabled_phas, function(i,v){
        PHAController.dispatch('_add_app', {'pha': v, 'fire_p': false})
    });
},

});
<|endoftext|>


<commit_before>/*global describe:false, it:false */
var callback = require('./../index');

var should = require('should');
var File = require('vinyl');
var es = require('event-stream');
var gutil = require('gulp-util');

var objectEquals = function(x, y) {
    if (x instanceof Function) {
        if (y instanceof Function) {
            return x.toString() === y.toString();
        }
        return false;
    }
    if (x === null || x === undefined || y === null || y === undefined) {
        return x === y;
    }
    if (x === y || x.valueOf() === y.valueOf()) {
        return true;
    }

    // if one of them is date, they must had equal valueOf
    if (x instanceof Date) {
        return false;
    }
    if (y instanceof Date) {
        return false;
    }

    // if they are not function or strictly equal, they both need to be Objects
    if (!(x instanceof Object)) {
        return false;
    }
    if (!(y instanceof Object)) {
        return false;
    }

    var p = Object.keys(x);
    return Object.keys(y).every(function (i) {
        return p.indexOf(i) !== -1;
    }) ?
        p.every(function (i) {
            return objectEquals(x[i], y[i]);
        }) : false;
};

describe('gulp-callback', function() {
    it('should works', function(done) {
        var fakeFile = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var myCallback = callback(function (file, enc, cb) {
            cb();
        });

        myCallback.write(fakeFile);

        myCallback.once('data', function(file) {
            file.isStream().should.be.true;

            file.contents.pipe(
                es.wait(function(err, data) {
                    data.should.equal('streamwiththosecontents');
                    done();
                })
            );
        });

        myCallback.end();
    });

    //is first argument function
    it('is first argument should function', function (done) {
        var fakeFile = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var myCallback = callback(function (file, enc, cb) {
            cb();
        });

        (function () {
            myCallback.write(fakeFile);
        }).should.not.throw();

        done();
    });

    // Test exception
    it('is first argument should not function', function (done) {
        var fakeFile = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var myCallback = callback('string');

        /*assert.throws(function () {
            myCallback.write(fakeFile);
        }, function (error) {
            if ((error instanceof Object) && /gulp-callback/.test(error.plugin) && /Callback is not function/.test(error.message)) {
                return true;

            }
        });*/
        var fn = function () {
            myCallback.write(fakeFile);
        };

        fn.should.throw('Callback is not function');

        done();
    });

    // is set once true
    it('is option once set true', function (done) {
        var fakeFile = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var fakeFileNext = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var count = 2;
        var myCallback = callback(function (file, enc, cb) {
            count--;
            cb();
        }, {
            once: true
        });

        myCallback.write(fakeFile);

        myCallback.write(fakeFileNext);

        count.should.equal(1);

        done();
    });

    // is set once false
    it('is option once set false', function (done) {
        var fakeFile = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var fakeFileNext = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var count = 2;
        var myCallback = callback(function (file, enc, cb) {
            count--;
            cb();
        }, {
            once: false
        });

        myCallback.write(fakeFile);

        myCallback.write(fakeFileNext);

        count.should.equal(0);

        done();
    });

    // is arguments callback working
    it('is first argument callback should File', function (done) {
        var fakeFile = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var myCallback = callback(function (file, enc, cb) {
            file.should.be.an.instanceof(File);
            cb();
        });

        myCallback.write(fakeFile);

        done();
    });

    it('is second argument callback should string', function (done) {
        var fakeFile = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var myCallback = callback(function (file, enc, cb) {
            enc.should.be.type('string');
            cb();
        });

        myCallback.write(fakeFile);

        done();
    });

    it('is second argument callback should function', function (done) {
        var fakeFile = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var myCallback = callback(function (file, enc, cb) {
            cb.should.be.an.Function;
            cb();
        });

        myCallback.write(fakeFile);

        done();
    });
});
<commit_msg>remove comment<commit_after>/*global describe:false, it:false */
var callback = require('./../index');

var should = require('should');
var File = require('vinyl');
var es = require('event-stream');
var gutil = require('gulp-util');

var objectEquals = function(x, y) {
    if (x instanceof Function) {
        if (y instanceof Function) {
            return x.toString() === y.toString();
        }
        return false;
    }
    if (x === null || x === undefined || y === null || y === undefined) {
        return x === y;
    }
    if (x === y || x.valueOf() === y.valueOf()) {
        return true;
    }

    // if one of them is date, they must had equal valueOf
    if (x instanceof Date) {
        return false;
    }
    if (y instanceof Date) {
        return false;
    }

    // if they are not function or strictly equal, they both need to be Objects
    if (!(x instanceof Object)) {
        return false;
    }
    if (!(y instanceof Object)) {
        return false;
    }

    var p = Object.keys(x);
    return Object.keys(y).every(function (i) {
        return p.indexOf(i) !== -1;
    }) ?
        p.every(function (i) {
            return objectEquals(x[i], y[i]);
        }) : false;
};

describe('gulp-callback', function() {
    it('should works', function(done) {
        var fakeFile = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var myCallback = callback(function (file, enc, cb) {
            cb();
        });

        myCallback.write(fakeFile);

        myCallback.once('data', function(file) {
            file.isStream().should.be.true;

            file.contents.pipe(
                es.wait(function(err, data) {
                    data.should.equal('streamwiththosecontents');
                    done();
                })
            );
        });

        myCallback.end();
    });

    //is first argument function
    it('is first argument should function', function (done) {
        var fakeFile = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var myCallback = callback(function (file, enc, cb) {
            cb();
        });

        (function () {
            myCallback.write(fakeFile);
        }).should.not.throw();

        done();
    });

    // Test exception
    it('is first argument should not function', function (done) {
        var fakeFile = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var myCallback = callback('string');

        var fn = function () {
            myCallback.write(fakeFile);
        };

        fn.should.throw('Callback is not function');

        done();
    });

    // is set once true
    it('is option once set true', function (done) {
        var fakeFile = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var fakeFileNext = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var count = 2;
        var myCallback = callback(function (file, enc, cb) {
            count--;
            cb();
        }, {
            once: true
        });

        myCallback.write(fakeFile);

        myCallback.write(fakeFileNext);

        count.should.equal(1);

        done();
    });

    // is set once false
    it('is option once set false', function (done) {
        var fakeFile = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var fakeFileNext = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var count = 2;
        var myCallback = callback(function (file, enc, cb) {
            count--;
            cb();
        }, {
            once: false
        });

        myCallback.write(fakeFile);

        myCallback.write(fakeFileNext);

        count.should.equal(0);

        done();
    });

    // is arguments callback working
    it('is first argument callback should File', function (done) {
        var fakeFile = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var myCallback = callback(function (file, enc, cb) {
            file.should.be.an.instanceof(File);
            cb();
        });

        myCallback.write(fakeFile);

        done();
    });

    it('is second argument callback should string', function (done) {
        var fakeFile = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var myCallback = callback(function (file, enc, cb) {
            enc.should.be.type('string');
            cb();
        });

        myCallback.write(fakeFile);

        done();
    });

    it('is second argument callback should function', function (done) {
        var fakeFile = new File({
            contents: es.readArray(['stream', 'with', 'those', 'contents'])
        });

        var myCallback = callback(function (file, enc, cb) {
            cb.should.be.an.Function;
            cb();
        });

        myCallback.write(fakeFile);

        done();
    });
});
<|endoftext|>


<commit_before>import React from "react-native";
import AppbarTouchable from "../components/appbar-touchable";
import AppbarIcon from "../components/appbar-icon";
import routes from "./routes";

const {
	StyleSheet,
	Text
} = React;

const styles = StyleSheet.create({
	title: {
		color: "#fff",
		fontWeight: "bold",
		fontSize: 18,
		marginVertical: 14,
		marginRight: 64,
		paddingHorizontal: 4
	}
});

const NavigationBarRouteMapper = {
	LeftButton(route, navigator) {
		const goBack = () => {
			if (navigator.getCurrentRoutes().length > 1) {
				navigator.pop();
			} else {
				navigator.replace(routes.home());
			}
		};

		if (route.leftComponent) {
			return <route.leftComponent {...route.passProps} navigator={navigator} />;
		}

		if (route.index !== 0) {
			return (
				<AppbarTouchable onPress={goBack}>
					<AppbarIcon name="arrow-back" />
				</AppbarTouchable>
			);
		}
	},

	RightButton(route, navigator) {
		if (route.rightComponent) {
			return <route.rightComponent {...route.passProps} navigator={navigator} />;
		}

		return null;
	},

	Title(route, navigator) {
		if (route.titleComponent) {
			return <route.titleComponent {...route.passProps} navigator={navigator} />;
		}

		return (
			<Text style={styles.title} numberOfLines={1}>
				{route.title}
			</Text>
		);
	}
};

export default NavigationBarRouteMapper;
<commit_msg>Put a phantom view on the right to prevent unwanted taps<commit_after>import React from "react-native";
import AppbarTouchable from "../components/appbar-touchable";
import AppbarIcon from "../components/appbar-icon";
import routes from "./routes";

const {
	StyleSheet,
	View,
	Text
} = React;

const styles = StyleSheet.create({
	title: {
		color: "#fff",
		fontWeight: "bold",
		fontSize: 18,
		marginVertical: 14,
		marginRight: 64,
		paddingHorizontal: 4
	},

	phantom: {
		height: 56,
		width: 56
	}
});

const NavigationBarRouteMapper = {
	LeftButton(route, navigator) {
		const goBack = () => {
			if (navigator.getCurrentRoutes().length > 1) {
				navigator.pop();
			} else {
				navigator.replace(routes.home());
			}
		};

		if (route.leftComponent) {
			return <route.leftComponent {...route.passProps} navigator={navigator} />;
		}

		if (route.index !== 0) {
			return (
				<AppbarTouchable onPress={goBack}>
					<AppbarIcon name="arrow-back" />
				</AppbarTouchable>
			);
		}
	},

	RightButton(route, navigator) {
		if (route.rightComponent) {
			return <route.rightComponent {...route.passProps} navigator={navigator} />;
		}

		return <View style={styles.phantom} />;
	},

	Title(route, navigator) {
		if (route.titleComponent) {
			return <route.titleComponent {...route.passProps} navigator={navigator} />;
		}

		return (
			<Text style={styles.title} numberOfLines={1}>
				{route.title}
			</Text>
		);
	}
};

export default NavigationBarRouteMapper;
<|endoftext|>


<commit_before>var Server = require('../index.js');
var WebSocket = require('ws');

var server = new Server({
  projects: ['/Users/xukaixuan/Projects/taxi/passenger-gulp-app/*', '/Users/xukaixuan/Projects/taxi/hx-ser/svn/*']
});
// setTimeout(function () {
//   var ws = new WebSocket('ws://127.0.0.1:' + server.info().port);
//   ws.on('open', function() {
//     ws.send(JSON.stringify({
//       command: 2,
//       appid: 'A6932196309087',
//       timestamp: new Date().getTime() / 1000
//     }));
//   });
// }, 2000);<commit_msg>test update<commit_after>var Server = require('../index.js');
var WebSocket = require('ws');

var server = new Server({
  projects: ['/Users/xukaixuan/Projects/taxi/passenger-gulp-app/*', '/Users/xukaixuan/Projects/taxi/hx-ser/svn/*']
});
server.onLog(console.log);
setTimeout(function () {
  var ws = new WebSocket('ws://127.0.0.1:' + server.info().port);
  ws.on('open', function() {
    ws.send(JSON.stringify({
      command: 2,
      appid: 'A6932196309087',
      timestamp: new Date().getTime() / 1000
    }));
  });
}, 2000);<|endoftext|>


<commit_before>var fs = require('fs')
var path = require('path')
var tape = require('tape')
var fmt = require('..')

function input (testType, testName) {
  var filepath = path.resolve('test', testType, testName + '.css')
  return fs.readFileSync(filepath, 'utf-8')
}

function output (dir, testName) {
  var filepath = path.resolve(dir, testName + '.out.css')
  return fs.readFileSync(filepath, 'utf-8')
}

function test (testName) {
  tape(testName, function (t) {
    var dir = path.join(process.cwd(), 'test/fixtures')
    t.equal(fmt.process(input(dir, testName)), output(dir, testName))
    t.end()
  })
}

function testWithStylelint (testName) {
  var dir = path.join(process.cwd(), 'test/stylelint', testName)
  var stylelintrcPath = path.resolve(dir, '.stylelintrc');
  var opts = {
    stylelintrcPath: stylelintrcPath
  }
  tape(testName, function (t) {
    t.equal(fmt.process(input(dir, testName), opts), output(dir, testName))
    t.end()
  })
}

test('readme')
test('nested')
test('nested-2')
test('non-nested-combinator')
test('important')
test('values')
test('comment')
test('comment-in-rules')
test('vendor-prefix')
test('nested-atrule')
test('nested-mixin')
test('nested-mixin-2')
test('font-face')
test('font-shorthand')
test('import')
test('charset')
test('charset-2')
test('pseudo-element')
test('at-media')
test('data-url')
test('color-hex-lowercase')
test('lowercase')
test('content')
test('ie-hacks')

// for future syntaxes
test('cssnext-example')
test('custom-properties')
test('var-notation')
test('custom-media-queries')
test('media-queries-ranges')
test('custom-selectors')
test('at-apply')

// for Sass
test('sass-mixin')
test('sass-mixin-2')
test('sass-extend')
test('sass-variables')
test('shorthand-with-sass-variables')
test('sass-include')
test('sass-include-2')
test('inline-comment')
test('sass-function')
test('sass-function-2')
test('scss')
test('sass-at-root')
test('sass-comment')
test('sass-inline-comment')
test('sass-content')
test('sass-single-at-rule')
test('sass-math')
test('sass-if-else')
test('sass-if-else-2')
test('sass-indent')
test('media-indent')
test('media-indent-with-import')

// for stylelint
testWithStylelint('selector-list-comma-space-before')
testWithStylelint('selector-list-comma-space-after')
testWithStylelint('selector-list-comma-newline-before')
testWithStylelint('selector-list-comma-newline-after')
testWithStylelint('selector-combinator-space-before')
testWithStylelint('selector-combinator-space-after')
testWithStylelint('block-opening-brace-space-before')
testWithStylelint('block-opening-brace-newline-before')
<commit_msg>modify test/index.js<commit_after>var fs = require('fs-extra')
var path = require('path')
var tape = require('tape')
var fmt = require('..')

function input (testType, testName) {
  var filepath = path.resolve('test', testType, testName + '.css')
  return fs.readFileSync(filepath, 'utf-8')
}

function output (dir, testName) {
  var filepath = path.resolve(dir, testName + '.out.css')
  return fs.readFileSync(filepath, 'utf-8')
}

function test (testName) {
  tape(testName, function (t) {
    var testDir = path.join(process.cwd(), 'test/fixtures')
    t.plan(1)
    t.equal(fmt.process(input(testDir, testName)), output(testDir, testName))
    t.end()
  })
}

function testWithStylelint (testName) {
  tape(testName, function (t) {
    var cwd = process.cwd()
    var configSrc = path.resolve(path.join(cwd, 'test/stylelint', testName), '.stylelintrc')
    var configDest = path.resolve(cwd, '.stylelintrc')
    fs.copySync(configSrc, configDest, { clobber: true })

    var testDir = path.join(cwd, 'test/stylelint', testName)
    t.plan(1)
    t.equal(fmt.process(input(testDir, testName)), output(testDir, testName))
    t.end()
  })
}

tape.onFinish(function () {
  var config = path.resolve(process.cwd(), '.stylelintrc')
  fs.removeSync(config)
})

test('readme')
test('nested')
test('nested-2')
test('non-nested-combinator')
test('important')
test('values')
test('comment')
test('comment-in-rules')
test('vendor-prefix')
test('nested-atrule')
test('nested-mixin')
test('nested-mixin-2')
test('font-face')
test('font-shorthand')
test('import')
test('charset')
test('charset-2')
test('pseudo-element')
test('at-media')
test('data-url')
test('color-hex-lowercase')
test('lowercase')
test('content')
test('ie-hacks')

// for future syntaxes
test('cssnext-example')
test('custom-properties')
test('var-notation')
test('custom-media-queries')
test('media-queries-ranges')
test('custom-selectors')
test('at-apply')

// for Sass
test('sass-mixin')
test('sass-mixin-2')
test('sass-extend')
test('sass-variables')
test('shorthand-with-sass-variables')
test('sass-include')
test('sass-include-2')
test('inline-comment')
test('sass-function')
test('sass-function-2')
test('scss')
test('sass-at-root')
test('sass-comment')
test('sass-inline-comment')
test('sass-content')
test('sass-single-at-rule')
test('sass-math')
test('sass-if-else')
test('sass-if-else-2')
test('sass-indent')
test('media-indent')
test('media-indent-with-import')

// for stylelint configuration
testWithStylelint('selector-list-comma-space-before')
testWithStylelint('selector-list-comma-space-after')
testWithStylelint('selector-list-comma-newline-before')
testWithStylelint('selector-list-comma-newline-after')
testWithStylelint('selector-combinator-space-before')
testWithStylelint('selector-combinator-space-after')
testWithStylelint('block-opening-brace-space-before')
testWithStylelint('block-opening-brace-newline-before')
<|endoftext|>