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

/**
 * General dialog type
 * Used to live at 'JOIST/Dialog'. Moved to 'SUN/Dialog' on 4/10/2018
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Andrea Lin
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var AccessibilityUtil = require( 'SCENERY/accessibility/AccessibilityUtil' );
  var AccessiblePeer = require( 'SCENERY/accessibility/AccessiblePeer' );
  var AlignBox = require( 'SCENERY/nodes/AlignBox' );
  var DialogIO = require( 'SUN/DialogIO' );
  var Display = require( 'SCENERY/display/Display' );
  var FullScreen = require( 'SCENERY/util/FullScreen' );
  var HBox = require( 'SCENERY/nodes/HBox' );
  var inherit = require( 'PHET_CORE/inherit' );
  var KeyboardUtil = require( 'SCENERY/accessibility/KeyboardUtil' );
  var Panel = require( 'SUN/Panel' );
  var Path = require( 'SCENERY/nodes/Path' );
  var RectangularButtonView = require( 'SUN/buttons/RectangularButtonView' );
  var RectangularPushButton = require( 'SUN/buttons/RectangularPushButton' );
  var Shape = require( 'KITE/Shape' );
  var sun = require( 'SUN/sun' );
  var SunA11yStrings = require( 'SUN/SunA11yStrings' );
  var Tandem = require( 'TANDEM/Tandem' );
  var VBox = require( 'SCENERY/nodes/VBox' );

  // strings
  var closeString = SunA11yStrings.close.value;

  // constants
  var CLOSE_BUTTON_WIDTH = 14;

  /**
   * @param {Node} content - The content to display inside the dialog (not including the title)
   * @param {Object} [options]
   * @constructor
   */
  function Dialog( content, options ) {

    var self = this;

    options = _.extend( {

      /* Margins and spacing:
       ____________________________________________________________________________
      |                                     |                          |           |
      |                                     |                          closeButton |
      |                                     topMargin                  TopMargin   |
      |                                     |                         _|___        |
      |                  ___________________|____________________    |     |       |
      |--------l--------|                                        |-x-|  X  |---c---|
      |        e        |   Title                                | S |_____|   l   |
      |        f        |________________________________________| P           o   |
      |        t        |   |                                    | a           s   |
      |        M        |   ySpacing                             | c           e   |
      |        a        |___|____________________________________| i           B   |
      |        r        |                                        | n           u   |
      |        g        |   Content                              | g           t   |
      |        i        |                                        |             t   |
      |        n        |                                        |             o   |
      |                 |                                        |             n   |
      |                 |                                        |             R   |
      |                 |                                        |             i   |
      |                 |                                        |             g   |
      |                 |                                        |             h   |
      |                 |                                        |             M   |
      |                 |________________________________________|             a   |
      |                                     |                                  r   |
      |                                     |                                  g   |
      |                                     bottomMargin                       i   |
      |                                     |                                  n   |
      |_____________________________________|______________________________________|
       */

      xSpacing: 10, // {number} how far the title and content is placed to the left of the close button
      ySpacing: 10, // {number} vertical space between title and content
      topMargin: 15, // {number} margin above content, or above title if provided
      bottomMargin: 15, // {number} margin below content
      leftMargin: null, // {number|null} margin to the left of the content.  If null, this is computed so that we have
      // the same margins on the left and right of the content.
      closeButtonTopMargin: 10, // {number} margin above the close button
      closeButtonRightMargin: 10, // {number} margin to the right of the close button

      // more Dialog-specific options
      modal: true, // {boolean} modal dialogs prevent interaction with the rest of the sim while open
      title: null, // {Node|null} title to be displayed at top
      titleAlign: 'center', // horizontal alignment of the title: {string} left, right or center

      // {function} which sets the dialog's position in global coordinates. called as
      // layoutStrategy( dialog, simBounds, screenBounds, scale )
      layoutStrategy: Dialog.DEFAULT_LAYOUT_STRATEGY,

      // close button options
      closeButtonListener: function() { self.hide(); },
      closeButtonTouchAreaXDilation: 0,
      closeButtonTouchAreaYDilation: 0,
      closeButtonMouseAreaXDilation: 0,
      closeButtonMouseAreaYDilation: 0,

      // {function|null} called after the dialog is shown, see https://github.com/phetsims/joist/issues/478
      showCallback: null,

      // {function|null} called after the dialog is hidden, see https://github.com/phetsims/joist/issues/478
      hideCallback: null,

      // pass through to Panel options
      cornerRadius: 10, // {number} radius of the dialog's corners
      resize: true, // {boolean} whether to resize if content's size changes
      fill: 'white', // {string|Color}
      stroke: 'black', // {string|Color}
      backgroundPickable: true,
      tandem: Tandem.optional,
      phetioType: DialogIO,
      phetioReadOnly: false, // default to false so it can pass it through to the close button
      phetioState: false, // default to false so it can pass it through to the close button

      // a11y options
      tagName: 'div',
      ariaRole: 'dialog',
      focusOnCloseNode: null // {Node} receives focus on close, if null focus returns to element that had focus on open
    }, options );

    assert && assert( options.xMargin === undefined, 'Dialog sets xMargin' );
    options.xMargin = 0;
    assert && assert( options.yMargin === undefined, 'Dialog sets yMargin' );
    options.yMargin = 0;

    // if left margin is specified in options, use it. otherwise, set it to make the left right gutters symmetrical
    if ( options.leftMargin === null ) {
      options.leftMargin = options.xSpacing + CLOSE_BUTTON_WIDTH + options.closeButtonRightMargin;
    }

    // @private (read-only)
    this.isModal = options.modal;

    // @private
    this.showCallback = options.showCallback;
    this.hideCallback = options.hideCallback;

    // see https://github.com/phetsims/joist/issues/293
    assert && assert( this.isModal, 'Non-modal dialogs not currently supported' );

    // @private - whether the dialog is showing
    this.isShowing = false;

    // create close button
    var closeButton = new CloseButton( {

      iconLength: CLOSE_BUTTON_WIDTH,
      listener: options.closeButtonListener,

      // phet-io
      tandem: options.tandem.createTandem( 'closeButton' ),
      phetioReadOnly: options.phetioReadOnly, // match the readOnly of the Dialog
      phetioState: options.phetioState, // match the state transfer of the Dialog

      // a11y
      tagName: 'button',
      innerContent: closeString,
      accessibleFire: function() {
        self.focusActiveElement();
      }
    } );

    // touch/mouse areas for the close button
    closeButton.touchArea = closeButton.bounds.dilatedXY(
      options.closeButtonTouchAreaXDilation,
      options.closeButtonTouchAreaYDilation
    );
    closeButton.mouseArea = closeButton.bounds.dilatedXY(
      options.closeButtonMouseAreaXDilation,
      options.closeButtonMouseAreaYDilation
    );

    // @protected (a11y)
    this.closeButton = closeButton;

    // Align content, title, and close button using spacing and margin options

    // align content and title (if provided) vertically
    var contentAndTitle = new VBox( {
      children: options.title ? [ options.title, content ] : [ content ],
      spacing: options.ySpacing,
      align: options.titleAlign
    } );

    // add topMargin, bottomMargin, and leftMargin
    var contentAndTitleWithMargins = new AlignBox( contentAndTitle, {
      topMargin: options.topMargin,
      bottomMargin: options.bottomMargin,
      leftMargin: options.leftMargin
    } );

    // add closeButtonTopMargin and closeButtonRightMargin
    var closeButtonWithMargins = new AlignBox( closeButton, {
      topMargin: options.closeButtonTopMargin,
      rightMargin: options.closeButtonRightMargin
    } );

    // create content for Panel
    var dialogContent = new HBox( {
      children: [ contentAndTitleWithMargins, closeButtonWithMargins ],
      spacing: options.xSpacing,
      align: 'top'
    } );

    Panel.call( this, dialogContent, options );

    var sim = window.phet.joist.sim;

    // @private
    this.updateLayout = function() {
      options.layoutStrategy( self, sim.boundsProperty.value, sim.screenBoundsProperty.value, sim.scaleProperty.value );
    };

    this.updateLayout();

    // @private
    this.sim = sim;

    // a11y - set the order of content for accessibility, title before content
    this.accessibleOrder = [ options.title, content ].filter( function( node ) {
      return node !== undefined;
    } );

    // a11y - set the aria-labelledby relation so that whenever focus enters the dialog the title is read
    if ( options.title && options.title.tagName ) {
      this.addAriaLabelledbyAssociation( {
        thisElementName: AccessiblePeer.PRIMARY_SIBLING,
        otherNode: options.title,
        otherElementName: AccessiblePeer.PRIMARY_SIBLING
      } );
    }

    // must be removed on dispose
    this.sim.resizedEmitter.addListener( this.updateLayout );

    // @private (a11y) - the active element when the dialog is shown, tracked so that focus can be restored on close
    this.activeElement = options.focusOnCloseNode || null;

    // a11y - close the dialog when pressing "escape"
    var escapeListener = {
      keydown: function( event ) {
        if ( event.keyCode === KeyboardUtil.KEY_ESCAPE ) {
          event.preventDefault();
          self.hide();
          self.focusActiveElement();
        }
        else if ( event.keyCode === KeyboardUtil.KEY_TAB && FullScreen.isFullScreen() ) {

          // prevent a particular bug in Windows 7/8.1 Firefox where focus gets trapped in the document
          // when the navigation bar is hidden and there is only one focusable element in the DOM
          // see https://bugzilla.mozilla.org/show_bug.cgi?id=910136
          var activeId = Display.focus.trail.getUniqueId();
          var noNextFocusable = AccessibilityUtil.getNextFocusable().id === activeId;
          var noPreviousFocusable = AccessibilityUtil.getPreviousFocusable().id === activeId;

          if ( noNextFocusable && noPreviousFocusable ) {
            event.preventDefault();
          }
        }
      }
    };
    this.addAccessibleInputListener( escapeListener );

    // @private - to be called on dispose()
    this.disposeDialog = function() {
      self.sim.resizedEmitter.removeListener( self.updateLayout );
      self.removeAccessibleInputListener( escapeListener );

      closeButton.dispose();

      // remove dialog content from scene graph, but don't dispose because Panel
      // needs to remove listeners on the content in its dispose()
      dialogContent.removeAllChildren();
      dialogContent.detach();
    };
  }

  sun.register( 'Dialog', Dialog );

  // @private
  Dialog.DEFAULT_LAYOUT_STRATEGY = function( dialog, simBounds, screenBounds, scale ) {

    // The size is set in the Sim.topLayer, but we need to update the location here
    dialog.center = simBounds.center.times( 1.0 / scale );
  };

  inherit( Panel, Dialog, {

    // @public
    show: function() {
      if ( !this.isShowing ) {
        window.phet.joist.sim.showPopup( this, this.isModal );
        this.isShowing = true;

        // a11y - store the currently active element before hiding all other accessible content
        // so that the active element isn't blurred
        this.activeElement = this.activeElement || Display.focusedNode;
        this.setAccessibleViewsVisible( false );

        // In case the window size has changed since the dialog was hidden, we should try layout out again.
        // See https://github.com/phetsims/joist/issues/362
        this.updateLayout();

        // Do this last
        this.showCallback && this.showCallback();
      }
    },

    /**
     * Hide the dialog.  If you create a new dialog next time you show(), be sure to dispose this
     * dialog instead.
     * @public
     */
    hide: function() {
      if ( this.isShowing ) {

        window.phet.joist.sim.hidePopup( this, this.isModal );
        this.isShowing = false;

        // a11y - when the dialog is hidden, make all ScreenView content visible to assistive technology
        this.setAccessibleViewsVisible( true );

        // Do this last
        this.hideCallback && this.hideCallback();
      }
    },

    /**
     * Make eligible for garbage collection.
     * @public
     */
    dispose: function() {
      this.hide();
      this.disposeDialog();
      Panel.prototype.dispose.call( this );
    },

    /**
     * Hide or show all accessible content related to the sim ScreenViews, navigation bar, and alert content. Instead
     * of using setVisible, we have to remove the subtree of accessible content from each view element in order to
     * prevent an IE11 bug where content remains invisible in the accessibility tree, see
     * https://github.com/phetsims/john-travoltage/issues/247
     *
     * @param {boolean} visible
     */
    setAccessibleViewsVisible: function( visible ) {
      for ( var i = 0; i < this.sim.screens.length; i++ ) {
        this.sim.screens[ i ].view.accessibleContentDisplayed = visible;
      }
      this.sim.navigationBar.accessibleContentDisplayed = visible;
      this.sim.homeScreen && this.sim.homeScreen.view.setAccessibleContentDisplayed( visible );

      // workaround for a strange Edge bug where this child of the navigation bar remains visible,
      // see https://github.com/phetsims/a11y-research/issues/30
      if ( this.sim.navigationBar.keyboardHelpButton ) {
        this.sim.navigationBar.keyboardHelpButton.accessibleVisible = visible;
      }
    },

    /**
     * If there is an active element, focus it.  Should almost always be called after the Dialog has been closed.
     *
     * @public
     * @a11y
     */
    focusActiveElement: function() {
      this.activeElement && this.activeElement.focus();
    }
  } );

  /**
   * The close button for Dialog
   * A flat x
   *
   * @param {Object} [options] - see RectangularPushButton
   * @constructor
   */
  function CloseButton( options ) {
    options = _.extend( {
      iconLength: 7,
      baseColor: 'transparent',
      buttonAppearanceStrategy: RectangularButtonView.FlatAppearanceStrategy,
      xMargin: 0,
      yMargin: 0,
      listener: null // {function} called when the button is pressed
    }, options );

    // close button shape, an 'X'
    var closeButtonShape = new Shape()
      .moveTo( -options.iconLength / 2, -options.iconLength / 2 )
      .lineTo( options.iconLength / 2, options.iconLength / 2 )
      .moveTo( options.iconLength / 2, -options.iconLength / 2 )
      .lineTo( -options.iconLength / 2, options.iconLength / 2 );

    assert && assert( !options.content, 'Dialog.CloseButton sets content' );

    options.content = new Path( closeButtonShape, {
      stroke: 'black',
      lineCap: 'round',
      lineWidth: 2
    } );

    RectangularPushButton.call( this, options );
  }

  sun.register( 'Dialog.CloseButton', CloseButton );

  inherit( RectangularPushButton, CloseButton );

  return Dialog;
} );<commit_msg>close button comes first in focus order and is private to Dialog, see phetsims/joist#486<commit_after>// Copyright 2018, University of Colorado Boulder

