<commit_before>// Copyright 2015-2016, University of Colorado Boulder

/**
 * An instance that is synchronously created, for handling accessibility needs.
 *
 * @author <NAME> <<EMAIL>>
 */

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

  // var AccessiblePeer = require( 'SCENERY/accessibility/AccessiblePeer' );
  var cleanArray = require( 'PHET_CORE/cleanArray' );
  // var Display = require( 'SCENERY/display/Display' );
  var Events = require( 'AXON/Events' );
  var inherit = require( 'PHET_CORE/inherit' );
  var KeyboardUtil = require( 'SCENERY/accessibility/KeyboardUtil' );
  var platform = require( 'PHET_CORE/platform' );
  var Poolable = require( 'PHET_CORE/Poolable' );
  var scenery = require( 'SCENERY/scenery' );

  var globalId = 1;

  /**
   * @constructor
   * @mixes Poolable
   *
   * @param parent
   * @param display
   * @param trail
   */
  function AccessibleInstance( parent, display, trail ) {
    this.initializeAccessibleInstance( parent, display, trail );
  }

  scenery.register( 'AccessibleInstance', AccessibleInstance );

  inherit( Events, AccessibleInstance, {
    /**
     * @param {AccessibleInstance|null} parent
     * @param {Display} display
     * @param {HTMLElement} [domElement] - If not included here, subtype is responsible for setting it in the constructor.
     * @returns {AccessibleInstance} - Returns 'this' reference, for chaining
     */
    initializeAccessibleInstance: function( parent, display, trail ) {
      Events.call( this ); // TODO: is Events worth mixing in by default? Will we need to listen to events?

      assert && assert( !this.id || this.disposed, 'If we previously existed, we need to have been disposed' );

      // unique ID
      this.id = this.id || globalId++;

      this.parent = parent;
      this.display = display;
      this.trail = trail;
      this.node = trail.lastNode();
      this.isRootInstance = this.trail.length === 0;

      // TODO: doc
      this.children = cleanArray( this.children );

      // If we are the root accessible instance, we won't actually have a reference to a node.
      if ( this.node ) {
        this.node.addAccessibleInstance( this );
      }

      this.isSorted = true;

      if ( this.isRootInstance ) {
        var accessibilityContainer = document.createElement( 'div' );

        // give the container a class name so it is hidden in the Display, see accessibility styling in Display.js
        accessibilityContainer.className = 'accessibility';
        this.peer = new scenery.AccessiblePeer( this, accessibilityContainer );

        var self = this;
        document.body.addEventListener( 'keydown', function( event ) {

          // if an accessible node was being interacted with a mouse, or had focus when sim is made inactive, this node
          // should receive focus upon resuming keyboard navigation
          if ( self.display.pointerFocus || self.display.activeNode ) {
            var active = self.display.pointerFocus || self.display.activeNode;
            var focusable = active.focusable;
            var visible = active.visible;
            var inDOM = active.getAccessibleInstances().length > 0;
            if ( focusable && visible && inDOM ) {
              if ( event.keyCode === KeyboardUtil.KEY_TAB ) {
                event.preventDefault();
                active.focus();
                self.display.pointerFocus = null;
                self.display.activeNode = null;
              }
            }
          }
        } );
      }
      else {
        this.peer = this.node.accessibleContent.createPeer( this );
        var childContainerElement = this.parent.peer.getChildContainerElement();

        // insert the peer's DOM element or its parent if it is contained in a parent element for structure
        childContainerElement.insertBefore( this.peer.getParentContainerElement(), childContainerElement.childNodes[ 0 ] );

        // get the difference between the trails of the parent and this AccessibleInstance
        var parentTrail = this.parent.trail;
        var thisTrail = this.trail;
        this.trailDiff = [];
        for ( var i = parentTrail.length; i < thisTrail.length; i++ ) {
          this.trailDiff.push( thisTrail.get( i ) );
        }

        // when visibility or accessibleVisibility of a node in between the two trails changes, we must update
        // visibility of this peer's DOM content
        this.accessibleVisibilityListener = this.updateVisibility.bind( this );
        for ( var j = 0; j < this.trailDiff.length; j++ ) {
          this.trailDiff[ j ].onStatic( 'visibility', this.accessibleVisibilityListener );
          this.trailDiff[ j ].accessibleVisibilityChangedEmitter.addListener( this.accessibleVisibilityListener );
        }
        this.accessibleVisibilityListener();
      }

      sceneryLog && sceneryLog.AccessibleInstance && sceneryLog.AccessibleInstance(
        'Initialized ' + this.toString() );

      return this;
    },

    /**
     * Consider the following example:
     *
     * We have a node structure:
     * A
     *  B ( accessible )
     *    C (accessible )
     *      D
     *        E (accessible)
     *         G (accessible)
     *        F
     *          H (accessible)
     *
     *
     * Which has an equivalent accessible instance tree:
     * root
     *  AB
     *    ABC
     *      ABCDE
     *        ABCDEG
     *      ABCDFH
     *
     * Produces the call tree for adding instances to the accessible instance tree:
     * ABC.addSubtree( ABCD ) - not accessible
     *     ABC.addSubtree( ABCDE)
     *       ABCDE.addSubtree( ABCDEG )
     *     ABC.addSubtree( ABCDF )
     *       ABC.addSubtree( ABCDFH )
     */
    addSubtree: function( trail ) {
      sceneryLog && sceneryLog.AccessibleInstance && sceneryLog.AccessibleInstance(
        'addSubtree on ' + this.toString() + ' with trail ' + trail.toString() );
      sceneryLog && sceneryLog.AccessibleInstance && sceneryLog.push();

      var node = trail.lastNode();
      var nextInstance = this; // eslint-disable-line consistent-this
      if ( node.accessibleContent ) {
        var accessibleInstance = AccessibleInstance.createFromPool( this, this.display, trail.copy() ); // TODO: Pooling
        sceneryLog && sceneryLog.AccessibleInstance && sceneryLog.AccessibleInstance(
          'Insert parent: ' + this.toString() + ', (new) child: ' + accessibleInstance.toString() );
        this.children.push( accessibleInstance ); // TODO: Mark us as dirty for performance.
        this.markAsUnsorted();

        nextInstance = accessibleInstance;
      }
      var children = node._children;
      for ( var i = 0; i < children.length; i++ ) {
        trail.addDescendant( children[ i ], i );
        nextInstance.addSubtree( trail );
        trail.removeDescendant();
      }

      sceneryLog && sceneryLog.AccessibleInstance && sceneryLog.pop();
    },

    removeSubtree: function( trail ) {
      sceneryLog && sceneryLog.AccessibleInstance && sceneryLog.AccessibleInstance(
        'removeSubtree on ' + this.toString() + ' with trail ' + trail.toString() );
      sceneryLog && sceneryLog.AccessibleInstance && sceneryLog.push();

      for ( var i = this.children.length - 1; i >= 0; i-- ) {
        var childInstance = this.children[ i ];
        if ( childInstance.trail.isExtensionOf( trail, true ) ) {
          sceneryLog && sceneryLog.AccessibleInstance && sceneryLog.AccessibleInstance(
            'Remove parent: ' + this.toString() + ', child: ' + childInstance.toString() );
          this.children.splice( i, 1 ); // remove it from the children array

          // Dispose the entire subtree of AccessibleInstances
          childInstance.dispose();
        }
      }

      sceneryLog && sceneryLog.AccessibleInstance && sceneryLog.pop();
    },

    /**
     * TODO: doc
     */
    markAsUnsorted: function() {
      if ( this.isSorted ) {
        this.isSorted = false;
        this.display.markUnsortedAccessibleInstance( this );
      }
    },

    /**
     * Update visibility of this peer's accessible DOM content. The hidden attribute will hide all of the descendant
     * DOM content, so it is not necessary to update the subtree of AccessibleInstances since the browser
     * will do this for us.
     *
     * @private
     */
    updateVisibility: function() {

      // if all nodes in the trail diff are both visible and accessibleVisible, this AccessibleInstance will be
      // visible for screen readers
      var visibilityCount = 0;
      for ( var i = 0; i < this.trailDiff.length; i++ ) {
        if ( this.trailDiff[ i ].visible && this.trailDiff[ i ].accessibleVisible ) {
          visibilityCount++;
        }
      }

      var parentElement = this.peer.getParentContainerElement();

      var self = this;
      var hideParent = function() {
        parentElement.hidden = !( visibilityCount === self.trailDiff.length );

        // if we hid a parent element, blur focus if active element was an ancestor
        if ( parentElement.hidden ) {
          if ( parentElement.contains( document.activeElement ) ) {
            scenery.Display.focus = null;
          }
        }
      }
      hideParent();

      // wrapping the hiding and bluring in a timeout is a workaround for an Edge bug where unhidden elements remain
      // excluded from the navigation order, see https://github.com/phetsims/a11y-research/issues/30
      platform.edge && window.setTimeout( hideParent, 1 );
    },

    /**
     * Sort our child accessible instances in the order they should appear in the parallel DOM. We do this by
     * creating a comparison function between two accessible instances, and sorting the array with that.
     */
    sortChildren: function() {<commit_msg>lint fix and wrap Edge workaround with platform check, see phetsims/a11y-research#30<commit_after>// Copyright 2015-2016, University of Colorado Boulder

/**
 * An instance that is synchronously created, for handling accessibility needs.
 *
 * @author <NAME> <<EMAIL>>
 */

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

  // var AccessiblePeer = require( 'SCENERY/accessibility/AccessiblePeer' );
  var cleanArray = require( 'PHET_CORE/cleanArray' );
  // var Display = require( 'SCENERY/display/Display' );
  var Events = require( 'AXON/Events' );
  var inherit = require( 'PHET_CORE/inherit' );
  var KeyboardUtil = require( 'SCENERY/accessibility/KeyboardUtil' );
  var platform = require( 'PHET_CORE/platform' );
  var Poolable = require( 'PHET_CORE/Poolable' );
  var scenery = require( 'SCENERY/scenery' );

  var globalId = 1;

  /**
   * @constructor
   * @mixes Poolable
   *
   * @param parent
   * @param display
   * @param trail
   */
  function AccessibleInstance( parent, display, trail ) {
    this.initializeAccessibleInstance( parent, display, trail );
  }

  scenery.register( 'AccessibleInstance', AccessibleInstance );

  inherit( Events, AccessibleInstance, {
    /**
     * @param {AccessibleInstance|null} parent
     * @param {Display} display
     * @param {HTMLElement} [domElement] - If not included here, subtype is responsible for setting it in the constructor.
     * @returns {AccessibleInstance} - Returns 'this' reference, for chaining
     */
    initializeAccessibleInstance: function( parent, display, trail ) {
      Events.call( this ); // TODO: is Events worth mixing in by default? Will we need to listen to events?

      assert && assert( !this.id || this.disposed, 'If we previously existed, we need to have been disposed' );

      // unique ID
      this.id = this.id || globalId++;

      this.parent = parent;
      this.display = display;
      this.trail = trail;
      this.node = trail.lastNode();
      this.isRootInstance = this.trail.length === 0;

      // TODO: doc
      this.children = cleanArray( this.children );

      // If we are the root accessible instance, we won't actually have a reference to a node.
      if ( this.node ) {
        this.node.addAccessibleInstance( this );
      }

      this.isSorted = true;

      if ( this.isRootInstance ) {
        var accessibilityContainer = document.createElement( 'div' );

        // give the container a class name so it is hidden in the Display, see accessibility styling in Display.js
        accessibilityContainer.className = 'accessibility';
        this.peer = new scenery.AccessiblePeer( this, accessibilityContainer );

        var self = this;
        document.body.addEventListener( 'keydown', function( event ) {

          // if an accessible node was being interacted with a mouse, or had focus when sim is made inactive, this node
          // should receive focus upon resuming keyboard navigation
          if ( self.display.pointerFocus || self.display.activeNode ) {
            var active = self.display.pointerFocus || self.display.activeNode;
            var focusable = active.focusable;
            var visible = active.visible;
            var inDOM = active.getAccessibleInstances().length > 0;
            if ( focusable && visible && inDOM ) {
              if ( event.keyCode === KeyboardUtil.KEY_TAB ) {
                event.preventDefault();
                active.focus();
                self.display.pointerFocus = null;
                self.display.activeNode = null;
              }
            }
          }
        } );
      }
      else {
        this.peer = this.node.accessibleContent.createPeer( this );
        var childContainerElement = this.parent.peer.getChildContainerElement();

        // insert the peer's DOM element or its parent if it is contained in a parent element for structure
        childContainerElement.insertBefore( this.peer.getParentContainerElement(), childContainerElement.childNodes[ 0 ] );

        // get the difference between the trails of the parent and this AccessibleInstance
        var parentTrail = this.parent.trail;
        var thisTrail = this.trail;
        this.trailDiff = [];
        for ( var i = parentTrail.length; i < thisTrail.length; i++ ) {
          this.trailDiff.push( thisTrail.get( i ) );
        }

        // when visibility or accessibleVisibility of a node in between the two trails changes, we must update
        // visibility of this peer's DOM content
        this.accessibleVisibilityListener = this.updateVisibility.bind( this );
        for ( var j = 0; j < this.trailDiff.length; j++ ) {
          this.trailDiff[ j ].onStatic( 'visibility', this.accessibleVisibilityListener );
          this.trailDiff[ j ].accessibleVisibilityChangedEmitter.addListener( this.accessibleVisibilityListener );
        }
        this.accessibleVisibilityListener();
      }

      sceneryLog && sceneryLog.AccessibleInstance && sceneryLog.AccessibleInstance(
        'Initialized ' + this.toString() );

      return this;
    },

    /**
     * Consider the following example:
     *
     * We have a node structure:
     * A
     *  B ( accessible )
     *    C (accessible )
     *      D
     *        E (accessible)
     *         G (accessible)
     *        F
     *          H (accessible)
     *
     *
     * Which has an equivalent accessible instance tree:
     * root
     *  AB
     *    ABC
     *      ABCDE
     *        ABCDEG
     *      ABCDFH
     *
     * Produces the call tree for adding instances to the accessible instance tree:
     * ABC.addSubtree( ABCD ) - not accessible
     *     ABC.addSubtree( ABCDE)
     *       ABCDE.addSubtree( ABCDEG )
     *     ABC.addSubtree( ABCDF )
     *       ABC.addSubtree( ABCDFH )
     */
    addSubtree: function( trail ) {
      sceneryLog && sceneryLog.AccessibleInstance && sceneryLog.AccessibleInstance(
        'addSubtree on ' + this.toString() + ' with trail ' + trail.toString() );
      sceneryLog && sceneryLog.AccessibleInstance && sceneryLog.push();

      var node = trail.lastNode();
      var nextInstance = this; // eslint-disable-line consistent-this
      if ( node.accessibleContent ) {
        var accessibleInstance = AccessibleInstance.createFromPool( this, this.display, trail.copy() ); // TODO: Pooling
        sceneryLog && sceneryLog.AccessibleInstance && sceneryLog.AccessibleInstance(
          'Insert parent: ' + this.toString() + ', (new) child: ' + accessibleInstance.toString() );
        this.children.push( accessibleInstance ); // TODO: Mark us as dirty for performance.
        this.markAsUnsorted();

        nextInstance = accessibleInstance;
      }
      var children = node._children;
      for ( var i = 0; i < children.length; i++ ) {
        trail.addDescendant( children[ i ], i );
        nextInstance.addSubtree( trail );
        trail.removeDescendant();
      }

      sceneryLog && sceneryLog.AccessibleInstance && sceneryLog.pop();
    },

    removeSubtree: function( trail ) {
      sceneryLog && sceneryLog.AccessibleInstance && sceneryLog.AccessibleInstance(
        'removeSubtree on ' + this.toString() + ' with trail ' + trail.toString() );
      sceneryLog && sceneryLog.AccessibleInstance && sceneryLog.push();

      for ( var i = this.children.length - 1; i >= 0; i-- ) {
        var childInstance = this.children[ i ];
        if ( childInstance.trail.isExtensionOf( trail, true ) ) {
          sceneryLog && sceneryLog.AccessibleInstance && sceneryLog.AccessibleInstance(
            'Remove parent: ' + this.toString() + ', child: ' + childInstance.toString() );
          this.children.splice( i, 1 ); // remove it from the children array

          // Dispose the entire subtree of AccessibleInstances
          childInstance.dispose();
        }
      }

      sceneryLog && sceneryLog.AccessibleInstance && sceneryLog.pop();
    },

    /**
     * TODO: doc
     */
    markAsUnsorted: function() {
      if ( this.isSorted ) {
        this.isSorted = false;
        this.display.markUnsortedAccessibleInstance( this );
      }
    },

    /**
     * Update visibility of this peer's accessible DOM content. The hidden attribute will hide all of the descendant
     * DOM content, so it is not necessary to update the subtree of AccessibleInstances since the browser
     * will do this for us.
     *
     * @private
     */
    updateVisibility: function() {

      // if all nodes in the trail diff are both visible and accessibleVisible, this AccessibleInstance will be
      // visible for screen readers
      var visibilityCount = 0;
      for ( var i = 0; i < this.trailDiff.length; i++ ) {
        if ( this.trailDiff[ i ].visible && this.trailDiff[ i ].accessibleVisible ) {
          visibilityCount++;
        }
      }

      var parentElement = this.peer.getParentContainerElement();

      var self = this;
      var hideParent = function() {
        parentElement.hidden = !( visibilityCount === self.trailDiff.length );

        // if we hid a parent element, blur focus if active element was an ancestor
        if ( parentElement.hidden ) {
          if ( parentElement.contains( document.activeElement ) ) {
            scenery.Display.focus = null;
          }
        }
      };

      // wrapping the hiding and bluring in a timeout is a workaround for an Edge bug where unhidden elements remain
      // excluded from the navigation order, see https://github.com/phetsims/a11y-research/issues/30
      platform.edge ? window.setTimeout( hideParent, 1 ) : hideParent();
    },

    /**
     * Sort our child accessible instances in the order they should appear in the parallel DOM. We do this by
     * creating a comparison function between two accessible instances, and sorting the array with that.
     */
    sortChildren: function() {
{'fraction_non_alphanumeric': 0.10205542489007056, 'fraction_numerical': 0.0026587585642703754, 'mean_word_length': 2.42060160895418, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 13, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2218343', 'n_tokens_mistral': 5442, 'n_tokens_neox': 5180, 'n_words': 1564}