/**
 * General dialog type
 * Used to live at 'JOIST/Dialog'. Moved to 'SUN/Dialog' on 4/10/2018
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Andrea Lin
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var AccessibilityUtil = require( 'SCENERY/accessibility/AccessibilityUtil' );
  var AccessiblePeer = require( 'SCENERY/accessibility/AccessiblePeer' );
  var AlignBox = require( 'SCENERY/nodes/AlignBox' );
  var DialogIO = require( 'SUN/DialogIO' );
  var Display = require( 'SCENERY/display/Display' );
  var FullScreen = require( 'SCENERY/util/FullScreen' );
  var HBox = require( 'SCENERY/nodes/HBox' );
  var inherit = require( 'PHET_CORE/inherit' );
  var KeyboardUtil = require( 'SCENERY/accessibility/KeyboardUtil' );
  var Panel = require( 'SUN/Panel' );
  var Path = require( 'SCENERY/nodes/Path' );
  var RectangularButtonView = require( 'SUN/buttons/RectangularButtonView' );
  var RectangularPushButton = require( 'SUN/buttons/RectangularPushButton' );
  var Shape = require( 'KITE/Shape' );
  var sun = require( 'SUN/sun' );
  var SunA11yStrings = require( 'SUN/SunA11yStrings' );
  var Tandem = require( 'TANDEM/Tandem' );
  var VBox = require( 'SCENERY/nodes/VBox' );

  // strings
  var closeString = SunA11yStrings.close.value;

  // constants
  var CLOSE_BUTTON_WIDTH = 14;

  /**
   * @param {Node} content - The content to display inside the dialog (not including the title)
   * @param {Object} [options]
   * @constructor
   */
  function Dialog( content, options ) {

    var self = this;

    options = _.extend( {

      /* Margins and spacing:
       ____________________________________________________________________________
      |                                     |                          |           |
      |                                     |                          closeButton |
      |                                     topMargin                  TopMargin   |
      |                                     |                         _|___        |
      |                  ___________________|____________________    |     |       |
      |--------l--------|                                        |-x-|  X  |---c---|
      |        e        |   Title                                | S |_____|   l   |
      |        f        |________________________________________| P           o   |
      |        t        |   |                                    | a           s   |
      |        M        |   ySpacing                             | c           e   |
      |        a        |___|____________________________________| i           B   |
      |        r        |                                        | n           u   |
      |        g        |   Content                              | g           t   |
      |        i        |                                        |             t   |
      |        n        |                                        |             o   |
      |                 |                                        |             n   |
      |                 |                                        |             R   |
      |                 |                                        |             i   |
      |                 |                                        |             g   |
      |                 |                                        |             h   |
      |                 |                                        |             M   |
      |                 |________________________________________|             a   |
      |                                     |                                  r   |
      |                                     |                                  g   |
      |                                     bottomMargin                       i   |
      |                                     |                                  n   |
      |_____________________________________|______________________________________|
       */

      xSpacing: 10, // {number} how far the title and content is placed to the left of the close button
      ySpacing: 10, // {number} vertical space between title and content
      topMargin: 15, // {number} margin above content, or above title if provided
      bottomMargin: 15, // {number} margin below content
      leftMargin: null, // {number|null} margin to the left of the content.  If null, this is computed so that we have
      // the same margins on the left and right of the content.
      closeButtonTopMargin: 10, // {number} margin above the close button
      closeButtonRightMargin: 10, // {number} margin to the right of the close button

      // more Dialog-specific options
      modal: true, // {boolean} modal dialogs prevent interaction with the rest of the sim while open
      title: null, // {Node|null} title to be displayed at top
      titleAlign: 'center', // horizontal alignment of the title: {string} left, right or center

      // {function} which sets the dialog's position in global coordinates. called as
      // layoutStrategy( dialog, simBounds, screenBounds, scale )
      layoutStrategy: Dialog.DEFAULT_LAYOUT_STRATEGY,

      // close button options
      closeButtonListener: function() { self.hide(); },
      closeButtonTouchAreaXDilation: 0,
      closeButtonTouchAreaYDilation: 0,
      closeButtonMouseAreaXDilation: 0,
      closeButtonMouseAreaYDilation: 0,

      // {function|null} called after the dialog is shown, see https://github.com/phetsims/joist/issues/478
      showCallback: null,

      // {function|null} called after the dialog is hidden, see https://github.com/phetsims/joist/issues/478
      hideCallback: null,

      // pass through to Panel options
      cornerRadius: 10, // {number} radius of the dialog's corners
      resize: true, // {boolean} whether to resize if content's size changes
      fill: 'white', // {string|Color}
      stroke: 'black', // {string|Color}
      backgroundPickable: true,
      tandem: Tandem.optional,
      phetioType: DialogIO,
      phetioReadOnly: false, // default to false so it can pass it through to the close button
      phetioState: false, // default to false so it can pass it through to the close button

      // a11y options
      tagName: 'div',
      ariaRole: 'dialog',
      focusOnCloseNode: null // {Node} receives focus on close, if null focus returns to element that had focus on open
    }, options );

    assert && assert( options.xMargin === undefined, 'Dialog sets xMargin' );
    options.xMargin = 0;
    assert && assert( options.yMargin === undefined, 'Dialog sets yMargin' );
    options.yMargin = 0;

    // if left margin is specified in options, use it. otherwise, set it to make the left right gutters symmetrical
    if ( options.leftMargin === null ) {
      options.leftMargin = options.xSpacing + CLOSE_BUTTON_WIDTH + options.closeButtonRightMargin;
    }

    // @private (read-only)
    this.isModal = options.modal;

    // @private
    this.showCallback = options.showCallback;
    this.hideCallback = options.hideCallback;

    // see https://github.com/phetsims/joist/issues/293
    assert && assert( this.isModal, 'Non-modal dialogs not currently supported' );

    // @private - whether the dialog is showing
    this.isShowing = false;

    // create close button
    var closeButton = new CloseButton( {

      iconLength: CLOSE_BUTTON_WIDTH,
      listener: options.closeButtonListener,

      // phet-io
      tandem: options.tandem.createTandem( 'closeButton' ),
      phetioReadOnly: options.phetioReadOnly, // match the readOnly of the Dialog
      phetioState: options.phetioState, // match the state transfer of the Dialog

      // a11y
      tagName: 'button',
      innerContent: closeString,
      accessibleFire: function() {
        self.focusActiveElement();
      }
    } );

    // touch/mouse areas for the close button
    closeButton.touchArea = closeButton.bounds.dilatedXY(
      options.closeButtonTouchAreaXDilation,
      options.closeButtonTouchAreaYDilation
    );
    closeButton.mouseArea = closeButton.bounds.dilatedXY(
      options.closeButtonMouseAreaXDilation,
      options.closeButtonMouseAreaYDilation
    );

    // @private (a11y)
    this.closeButton = closeButton;

    // Align content, title, and close button using spacing and margin options

    // align content and title (if provided) vertically
    var contentAndTitle = new VBox( {
      children: options.title ? [ options.title, content ] : [ content ],
      spacing: options.ySpacing,
      align: options.titleAlign
    } );

    // add topMargin, bottomMargin, and leftMargin
    var contentAndTitleWithMargins = new AlignBox( contentAndTitle, {
      topMargin: options.topMargin,
      bottomMargin: options.bottomMargin,
      leftMargin: options.leftMargin
    } );

    // add closeButtonTopMargin and closeButtonRightMargin
    var closeButtonWithMargins = new AlignBox( closeButton, {
      topMargin: options.closeButtonTopMargin,
      rightMargin: options.closeButtonRightMargin
    } );

    // create content for Panel
    var dialogContent = new HBox( {
      children: [ contentAndTitleWithMargins, closeButtonWithMargins ],
      spacing: options.xSpacing,
      align: 'top'
    } );

    Panel.call( this, dialogContent, options );

    var sim = window.phet.joist.sim;

    // @private
    this.updateLayout = function() {
      options.layoutStrategy( self, sim.boundsProperty.value, sim.screenBoundsProperty.value, sim.scaleProperty.value );
    };

    this.updateLayout();

    // @private
    this.sim = sim;

    // a11y - set the order of content, close button first so remaining content can be read from top to bottom
    // with virtual cursor
    this.accessibleOrder = [ closeButton, options.title, content ].filter( function( node ) {
      return node !== undefined;
    } );

    // a11y - set the aria-labelledby relation so that whenever focus enters the dialog the title is read
    if ( options.title && options.title.tagName ) {
      this.addAriaLabelledbyAssociation( {
        thisElementName: AccessiblePeer.PRIMARY_SIBLING,
        otherNode: options.title,
        otherElementName: AccessiblePeer.PRIMARY_SIBLING
      } );
    }

    // must be removed on dispose
    this.sim.resizedEmitter.addListener( this.updateLayout );

    // @private (a11y) - the active element when the dialog is shown, tracked so that focus can be restored on close
    this.activeElement = options.focusOnCloseNode || null;

    // a11y - close the dialog when pressing "escape"
    var escapeListener = {
      keydown: function( event ) {
        if ( event.keyCode === KeyboardUtil.KEY_ESCAPE ) {
          event.preventDefault();
          self.hide();
          self.focusActiveElement();
        }
        else if ( event.keyCode === KeyboardUtil.KEY_TAB && FullScreen.isFullScreen() ) {

          // prevent a particular bug in Windows 7/8.1 Firefox where focus gets trapped in the document
          // when the navigation bar is hidden and there is only one focusable element in the DOM
          // see https://bugzilla.mozilla.org/show_bug.cgi?id=910136
          var activeId = Display.focus.trail.getUniqueId();
          var noNextFocusable = AccessibilityUtil.getNextFocusable().id === activeId;
          var noPreviousFocusable = AccessibilityUtil.getPreviousFocusable().id === activeId;

          if ( noNextFocusable && noPreviousFocusable ) {
            event.preventDefault();
          }
        }
      }
    };
    this.addAccessibleInputListener( escapeListener );

    // @private - to be called on dispose()
    this.disposeDialog = function() {
      self.sim.resizedEmitter.removeListener( self.updateLayout );
      self.removeAccessibleInputListener( escapeListener );

      closeButton.dispose();

      // remove dialog content from scene graph, but don't dispose because Panel
      // needs to remove listeners on the content in its dispose()
      dialogContent.removeAllChildren();
      dialogContent.detach();
    };
  }

  sun.register( 'Dialog', Dialog );

  // @private
  Dialog.DEFAULT_LAYOUT_STRATEGY = function( dialog, simBounds, screenBounds, scale ) {

    // The size is set in the Sim.topLayer, but we need to update the location here
    dialog.center = simBounds.center.times( 1.0 / scale );
  };

  inherit( Panel, Dialog, {

    // @public
    show: function() {
      if ( !this.isShowing ) {
        window.phet.joist.sim.showPopup( this, this.isModal );
        this.isShowing = true;

        // a11y - store the currently active element before hiding all other accessible content
        // so that the active element isn't blurred
        this.activeElement = this.activeElement || Display.focusedNode;
        this.setAccessibleViewsVisible( false );

        // In case the window size has changed since the dialog was hidden, we should try layout out again.
        // See https://github.com/phetsims/joist/issues/362
        this.updateLayout();

        // Do this last
        this.showCallback && this.showCallback();
      }
    },

    /**
     * Hide the dialog.  If you create a new dialog next time you show(), be sure to dispose this
     * dialog instead.
     * @public
     */
    hide: function() {
      if ( this.isShowing ) {

        window.phet.joist.sim.hidePopup( this, this.isModal );
        this.isShowing = false;

        // a11y - when the dialog is hidden, make all ScreenView content visible to assistive technology
        this.setAccessibleViewsVisible( true );

        // Do this last
        this.hideCallback && this.hideCallback();
      }
    },

    /**
     * Make eligible for garbage collection.
     * @public
     */
    dispose: function() {
      this.hide();
      this.disposeDialog();
      Panel.prototype.dispose.call( this );
    },

    /**
     * Hide or show all accessible content related to the sim ScreenViews, navigation bar, and alert content. Instead
     * of using setVisible, we have to remove the subtree of accessible content from each view element in order to
     * prevent an IE11 bug where content remains invisible in the accessibility tree, see
     * https://github.com/phetsims/john-travoltage/issues/247
     *
     * @param {boolean} visible
     */
    setAccessibleViewsVisible: function( visible ) {
      for ( var i = 0; i < this.sim.screens.length; i++ ) {
        this.sim.screens[ i ].view.accessibleContentDisplayed = visible;
      }
      this.sim.navigationBar.accessibleContentDisplayed = visible;
      this.sim.homeScreen && this.sim.homeScreen.view.setAccessibleContentDisplayed( visible );

      // workaround for a strange Edge bug where this child of the navigation bar remains visible,
      // see https://github.com/phetsims/a11y-research/issues/30
      if ( this.sim.navigationBar.keyboardHelpButton ) {
        this.sim.navigationBar.keyboardHelpButton.accessibleVisible = visible;
      }
    },

    /**
     * If there is an active element, focus it.  Should almost always be called after the Dialog has been closed.
     *
     * @public
     * @a11y
     */
    focusActiveElement: function() {
      this.activeElement && this.activeElement.focus();
    },

    /**
     * Place keyboard focus on the close button, useful when opening the dialog with an accessibility interaction.
     * @public
     */
    focusCloseButton: function() {
      this.closeButton.focus();
    }
  } );

  /**
   * The close button for Dialog
   * A flat x
   *
   * @param {Object} [options] - see RectangularPushButton
   * @constructor
   */
  function CloseButton( options ) {
    options = _.extend( {
      iconLength: 7,
      baseColor: 'transparent',
      buttonAppearanceStrategy: RectangularButtonView.FlatAppearanceStrategy,
      xMargin: 0,
      yMargin: 0,
      listener: null // {function} called when the button is pressed
    }, options );

    // close button shape, an 'X'
    var closeButtonShape = new Shape()
      .moveTo( -options.iconLength / 2, -options.iconLength / 2 )
      .lineTo( options.iconLength / 2, options.iconLength / 2 )
      .moveTo( options.iconLength / 2, -options.iconLength / 2 )
      .lineTo( -options.iconLength / 2, options.iconLength / 2 );

    assert && assert( !options.content, 'Dialog.CloseButton sets content' );

    options.content = new Path( closeButtonShape, {
      stroke: 'black',
      lineCap: 'round',
      lineWidth: 2
    } );

    RectangularPushButton.call( this, options );
  }

  sun.register( 'Dialog.CloseButton', CloseButton );

  inherit( RectangularPushButton, CloseButton );

  return Dialog;
} );<|endoftext|>


<commit_before>// This is called with the results from from FB.getLoginStatus().
function statusChangeCallback(response) {
    console.log('statusChangeCallback');
    console.log(response);
    // The response object is returned with a status field that lets the
    // app know the current login status of the person.
    // Full docs on the response object can be found in the documentation
    // for FB.getLoginStatus().
    if (response.status === 'connected') {
        // Logged into your app and Facebook.
        testAPI();
        document.getElementById('actions').style = "visibility: visible";
    } else if (response.status === 'not_authorized') {
        // The person is logged into Facebook, but not your app.
        document.getElementById('status').innerHTML = 'Přihlašte se do této aplikace.';
    } else {
        // The person is not logged into Facebook, so we're not sure if
        // they are logged into this app or not.
        document.getElementById('status').innerHTML = 'Přihlašte se pomocí Facebooku.';
    }
}

// This function is called when someone finishes with the Login
// Button.  See the onlogin handler attached to it in the sample
// code below.
function checkLoginState() {
    FB.getLoginStatus(function(response) {
        statusChangeCallback(response);
    });
}

window.fbAsyncInit = function() {
    FB.init({
        appId      : '177757152673157',
        cookie     : true,  // enable cookies to allow the server to access
                            // the session
        xfbml      : true,  // parse social plugins on this page
        version    : 'v2.5' // use graph api version 2.5
    });

    // Now that we've initialized the JavaScript SDK, we call
    // FB.getLoginStatus().  This function gets the state of the
    // person visiting this page and can return one of three states to
    // the callback you provide.  They can be:
    //
    // 1. Logged into your app ('connected')
    // 2. Logged into Facebook, but not your app ('not_authorized')
    // 3. Not logged into Facebook and can't tell if they are logged into
    //    your app or not.
    //
    // These three cases are handled in the callback function.

    FB.getLoginStatus(function(response) {
        statusChangeCallback(response);
    });

};

// Load the SDK asynchronously
(function(d, s, id) {
    var js, fjs = d.getElementsByTagName(s)[0];
    if (d.getElementById(id)) return;
    js = d.createElement(s); js.id = id;
    js.src = "//connect.facebook.net/en_US/sdk.js";
    fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));

// Here we run a very simple test of the Graph API after login is
// successful.  See statusChangeCallback() for when this call is made.
function testAPI() {
    console.log('Welcome!  Fetching your information.... ');
    FB.api('/me', function(response) {
        console.log('Successful login for: ' + response.name);
        document.getElementById('status').innerHTML =
            'Thanks for logging in, ' + response.name + '!';
    });
}<commit_msg>viditelnost buttonů určených pouze pro přihlášeného uživatele<commit_after>// This is called with the results from from FB.getLoginStatus().
function statusChangeCallback(response) {
    console.log('statusChangeCallback');
    console.log(response);
    // The response object is returned with a status field that lets the
    // app know the current login status of the person.
    // Full docs on the response object can be found in the documentation
    // for FB.getLoginStatus().
    if (response.status === 'connected') {
        // Logged into your app and Facebook.
        testAPI();
        document.getElementById('actions').style = "visibility: visible";
    } else if (response.status === 'not_authorized') {
        // The person is logged into Facebook, but not your app.
        document.getElementById('status').innerHTML = 'Přihlašte se do této aplikace.';
        document.getElementById('actions').style = "visibility: hidden";
    } else {
        // The person is not logged into Facebook, so we're not sure if
        // they are logged into this app or not.
        document.getElementById('status').innerHTML = 'Přihlašte se pomocí Facebooku.';
        document.getElementById('actions').style = "visibility: hidden";
    }
}

// This function is called when someone finishes with the Login
// Button.  See the onlogin handler attached to it in the sample
// code below.
function checkLoginState() {
    FB.getLoginStatus(function(response) {
        statusChangeCallback(response);
    });
}

window.fbAsyncInit = function() {
    FB.init({
        appId      : '177757152673157',
        cookie     : true,  // enable cookies to allow the server to access
                            // the session
        xfbml      : true,  // parse social plugins on this page
        version    : 'v2.5' // use graph api version 2.5
    });

    // Now that we've initialized the JavaScript SDK, we call
    // FB.getLoginStatus().  This function gets the state of the
    // person visiting this page and can return one of three states to
    // the callback you provide.  They can be:
    //
    // 1. Logged into your app ('connected')
    // 2. Logged into Facebook, but not your app ('not_authorized')
    // 3. Not logged into Facebook and can't tell if they are logged into
    //    your app or not.
    //
    // These three cases are handled in the callback function.

    FB.getLoginStatus(function(response) {
        statusChangeCallback(response);
    });

};

// Load the SDK asynchronously
(function(d, s, id) {
    var js, fjs = d.getElementsByTagName(s)[0];
    if (d.getElementById(id)) return;
    js = d.createElement(s); js.id = id;
    js.src = "//connect.facebook.net/en_US/sdk.js";
    fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));

// Here we run a very simple test of the Graph API after login is
// successful.  See statusChangeCallback() for when this call is made.
function testAPI() {
    console.log('Welcome!  Fetching your information.... ');
    FB.api('/me', function(response) {
        console.log('Successful login for: ' + response.name);
        document.getElementById('status').innerHTML =
            'Thanks for logging in, ' + response.name + '!';
    });
}<|endoftext|>


<commit_before>// Declare global variables
var mainTimer = document.getElementById('startTimer');
var timerState = document.getElementById('displayTimer');

// Event handler for user clicks on main timer
mainTimer.addEventListener('click', startTimer);

function startTimer() {
    if (timerState.style.zIndex == '-1') {
        timerState.style.zIndex = '1';
    } else {
        timerState.style.zIndex = '-1';
    }

}<commit_msg>add start timer function<commit_after>// Declare global variables
var mainTimer = document.getElementById('startTimer');
var timerState = document.getElementById('displayTimer');
var currentTimer = document.getElementById('displayTimer').innerHTML;
console.log(currentTimer);

// Event handler for user clicks on main timer
mainTimer.addEventListener('click', activateTimer);

function activateTimer() {
    if (timerState.style.zIndex == '-1') {
        timerState.style.zIndex = '1';
    } else {
        timerState.style.zIndex = '-1';
    }

}

function startTimer() {
    if (timerState.style.zIndex == '-1') {

    }
}<|endoftext|>


<commit_before>/*
* Copyright (c) 2014 Stefan Marr, mail@stefan-marr.de
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
function makeLexerState() {
    return {
        lineNumber :    0,
        charsRead  :    0, // all characters read, excluding the current line
        line       :   "",
        linePos    :    0,
        sym        : null,
        text       :   "",
        startCoord : null,
        set        : function (sym, text) {
            this.sym  = sym;
            this.text = text;
        }
    }
}

function cloneLexerState(old) {
    return {
        lineNumber : old.lineNumber,
        charsRead  : old.charsRead, // all characters read, excluding the current line
        line       : old.line,
        linePos    : old.linePos,
        sym        : old.sym,
        text       : old.text,
        startCoord : old.startCoord,
        set        : function (sym, text) {
            this.sym  = sym;
            this.text = text;
        }
    }
}

function Lexer(fileContent) {
    var SEPARATOR = "----",
        PRIMITIVE = "primitive",
        peekDone  = false,
        state     = makeLexerState(),
        stateAfterPeek = null,
        fileLines = fileContent.split('\n'),
        _this = this;

    function makeSourceCoordinate() {
        var startLine   = state.lineNumber,
            startColumn = state.linePos + 1,
            charIndex   = state.charsRead + state.linePos;

        return {
            get startLine()   { return startLine;   },
            get startColumn() { return startColumn; },
            get charIndex()   { return charIndex;   },
            toString: function() {
                return "SrcCoord(line: " + startLine + ", col: " + startColumn + ")";
            }
        };
    }

    this.getStartCoordinate = function () {
        return state.startCoord;
    };

    this.getSym = function () {
        if (peekDone) {
            peekDone = false;
            state = stateAfterPeek;
            stateAfterPeek = null;
            return state.sym;
        }

        state.startCoord = makeSourceCoordinate();

        do {
            if (!hasMoreInput()) {
                state.set(Sym.NONE, "");
                return state.sym;
            }
            skipWhiteSpace();
            skipComment();
        }
        while (endOfLine() || /\s/.test(currentChar())
            || currentChar() == '"');

        if (currentChar() == '\'') {
            lexString();
        } else if (currentChar() == '[') {
            match(Sym.NewBlock);
        } else if (currentChar() == ']') {
            match(Sym.EndBlock);
        } else if (currentChar() == ':') {
            if (bufchar(state.linePos + 1) == '=') {
                state.linePos += 2;
                state.set(Sym.Assign, ":=");
            } else {
                state.linePos++;
                state.set(Sym.Colon, ":");
            }
        } else if (currentChar() == '(') {
            match(Sym.NewTerm);
        } else if (currentChar() == ')') {
            match(Sym.EndTerm);
        } else if (currentChar() == '#') {
            match(Sym.Pound);
        } else if (currentChar() == '^') {
            match(Sym.Exit);
        } else if (currentChar() == '.') {
            match(Sym.Period);
        } else if (currentChar() == '-') {
            if (state.line.indexOf(SEPARATOR, state.linePos) == state.linePos) {
                state.text = "";
                while (currentChar() == '-') {
                    state.text += bufchar(state.linePos++);
                }
                state.sym = Sym.Separator;
            } else {
                state.linePos++;
                state.set(Sym.Minus, "-");
            }
        } else if (isOperator(currentChar())) {
            lexOperator();
        } else if (nextWordInBufferIs(PRIMITIVE)) {
            state.linePos += PRIMITIVE.length;
            state.set(Sym.Primitive, PRIMITIVE);
        } else if (/[A-Za-z]/.test(currentChar())) {
            state.set(Sym.Identifier, "");
            while (isIdentifierChar(currentChar())) {
                state.text += bufchar(state.linePos++);
            }
            if (bufchar(state.linePos) == ':') {
                state.sym = Sym.Keyword;
                state.linePos++;
                state.text += ':';
                if (/[A-Za-z]/.test(currentChar())) {
                    state.sym = Sym.KeywordSequence;
                    while (/[A-Za-z]/.test(currentChar()) || currentChar() == ':') {
                        state.text += bufchar(state.linePos++);
                    }
                }
            }
        } else if (/\d/.test(currentChar())) {
            lexNumber();
        } else {
            state.set(Sym.NONE, currentChar());
        }

        return state.sym;
    };

    function lexNumber() {
        state.set(Sym.Integer, "");

        var sawDecimalMark = false;

        do {
            state.text += bufchar(state.linePos++);

            if (!sawDecimalMark      &&
                '.' == currentChar() &&
                /\d/.test(bufchar(state.linePos + 1))) {
                state.sym = Sym.Double;
                state.text += bufchar(state.linePos++);
            }
        } while (/\d/.test(currentChar()));
    }

    function lexString() {
        state.set(Sym.STString, "");

        do {
            state.text += bufchar(++state.linePos);
        }
        while (currentChar() != '\'');

        state.text = state.text.slice(0, -1);
        state.linePos++;
    }

    function lexOperator() {
        if (isOperator(bufchar(state.linePos + 1))) {
            state.set(Sym.OperatorSequence, "");
            while (isOperator(currentChar())) {
                state.text += bufchar(state.linePos++);
            }
        } else if (currentChar() == '~') {
            match(Sym.Not);
        } else if (currentChar() == '&') {
            match(Sym.And);
        } else if (currentChar() == '|') {
            match(Sym.Or);
        } else if (currentChar() == '*') {
            match(Sym.Star);
        } else if (currentChar() == '/') {
            match(Sym.Div);
        } else if (currentChar() == '\\') {
            match(Sym.Mod);
        } else if (currentChar() == '+') {
            match(Sym.Plus);
        } else if (currentChar() == '=') {
            match(Sym.Equal);
        } else if (currentChar() == '>') {
            match(Sym.More);
        } else if (currentChar() == '<') {
            match(Sym.Less);
        } else if (currentChar() == ',') {
            match(Sym.Comma);
        } else if (currentChar() == '@') {
            match(Sym.At);
        } else if (currentChar() == '%') {
            match(Sym.Per);
        }
    }

    this.peek = function () {
        var old = cloneLexerState(state);
        if (peekDone) {
            throw new IllegalStateException("SOM lexer: cannot peek twice!");
        }
        _this.getSym();
        var nextSym = state.sym;
        stateAfterPeek = state;
        state = old;

        peekDone = true;
        return nextSym;
    };

    this.getText = function () {
        return state.text;
    };

    this.getNextText = function () {
        return stateAfterPeek.text;
    };

    this.getCurrentLine = function () {
        return state.line;
    };

    this.getCurrentLineNumber = function () {
        return state.lineNumber;
    };

    this.getCurrentColumn = function () {
        return state.linePos + 1;
    };

    // All characters read and processed, including current line
    this.getNumberOfCharactersRead = function () {
        return state.startCoord.charIndex;
    };

    function readNextLine() {
        if (state.lineNumber >= fileLines.length) { return false; }

        var charCntOldLine = state.line.length;
        if (state.lineNumber > 0) { charCntOldLine++; } // add +1 for line break
        state.line = fileLines[state.lineNumber];
        state.charsRead = charCntOldLine;
        state.lineNumber++;
        state.linePos = 0;
        return true;
    }

    function hasMoreInput() {
        while (endOfLine()) {
            if (!readNextLine()) {
                return false;
            }
        }
        return true;
    }

    function skipWhiteSpace() {
        while (/\s/.test(currentChar())) {
            state.linePos++;
            while (endOfLine()) {
                if (!readNextLine()) {
                    return;
                }
            }
        }
    }

    function skipComment() {
        if (currentChar() == '"') {
            do {
                state.linePos++;
                while (endOfLine()) {
                    if (!readNextLine()) { return; }
                }
            } while (currentChar() != '"');
            state.linePos++;
        }
    }

    function currentChar() {
        return bufchar(state.linePos);
    }

    function endOfLine() {
        return state.linePos >= state.line.length;
    }

    function isOperator(c) {
        return c == '~'  || c == '&' || c == '|' || c == '*' || c == '/'
            || c == '\\' || c == '+' || c == '=' || c == '>' || c == '<'
            || c == ','  || c == '@' || c == '%';
    }

    function match(s) {
        state.set(s, currentChar());
        state.linePos++;
    }

    function bufchar(p) {
        return p >= state.line.length ? '\0' : state.line.charAt(p);
    }

    function isIdentifierChar(c) {
        return /[A-Za-z\d]/.test(c) || c == '_';
    }

    function nextWordInBufferIs(text) {
        if (state.line.indexOf(text, state.linePos) != state.linePos) {
            return false;
        }
        return !isIdentifierChar(bufchar(state.linePos + text.length));
    }
}
<commit_msg>Make - a proper operator<commit_after>/*
* Copyright (c) 2014 Stefan Marr, mail@stefan-marr.de
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
function makeLexerState() {
    return {
        lineNumber :    0,
        charsRead  :    0, // all characters read, excluding the current line
        line       :   "",
        linePos    :    0,
        sym        : null,
        text       :   "",
        startCoord : null,
        set        : function (sym, text) {
            this.sym  = sym;
            this.text = text;
        }
    }
}

function cloneLexerState(old) {
    return {
        lineNumber : old.lineNumber,
        charsRead  : old.charsRead, // all characters read, excluding the current line
        line       : old.line,
        linePos    : old.linePos,
        sym        : old.sym,
        text       : old.text,
        startCoord : old.startCoord,
        set        : function (sym, text) {
            this.sym  = sym;
            this.text = text;
        }
    }
}

function Lexer(fileContent) {
    var SEPARATOR = "----",
        PRIMITIVE = "primitive",
        peekDone  = false,
        state     = makeLexerState(),
        stateAfterPeek = null,
        fileLines = fileContent.split('\n'),
        _this = this;

    function makeSourceCoordinate() {
        var startLine   = state.lineNumber,
            startColumn = state.linePos + 1,
            charIndex   = state.charsRead + state.linePos;

        return {
            get startLine()   { return startLine;   },
            get startColumn() { return startColumn; },
            get charIndex()   { return charIndex;   },
            toString: function() {
                return "SrcCoord(line: " + startLine + ", col: " + startColumn + ")";
            }
        };
    }

    this.getStartCoordinate = function () {
        return state.startCoord;
    };

    this.getSym = function () {
        if (peekDone) {
            peekDone = false;
            state = stateAfterPeek;
            stateAfterPeek = null;
            return state.sym;
        }

        state.startCoord = makeSourceCoordinate();

        do {
            if (!hasMoreInput()) {
                state.set(Sym.NONE, "");
                return state.sym;
            }
            skipWhiteSpace();
            skipComment();
        }
        while (endOfLine() || /\s/.test(currentChar())
            || currentChar() == '"');

        if (currentChar() == '\'') {
            lexString();
        } else if (currentChar() == '[') {
            match(Sym.NewBlock);
        } else if (currentChar() == ']') {
            match(Sym.EndBlock);
        } else if (currentChar() == ':') {
            if (bufchar(state.linePos + 1) == '=') {
                state.linePos += 2;
                state.set(Sym.Assign, ":=");
            } else {
                state.linePos++;
                state.set(Sym.Colon, ":");
            }
        } else if (currentChar() == '(') {
            match(Sym.NewTerm);
        } else if (currentChar() == ')') {
            match(Sym.EndTerm);
        } else if (currentChar() == '#') {
            match(Sym.Pound);
        } else if (currentChar() == '^') {
            match(Sym.Exit);
        } else if (currentChar() == '.') {
            match(Sym.Period);
        } else if (currentChar() == '-') {
            if (state.line.indexOf(SEPARATOR, state.linePos) == state.linePos) {
                state.text = "";
                while (currentChar() == '-') {
                    state.text += bufchar(state.linePos++);
                }
                state.sym = Sym.Separator;
            } else {
                lexOperator();
            }
        } else if (isOperator(currentChar())) {
            lexOperator();
        } else if (nextWordInBufferIs(PRIMITIVE)) {
            state.linePos += PRIMITIVE.length;
            state.set(Sym.Primitive, PRIMITIVE);
        } else if (/[A-Za-z]/.test(currentChar())) {
            state.set(Sym.Identifier, "");
            while (isIdentifierChar(currentChar())) {
                state.text += bufchar(state.linePos++);
            }
            if (bufchar(state.linePos) == ':') {
                state.sym = Sym.Keyword;
                state.linePos++;
                state.text += ':';
                if (/[A-Za-z]/.test(currentChar())) {
                    state.sym = Sym.KeywordSequence;
                    while (/[A-Za-z]/.test(currentChar()) || currentChar() == ':') {
                        state.text += bufchar(state.linePos++);
                    }
                }
            }
        } else if (/\d/.test(currentChar())) {
            lexNumber();
        } else {
            state.set(Sym.NONE, currentChar());
        }

        return state.sym;
    };

    function lexNumber() {
        state.set(Sym.Integer, "");

        var sawDecimalMark = false;

        do {
            state.text += bufchar(state.linePos++);

            if (!sawDecimalMark      &&
                '.' == currentChar() &&
                /\d/.test(bufchar(state.linePos + 1))) {
                state.sym = Sym.Double;
                state.text += bufchar(state.linePos++);
            }
        } while (/\d/.test(currentChar()));
    }

    function lexString() {
        state.set(Sym.STString, "");

        do {
            state.text += bufchar(++state.linePos);
        }
        while (currentChar() != '\'');

        state.text = state.text.slice(0, -1);
        state.linePos++;
    }

    function lexOperator() {
        if (isOperator(bufchar(state.linePos + 1))) {
            state.set(Sym.OperatorSequence, "");
            while (isOperator(currentChar())) {
                state.text += bufchar(state.linePos++);
            }
        } else if (currentChar() == '~') {
            match(Sym.Not);
        } else if (currentChar() == '&') {
            match(Sym.And);
        } else if (currentChar() == '|') {
            match(Sym.Or);
        } else if (currentChar() == '*') {
            match(Sym.Star);
        } else if (currentChar() == '/') {
            match(Sym.Div);
        } else if (currentChar() == '\\') {
            match(Sym.Mod);
        } else if (currentChar() == '+') {
            match(Sym.Plus);
        } else if (currentChar() == '=') {
            match(Sym.Equal);
        } else if (currentChar() == '>') {
            match(Sym.More);
        } else if (currentChar() == '<') {
            match(Sym.Less);
        } else if (currentChar() == ',') {
            match(Sym.Comma);
        } else if (currentChar() == '@') {
            match(Sym.At);
        } else if (currentChar() == '%') {
            match(Sym.Per);
        } else if (currentChar() == '-') {
            match(Sym.Minus);
        }
    }

    this.peek = function () {
        var old = cloneLexerState(state);
        if (peekDone) {
            throw new IllegalStateException("SOM lexer: cannot peek twice!");
        }
        _this.getSym();
        var nextSym = state.sym;
        stateAfterPeek = state;
        state = old;

        peekDone = true;
        return nextSym;
    };

    this.getText = function () {
        return state.text;
    };

    this.getNextText = function () {
        return stateAfterPeek.text;
    };

    this.getCurrentLine = function () {
        return state.line;
    };

    this.getCurrentLineNumber = function () {
        return state.lineNumber;
    };

    this.getCurrentColumn = function () {
        return state.linePos + 1;
    };

    // All characters read and processed, including current line
    this.getNumberOfCharactersRead = function () {
        return state.startCoord.charIndex;
    };

    function readNextLine() {
        if (state.lineNumber >= fileLines.length) { return false; }

        var charCntOldLine = state.line.length;
        if (state.lineNumber > 0) { charCntOldLine++; } // add +1 for line break
        state.line = fileLines[state.lineNumber];
        state.charsRead = charCntOldLine;
        state.lineNumber++;
        state.linePos = 0;
        return true;
    }

    function hasMoreInput() {
        while (endOfLine()) {
            if (!readNextLine()) {
                return false;
            }
        }
        return true;
    }

    function skipWhiteSpace() {
        while (/\s/.test(currentChar())) {
            state.linePos++;
            while (endOfLine()) {
                if (!readNextLine()) {
                    return;
                }
            }
        }
    }

    function skipComment() {
        if (currentChar() == '"') {
            do {
                state.linePos++;
                while (endOfLine()) {
                    if (!readNextLine()) { return; }
                }
            } while (currentChar() != '"');
            state.linePos++;
        }
    }

    function currentChar() {
        return bufchar(state.linePos);
    }

    function endOfLine() {
        return state.linePos >= state.line.length;
    }

    function isOperator(c) {
        return c == '~'  || c == '&' || c == '|' || c == '*' || c == '/'
            || c == '\\' || c == '+' || c == '=' || c == '>' || c == '<'
            || c == ','  || c == '@' || c == '%' || c == '-';
    }

    function match(s) {
        state.set(s, currentChar());
        state.linePos++;
    }

    function bufchar(p) {
        return p >= state.line.length ? '\0' : state.line.charAt(p);
    }

    function isIdentifierChar(c) {
        return /[A-Za-z\d]/.test(c) || c == '_';
    }

    function nextWordInBufferIs(text) {
        if (state.line.indexOf(text, state.linePos) != state.linePos) {
            return false;
        }
        return !isIdentifierChar(bufchar(state.linePos + text.length));
    }
}
<|endoftext|>


<commit_before>sources = [
    "//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js",
    "//raw.githubusercontent.com/audibleblink/boom/ajax-configs/lib/bookmarkletsConfig.js"
];

Boom();

function insertJs(url){
    var js = document.createElement('script');
    js.src = url;
    document.head.appendChild(js);
}

for(var i = 0; i < sources.length; i++){
    insertJs(sources[i])
}

function Boom() {
    var requestedBookmarklet = window.prompt('Boom: Enter your Bookmarklet\nType help to see all available options');
    var boomMarklets = CONFIG['bookmarklets'];
    var availableBookmarklets = Object.keys(boomMarklets);
    console.log($.inArray(requestedBookmarklet, availableBookmarklets))

    if (requestedBookmarklet === "help") {
        alert("The available bookmarklets for this version are:\n" + "\n" + availableBookmarklets.join("\n"))
    } else if ($.inArray(requestedBookmarklet, availableBookmarklets) !== -1) {
        window.location = boomMarklets[requestedBookmarklet];
    } else {
        alert("Bookmarklet not found")
    }
};
<commit_msg>appends necessary script to run<commit_after>var requestedBookmarklet = window.prompt('Boom: Enter your Bookmarklet\nType help to see all available options'),
    sources = {
        bookmarkletsConfig: "//rawgit.com/audibleblink/boom/ajax-configs/lib/bookmarkletsConfig.js",
        jquery: "//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"
    },
    sourceKeys = Object.keys(sources);

function insertMissingScripts(word){
    var scriptList = document.getElementsByTagName('script'),
        results = [];

    for(var i = 0; i < scriptList.length; i++){
        if (scriptList[i].src.match(new RegExp(word))){
            results.push(true);
        } else {
            results.push(false);
        }
    }

    if (results.indexOf(true) === -1){
        appendScript(sources[word]);
    }
}

function appendScript(url){
    var js = document.createElement('script');
    js.src = url;
    document.head.appendChild(js);
}

for (var x = 0; x < sourceKeys.length; x++){
    insertMissingScripts(sourceKeys[x]);
}

setTimeout(function Boom() {
    var boomMarklets = CONFIG,
        availableBookmarklets = Object.keys(boomMarklets);

    if (requestedBookmarklet === "help") {
        alert("The available bookmarklets for this version are:\n" + "\n" + availableBookmarklets.join("\n"));
    } else if ($.inArray(requestedBookmarklet, availableBookmarklets) !== -1) {
        window.location = boomMarklets[requestedBookmarklet];
    } else {
        alert("Bookmarklet not found");
    }

    return Boom;

}, 200);
<|endoftext|>


<commit_before>import Ember from 'ember';
import ajax from 'ic-ajax';

export default Ember.Route.extend({
    refreshAfterLogin: Ember.observer('session.isLoggedIn', function() {
        this.refresh();
    }),

    model(params) {
        const requestedVersion = params.version_num === 'all' ? '' : params.version_num;

        const crate = this.modelFor('crate');
        const controller = this.controllerFor(this.routeName);
        const maxVersion = crate.get('max_version');

        const isUnstableVersion = version => {
            const versionLen = version.length;
            let majorMinorPatchChars = 0;
            let result = false;

            for (let i = 0; i < versionLen; i++) {
                const char = version.charAt(i);

                if (!isNaN(parseInt(char)) || char === '.') {
                    majorMinorPatchChars++;
                } else {
                    break;
                }
            }

            if (versionLen !== majorMinorPatchChars) {
                result = true;
            }

            return result;
        };

        // Fallback to the crate's last stable version
        // If `max_version` is `0.0.0` then all versions have been yanked
        if (!requestedVersion && maxVersion !== '0.0.0') {
            if (isUnstableVersion(maxVersion)) {
                crate.get('versions').then(versions => {
                    const latestStableVersion = versions.find(version => {
                        if (!isUnstableVersion(version.get('num'))) {
                            return version;
                        }
                    });

                    if (latestStableVersion == null) {
                        // If no stable version exists, fallback to `maxVersion`
                        params.version_num = maxVersion;
                    } else {
                        params.version_num = latestStableVersion.get('num');
                    }
                });
            } else {
                params.version_num = maxVersion;
            }
        }

        controller.set('crate', crate);
        controller.set('requestedVersion', requestedVersion);
        controller.set('fetchingFollowing', true);

        if (this.session.get('currentUser')) {
            ajax(`/api/v1/crates/${crate.get('name')}/following`)
                .then((d) => controller.set('following', d.following))
                .finally(() => controller.set('fetchingFollowing', false));
        }

        // Find version model
        return crate.get('versions')
            .then(versions => {
                const version = versions.find(version => version.get('num') === params.version_num);
                if (params.version_num && !version) {
                    this.controllerFor('application').set('nextFlashError',
                        `Version '${params.version_num}' of crate '${crate.get('name')}' does not exist`);
                }

                return version ||
                    versions.find(version => version.get('num') === maxVersion) ||
                    versions.objectAt(0);
            });
    },

    serialize(model) {
        let version_num = model ? model.get('num') : '';
        return { version_num };
    },
});
<commit_msg>Add docs.rs integration<commit_after>import Ember from 'ember';
import ajax from 'ic-ajax';

export default Ember.Route.extend({
    refreshAfterLogin: Ember.observer('session.isLoggedIn', function() {
        this.refresh();
    }),

    model(params) {
        const requestedVersion = params.version_num === 'all' ? '' : params.version_num;

        const crate = this.modelFor('crate');
        const controller = this.controllerFor(this.routeName);
        const maxVersion = crate.get('max_version');

        const isUnstableVersion = version => {
            const versionLen = version.length;
            let majorMinorPatchChars = 0;
            let result = false;

            for (let i = 0; i < versionLen; i++) {
                const char = version.charAt(i);

                if (!isNaN(parseInt(char)) || char === '.') {
                    majorMinorPatchChars++;
                } else {
                    break;
                }
            }

            if (versionLen !== majorMinorPatchChars) {
                result = true;
            }

            return result;
        };

        // Fallback to the crate's last stable version
        // If `max_version` is `0.0.0` then all versions have been yanked
        if (!requestedVersion && maxVersion !== '0.0.0') {
            if (isUnstableVersion(maxVersion)) {
                crate.get('versions').then(versions => {
                    const latestStableVersion = versions.find(version => {
                        if (!isUnstableVersion(version.get('num'))) {
                            return version;
                        }
                    });

                    if (latestStableVersion == null) {
                        // If no stable version exists, fallback to `maxVersion`
                        params.version_num = maxVersion;
                    } else {
                        params.version_num = latestStableVersion.get('num');
                    }
                });
            } else {
                params.version_num = maxVersion;
            }
        }

        controller.set('crate', crate);
        controller.set('requestedVersion', requestedVersion);
        controller.set('fetchingFollowing', true);

        if (this.session.get('currentUser')) {
            ajax(`/api/v1/crates/${crate.get('name')}/following`)
                .then((d) => controller.set('following', d.following))
                .finally(() => controller.set('fetchingFollowing', false));
        }

        if (!crate.get('documentation')) {
            let crateName = crate.get('name');
            let crateVersion = params.version_num;
            ajax(`https://docs.rs/crate/${crateName}/${crateVersion}/builds.json`)
                .then((r) => {
                    if (r.length > 0 && r[0].build_status === true) {
                        crate.set('documentation', `https://docs.rs/${crateName}/${crateVersion}/${crateName}/`);
                    }
                });
        }

        // Find version model
        return crate.get('versions')
            .then(versions => {
                const version = versions.find(version => version.get('num') === params.version_num);
                if (params.version_num && !version) {
                    this.controllerFor('application').set('nextFlashError',
                        `Version '${params.version_num}' of crate '${crate.get('name')}' does not exist`);
                }

                return version ||
                    versions.find(version => version.get('num') === maxVersion) ||
                    versions.objectAt(0);
            });
    },

    serialize(model) {
        let version_num = model ? model.get('num') : '';
        return { version_num };
    },
});
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	User = mongoose.model('User'),
	utils = require('./utils');

/**
 * Find all users
 */
exports.all = function(req, res) {
	var query = {};

	var Query = User.find(query);
	Query.limit(200 || req.query.limit);
	Query.exec(function(err, users) {

		utils.checkAndHandleError(err, res, 'Failed to load users');

		res.status(200);
		return res.json(users);
	});
};

exports.read = function(req, res) {
	User.findById(req.params.id, function(err, user) {
		utils.checkAndHandleError(err, res, 'Failed to load user');
		res.status(200);
		return res.json(user);
	});
};
exports.getByEntity = function(req, res) { //It is a temporary function. need to change this function to use elasticsearch!!!!
	var query = {
		_id: req.params.id
	};
	var model = (req.params.entity.charAt(0).toUpperCase() + req.params.entity.slice(1)).substring(0, req.params.entity.length - 1);
	var Query = mongoose.model(model).findOne(query);
	Query.exec(function(err, project) {
		if (err || !project) utils.checkAndHandleError(err ? err : !project, res, 'Failed to load project with id: ' + req.params.id);
		else {
			var userIds = project.watchers;
			userIds.push(project.creator);
			User.find({
				_id: {
					$in: userIds
				}
			}).exec(function(err, users) {
				res.status(200);
				return res.json(users);
			});
		}
	});
};<commit_msg>fix for swagger<commit_after>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	User = mongoose.model('User'),
	utils = require('./utils');

/**
 * Find all users
 */
exports.all = function(req, res) {
	var query = {};

	var Query = User.find(query);
	Query.limit(200 || req.query.limit);
	Query.exec(function(err, users) {

		utils.checkAndHandleError(err, res, 'Failed to load users');

		res.status(200);
		return res.json(users);
	});
};

exports.read = function(req, res) {
	User.findById(req.params.id, function(err, user) {
		utils.checkAndHandleError(err, res, 'Failed to load user');
		res.status(200);
		return res.json(user);
	});
};
exports.getByEntity = function(req, res) { //It is a temporary function. need to change this function to use elasticsearch!!!!
	res.status(200);
	return res.json([]);
	var query = {
		_id: req.params.id
	};
	var model = (req.params.entity.charAt(0).toUpperCase() + req.params.entity.slice(1)).substring(0, req.params.entity.length - 1);
	var Query = mongoose.model(model).findOne(query);
	Query.exec(function(err, project) {
		if (err || !project) utils.checkAndHandleError(err ? err : !project, res, 'Failed to load project with id: ' + req.params.id);
		else {
			var userIds = project.watchers;
			userIds.push(project.creator);
			User.find({
				_id: {
					$in: userIds
				}
			}).exec(function(err, users) {
				res.status(200);
				return res.json(users);
			});
		}
	});
};<|endoftext|>