<commit_before>define(['angular', 'map', 'app'],

    function(angular) {
        var mod = angular.module('hs.panoramio', ['hs.map'])
            .service("PanoramioPictures", ['OlMap', '$http', 'default_layers',
                function(OlMap, $http, default_layers) {
                    var map = OlMap.map;
                    var src = new ol.source.Vector();
                    var csrc = new ol.source.Cluster({
                        distance: 90,
                        source: src
                    });
                    var lyr = null;
                    var me = this;
                    var view = OlMap.map.getView();

                    lyr = new ol.layer.Vector({
                        title: "Panoramio pictures",
                        show_in_manager: true,
                        source: csrc,
                        style: function(feature, resolution) {
                            return feature.get('features')[0].getStyle()
                        }
                    });

                    default_layers.push(lyr);

                    var timer = null;

                    var changed = function(e) {
                        if (timer != null) clearTimeout(timer);
                        timer = setTimeout(function() {
                            me.update(e)
                        }, 500);
                    }

                    OlMap.map.getView().on('change:center', changed);
                    OlMap.map.getView().on('change:resolution', changed);

                    this.featuresReceived = function(panoramio) {
                        var features = [];
                        for (var i = 0; i < panoramio.photos.length; i++) {
                            var upload_date = panoramio.photos[i].upload_date;
                            var owner_name = panoramio.photos[i].owner_name;
                            var photo_id = panoramio.photos[i].photo_id;
                            var longitude = panoramio.photos[i].longitude;
                            var latitude = panoramio.photos[i].latitude;
                            var pheight = panoramio.photos[i].height;
                            var pwidth = panoramio.photos[i].width;
                            var photo_title = panoramio.photos[i].photo_title;
                            var owner_url = panoramio.photos[i].owner_url;
                            var owner_id = panoramio.photos[i].owner_id;
                            var photo_file_url = panoramio.photos[i].photo_file_url;
                            var photo_url = panoramio.photos[i].photo_url;

                            var attributes = {
                                    geometry: new ol.geom.Point(ol.proj.transform([longitude, latitude], 'EPSG:4326', 'EPSG:3857')),
                                    'upload_date': upload_date,
                                    'owner_name': owner_name,
                                    'photo_id': photo_id,
                                    'longitude': longitude,
                                    'latitude': latitude,
                                    'pheight': pheight,
                                    'pwidth': pwidth,
                                    'pheight': pheight,
                                    'photo_title': photo_title,
                                    'owner_url': owner_url,
                                    'owner_id': owner_id,
                                    'photo_file_url': photo_file_url,
                                    'photo_url': photo_url,
                                } //end attributes
                            var feature = new ol.Feature(attributes);
                            feature.setStyle([new ol.style.Style({
                                image: new ol.style.Icon({
                                    src: feature.get('photo_file_url')
                                })
                            })])

                            features.push(feature);
                        }
                        src.clear();
                        src.addFeatures(features);
                    }
                    this.update = function(url) {
                        var b = ol.proj.transform(map.getView().calculateExtent(map.getSize()), 'EPSG:3857', 'EPSG:4326'); // bounds
                        var url = window.escape('http://www.panoramio.com/map/get_panoramas.php?order=popularity&set=full&from=0&to=80&minx=' + b[0] + '&miny=' + b[1] + '&maxx=' + b[2] + '&maxy=' + b[3] + '&size=thumbnail');
                        $.ajax({
                            url: "/cgi-bin/hsproxy.cgi?toEncoding=utf-8&url=" + url,
                            cache: false,
                            success: this.featuresReceived
                        });
                    };
                }
            ])

        .run(function(PanoramioPictures) { // instance-injector
            //Gets executed after service is loaded
        });
    })
<commit_msg>cleanup<commit_after>define(['angular', 'map', 'app'],

    function(angular) {
        var mod = angular.module('hs.panoramio', ['hs.map'])
            .service("PanoramioPictures", ['OlMap', '$http', 'default_layers',
                function(OlMap, $http, default_layers) {
                    var map = OlMap.map;
                    var src = new ol.source.Vector();
                    var csrc = new ol.source.Cluster({
                        distance: 90,
                        source: src
                    });
                    var lyr = null;
                    var me = this;
                    var view = OlMap.map.getView();

                    lyr = new ol.layer.Vector({
                        title: "Panoramio pictures",
                        show_in_manager: true,
                        source: csrc,
                        style: function(feature, resolution) {
                            return feature.get('features')[0].getStyle()
                        }
                    });

                    default_layers.push(lyr);

                    var timer = null;

                    var changed = function(e) {
                        if (timer != null) clearTimeout(timer);
                        timer = setTimeout(function() {
                            me.update(e)
                        }, 500);
                    }

                    OlMap.map.getView().on('change:center', changed);
                    OlMap.map.getView().on('change:resolution', changed);

                    this.featuresReceived = function(panoramio) {
                        var features = [];
                        for (var i = 0; i < panoramio.photos.length; i++) {
                            var p = panoramio.photos[i];
                            var attributes = {
                                    geometry: new ol.geom.Point(ol.proj.transform([p.longitude, p.latitude], 'EPSG:4326', map.getView().getProjection())),
                                    upload_date: p.upload_date,
                                    owner_name: p.owner_name,
                                    photo_id: p.photo_id,
                                    longitude: p.longitude,
                                    latitude: p.latitude,
                                    pheight: p.height,
                                    pwidth: p.width,
                                    photo_title: p.photo_title,
                                    owner_url: p.owner_url,
                                    owner_id: p.owner_id,
                                    photo_file_url: p.photo_file_url,
                                    photo_url: p.photo_url,
                                } //end attributes
                            var feature = new ol.Feature(attributes);
                            feature.setStyle([new ol.style.Style({
                                image: new ol.style.Icon({
                                    src: feature.get('photo_file_url')
                                })
                            })])

                            features.push(feature);
                        }
                        src.clear();
                        src.addFeatures(features);
                    }
                    this.update = function(url) {
                        var b = ol.proj.transform(map.getView().calculateExtent(map.getSize()), map.getView().getProjection(), 'EPSG:4326'); // bounds
                        var limit = Math.floor($(map.getViewport()).width()*$(map.getViewport()).height() / 22280 * 1.2);
                        var url = window.escape('http://www.panoramio.com/map/get_panoramas.php?order=popularity&set=full&from=0&to='+limit+'&minx=' + b[0] + '&miny=' + b[1] + '&maxx=' + b[2] + '&maxy=' + b[3] + '&size=thumbnail');
                        $.ajax({
                            url: "/cgi-bin/hsproxy.cgi?toEncoding=utf-8&url=" + url,
                            cache: false,
                            success: this.featuresReceived
                        });
                    };
                }
            ])

        .run(function(PanoramioPictures) { // instance-injector
            //Gets executed after service is loaded
        });
    })
<|endoftext|>


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

/**
 * An action that can be executed and sent to the PhET-iO data stream, and optionally recorded for playback. This type
 * will also validate the argument types passed to the action function
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const ActionIO = require( 'AXON/ActionIO' );
  const axon = require( 'AXON/axon' );
  const PhetioObject = require( 'TANDEM/PhetioObject' );
  const Tandem = require( 'TANDEM/Tandem' );
  const ValidatorDef = require( 'AXON/ValidatorDef' );
  const validate = require( 'AXON/validate' );

  // constants
  const ActionIOWithNoArgs = ActionIO( [] );

  // Simulations have thousands of Emitters, so we re-use objects where possible.
  const EMPTY_ARRAY = [];
  assert && Object.freeze( EMPTY_ARRAY );

  class Action extends PhetioObject {

    /**
     * @param {function} action - the function that is called when this Action occurs
     * @param {Object} [options]
     */
    constructor( action, options ) {

      // It is important to know if the following options were provided by the client
      const phetioTypeSupplied = options && options.hasOwnProperty( 'phetioType' );
      const validatorsSupplied = options && options.hasOwnProperty( 'validators' );

      // Important to be before super call. OK to supply either or one or the other, but not both. This is a NAND operator.
      assert && assert( !( phetioTypeSupplied && validatorsSupplied ),
        'use either phetioType or validators, not both, see EmitterIO to set validators on an instrumented Action'
      );

      // ActionIO that have 0 args should use the built-in ActionIO([]) default.  But we must support EmitterIO([]),
      // so we guard based on the type name.
      if ( assert && phetioTypeSupplied && options.phetioType.typeName.indexOf( 'ActionIO' ) === 0 ) {
        assert( options.phetioType.parameterTypes.length > 0, 'do not specify phetioType that is the same as the default' );
      }

      options = _.extend( {

        // {ValidatorDef[]}
        validators: EMPTY_ARRAY,

        // {boolean} @deprecated, only to support legacy emit1, emit2, emit3 calls.
        validationEnabled: true,

        // phet-io - see PhetioObject.js for doc
        tandem: Tandem.optional,
        phetioState: false,
        phetioType: ActionIOWithNoArgs, // subtypes can override with ActionIO([...]), see ActionIO.js
        phetioPlayback: PhetioObject.DEFAULT_OPTIONS.phetioPlayback,
        phetioEventMetadata: PhetioObject.DEFAULT_OPTIONS.phetioEventMetadata
      }, options );

      // Use the phetioType's validators if provided, we know we aren't overwriting here because of the above assertion
      if ( phetioTypeSupplied ) {
        options.validators = options.phetioType.validators;
      }

      // phetioPlayback events need to know the order the arguments occur in order to call EmitterIO.emit()
      // Indicate whether the event is for playback, but leave this "sparse"--only indicate when this happens to be true
      if ( options.phetioPlayback ) {
        options.phetioEventMetadata = options.phetioEventMetadata || {};

        assert && assert( !options.phetioEventMetadata.hasOwnProperty( 'dataKeys' ),
          'dataKeys should be supplied by Action, not elsewhere' );

        options.phetioEventMetadata.dataKeys = options.phetioType.elements.map( element => element.name );
      }

      super( options );

      assert && this.validateValidators( options.validators, validatorsSupplied, phetioTypeSupplied );

      // @public (only for testing) - Note: one test indicates stripping this out via assert && in builds may save around 300kb heap
      this.validators = options.validators;

      // @private - opt out of validation. Can be removed when deprecated emit functions are gone.
      this.validationEnabled = options.validationEnabled;

      assert && assert( typeof action === 'function', 'action should be a function' );

      // @private {function}
      this._action = action;
    }

    /**
     * @private
     * @param {ValidatorDef[]} validators
     * @param {boolean} validatorsSuppliedByClient - true if the validators option was passed into the constructor via options,
     *                                               false if using the default from the options extend call
     * @param phetioTypeSuppliedByClient - true if the phetioType option was passed into the constructor via options,
     *                                     false if using the default from the options extend call
     */
    validateValidators( validators, validatorsSuppliedByClient, phetioTypeSuppliedByClient ) {

      // validate the validators object
      validate( validators, { valueType: Array } );

      this.isPhetioInstrumented() && assert( !validatorsSuppliedByClient, 'when specifying tandem, use phetioType instead of validators' );

      // Iterate through each validator and make sure that it won't validate options on validating value. This is
      // mainly done for performance
      validators.forEach( validator => {
        assert(
          validator.validateOptionsOnValidateValue !== true,
          'Action sets its own validateOptionsOnValidateValue for each argument type'
        );
        validator.validateOptionsOnValidateValue = false;

        // Changing the validator options after construction indicates a logic error, except that many EmitterIOs
        // are shared between instances. Don't assume we "own" the validator if it came from the TypeIO.
        !phetioTypeSuppliedByClient && Object.freeze( validator );

        // validate the options passed in to validate each Action argument
        ValidatorDef.validateValidator( validator );
      } );

      // Changing after construction indicates a logic error, except that many EmitterIOs are shared between instances.
      // Don't assume we "own" the validator if it came from the TypeIO.
      !phetioTypeSuppliedByClient && Object.freeze( validators );
    }

    /**
     * Gets the data that will be emitted to the PhET-iO data stream, for an instrumented simulation.
     * @returns {*}
     * @private
     */
    getPhetioData() {

      // null if there are no arguments.  dataStream.js omits null values for data
      let data = null;
      if ( this.phetioType.elements.length > 0 ) {

        // Enumerate named argsObject for the data stream.
        data = {};
        for ( let i = 0; i < this.phetioType.elements.length; i++ ) {
          const element = this.phetioType.elements[ i ];
          data[ element.name ] = element.type.toStateObject( arguments[ i ] );
        }
      }
      return data;
    }

    /**
     * Emits a single event.  This method is called many times in a simulation and must be well-optimized.  Listeners
     * are notified in the order they were added via addListener, though it is poor practice to rely on the order
     * of listener notifications.
     * @params - expected parameters are based on options.validators, see constructor
     * @public
     */
    execute() {
      if ( assert && this.validationEnabled ) {
        assert( arguments.length === this.validators.length,
          `Emitted unexpected number of args. Expected: ${this.validators.length} and received ${arguments.length}`
        );
        for ( let i = 0; i < this.validators.length; i++ ) {
          validate( arguments[ i ], this.validators[ i ] );
        }
      }

      // handle phet-io data stream for the emitted event
      this.isPhetioInstrumented() && this.phetioStartEvent( 'emitted', this.getPhetioData.apply( this, arguments ) );

      this._action.apply( null, arguments );

      this.isPhetioInstrumented() && this.phetioEndEvent();
    }
  }

  return axon.register( 'Action', Action );
} );<commit_msg>Minor comment changes<commit_after>// Copyright 2019, University of Colorado Boulder

/**
 * An action that can be executed and sent to the PhET-iO data stream, and optionally recorded for playback. This type
 * will also validate the argument types passed to the action function.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const ActionIO = require( 'AXON/ActionIO' );
  const axon = require( 'AXON/axon' );
  const PhetioObject = require( 'TANDEM/PhetioObject' );
  const Tandem = require( 'TANDEM/Tandem' );
  const ValidatorDef = require( 'AXON/ValidatorDef' );
  const validate = require( 'AXON/validate' );

  // constants
  const ActionIOWithNoArgs = ActionIO( [] );

  // Simulations have thousands of Emitters, so we re-use objects where possible.
  const EMPTY_ARRAY = [];
  assert && Object.freeze( EMPTY_ARRAY );

  class Action extends PhetioObject {

    /**
     * @param {function} action - the function that is called when this Action occurs
     * @param {Object} [options]
     */
    constructor( action, options ) {

      // It is important to know if the following options were provided by the client
      const phetioTypeSupplied = options && options.hasOwnProperty( 'phetioType' );
      const validatorsSupplied = options && options.hasOwnProperty( 'validators' );

      // Important to be before super call. OK to supply either or one or the other, but not both. This is a NAND operator.
      assert && assert( !( phetioTypeSupplied && validatorsSupplied ),
        'use either phetioType or validators, not both, see EmitterIO to set validators on an instrumented Action'
      );

      // ActionIO that have 0 args should use the built-in ActionIO([]) default.  But we must support EmitterIO([]),
      // so we guard based on the type name.
      if ( assert && phetioTypeSupplied && options.phetioType.typeName.indexOf( 'ActionIO' ) === 0 ) {
        assert( options.phetioType.parameterTypes.length > 0, 'do not specify phetioType that is the same as the default' );
      }

      options = _.extend( {

        // {ValidatorDef[]}
        validators: EMPTY_ARRAY,

        // {boolean} @deprecated, only to support legacy emit1, emit2, emit3 calls.
        validationEnabled: true,

        // phet-io - see PhetioObject.js for doc
        tandem: Tandem.optional,
        phetioState: false,
        phetioType: ActionIOWithNoArgs, // subtypes can override with ActionIO([...]), see ActionIO.js
        phetioPlayback: PhetioObject.DEFAULT_OPTIONS.phetioPlayback,
        phetioEventMetadata: PhetioObject.DEFAULT_OPTIONS.phetioEventMetadata
      }, options );

      // Use the phetioType's validators if provided, we know we aren't overwriting here because of the above assertion
      if ( phetioTypeSupplied ) {
        options.validators = options.phetioType.validators;
      }

      // phetioPlayback events need to know the order the arguments occur in order to call EmitterIO.emit()
      // Indicate whether the event is for playback, but leave this "sparse"--only indicate when this happens to be true
      if ( options.phetioPlayback ) {
        options.phetioEventMetadata = options.phetioEventMetadata || {};

        assert && assert( !options.phetioEventMetadata.hasOwnProperty( 'dataKeys' ),
          'dataKeys should be supplied by Action, not elsewhere' );

        options.phetioEventMetadata.dataKeys = options.phetioType.elements.map( element => element.name );
      }

      super( options );

      assert && this.validateValidators( options.validators, validatorsSupplied, phetioTypeSupplied );

      // @public (only for testing) - Note: one test indicates stripping this out via assert && in builds may save around 300kb heap
      this.validators = options.validators;

      // @private - opt out of validation. Can be removed when deprecated emit functions are gone.
      this.validationEnabled = options.validationEnabled;

      assert && assert( typeof action === 'function', 'action should be a function' );

      // @private {function}
      this._action = action;
    }

    /**
     * @param {ValidatorDef[]} validators
     * @param {boolean} validatorsSuppliedByClient - true if the validators option was passed into the constructor via options,
     *                                               false if using the default from the options extend call
     * @param phetioTypeSuppliedByClient - true if the phetioType option was passed into the constructor via options,
     *                                     false if using the default from the options extend call
     * @private
     */
    validateValidators( validators, validatorsSuppliedByClient, phetioTypeSuppliedByClient ) {

      // validate the validators object
      validate( validators, { valueType: Array } );

      this.isPhetioInstrumented() && assert( !validatorsSuppliedByClient, 'when specifying tandem, use phetioType instead of validators' );

      // Iterate through each validator and make sure that it won't validate options on validating value. This is
      // mainly done for performance
      validators.forEach( validator => {
        assert(
          validator.validateOptionsOnValidateValue !== true,
          'Action sets its own validateOptionsOnValidateValue for each argument type'
        );
        validator.validateOptionsOnValidateValue = false;

        // Changing the validator options after construction indicates a logic error, except that many EmitterIOs
        // are shared between instances. Don't assume we "own" the validator if it came from the TypeIO.
        !phetioTypeSuppliedByClient && Object.freeze( validator );

        // validate the options passed in to validate each Action argument
        ValidatorDef.validateValidator( validator );
      } );

      // Changing after construction indicates a logic error, except that many EmitterIOs are shared between instances.
      // Don't assume we "own" the validator if it came from the TypeIO.
      !phetioTypeSuppliedByClient && Object.freeze( validators );
    }

    /**
     * Gets the data that will be emitted to the PhET-iO data stream, for an instrumented simulation.
     * @returns {*}
     * @private
     */
    getPhetioData() {

      // null if there are no arguments.  dataStream.js omits null values for data
      let data = null;
      if ( this.phetioType.elements.length > 0 ) {

        // Enumerate named argsObject for the data stream.
        data = {};
        for ( let i = 0; i < this.phetioType.elements.length; i++ ) {
          const element = this.phetioType.elements[ i ];
          data[ element.name ] = element.type.toStateObject( arguments[ i ] );
        }
      }
      return data;
    }

    /**
     * Emits a single event.  This method is called many times in a simulation and must be well-optimized.  Listeners
     * are notified in the order they were added via addListener, though it is poor practice to rely on the order
     * of listener notifications.
     * @params - expected parameters are based on options.validators, see constructor
     * @public
     */
    execute() {
      if ( assert && this.validationEnabled ) {
        assert( arguments.length === this.validators.length,
          `Emitted unexpected number of args. Expected: ${this.validators.length} and received ${arguments.length}`
        );
        for ( let i = 0; i < this.validators.length; i++ ) {
          validate( arguments[ i ], this.validators[ i ] );
        }
      }

      // handle phet-io data stream for the emitted event
      this.isPhetioInstrumented() && this.phetioStartEvent( 'emitted', this.getPhetioData.apply( this, arguments ) );

      this._action.apply( null, arguments );

      this.isPhetioInstrumented() && this.phetioEndEvent();
    }
  }

  return axon.register( 'Action', Action );
} );<|endoftext|>


<commit_before>"use strict";

var Config = {
  supportsHistory : !!(window.history && history.pushState),
  inMaintenanceMode : false,

  treeName: 'mozilla-central',
  hgBaseURL: "https://hg.mozilla.org/",
  hgURL: "https://hg.mozilla.org/mozilla-central/",
  hgRevURL: "https://hg.mozilla.org/mozilla-central/rev/",
  hgPushlogURL: "https://hg.mozilla.org/mozilla-central/pushloghtml?changeset=",
  showBugURL: "https://bugzilla.mozilla.org/show_bug.cgi?id=",
  productionURL: "https://tbpl.mozilla.org/mcmerge/",

  // Here be dragons
  versionRE: /^mozilla\d+$/i,
  csetInputRE: /^(tip|[\da-f]{12,40})$/i,
  csetIDRE: /\b([\da-f]{12,40})\b/ig,
  leaveOpenRE: /(?:leave|keep)(?:-|\s+)open/i,
  bugNumRE: /\b(\d{4,7})\b/g,
  strictBugNumRE: /^(\d{4,7})$/,
  mergeRE: /\bmerg(ed?|ing)\b/i,
  backoutRE: /back(ing|ed)?\s*out/i,
  revertRE: /revert(ing)?/i,
  partialRevertRE: /(?:from|in)(?:\s+(?:rev(?:ision)?|c(?:hange)?set))?\s+([\da-f]{12,40})/i,
  partialTestRE: /test\s+for/i,
  revertRangeRE: /revert(?:ing)?\s+(?:\S+\s+)?to(?:\s+(?:rev(?:ision)?|c(?:hange)set))?\s+([\da-f]{12,40})/i,
  csetRangeRE: /\b([\da-f]{12,40})\s*(?:to|:|-|through)\s*([\da-f]{12,40})/i,
  hgRevRE: /https?:\/\/hg.mozilla.org\/mozilla-central\/rev\//ig,
  hgRevFullRE: /https?:\/\/hg.mozilla.org\/mozilla-central\/rev\/[\da-f]{12}/ig,
  hgPushlogRE: /https?:\/\/hg.mozilla.org\/mozilla-central\/pushloghtml\?changeset=/ig,
  emailRE: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i,   // I'm not looking for RFC 822 compliance here!

  // The many ways bug numbers are specified
  bugRE1: /b(?:ug)?=(\d{4,7})\b/i,                       // e.g. b=XXXXXX
  bugRE2: /^fix(?:es)?\s*(?:for\s*)?(\d{4,7})\b/i,                         // Fix is sometimes used as a synonym for bug
  bugRE3: /to\s+fix\s+bug\s+(\d{4,7})/i,
  bugRE4: /\((\d{4,7}), r=/i,                          // e.g. JS-team style (XXXXXX, r=foo)
  bugRE5: /but\s*(\d{4,7})/i,                          // The typo but XXXXXX happens quite often
  bugRE6: /\bb(?:u?g(?:zilla)?)?:?\s*#?(\d{4,7})\b/i,  // The catchall
  bugRE7: /^(\d{4,7})\b/i,                              // e.g. XXXXXX,

  repoMergeRE: "\\s+(&|and|with|<->|<>|-?->|(in|on)?to)\\s+",

  mcSynonyms: ["mozilla-central", "central", "m-c", "mc", "mozilla central"],

  rewriteTrees: {
    "thunderbird-trunk": "comm-central",
    "thunderbird-aurora": "comm-aurora",
    "thunderbird-beta": "comm-beta",
    "thunderbird-release": "comm-release",
    "thunderbird-esr10": "comm-esr10",
  },

  treeInfo: {
    "mozilla-central": {
      repo: "mozilla-central",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["mozilla-central", "central", "m-c", "mc", "mozilla central"]
    },
    "mozilla-inbound": {
      repo: "integration/mozilla-inbound",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["mozilla-inbound", "inbound", "m-i", "mi"]
    },
    "mozilla-aurora": {
      repo: "releases/mozilla-aurora",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["releases/mozilla-aurora", "mozilla-aurora"]
    },
    "mozilla-beta": {
      repo: "releases/mozilla-beta",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["releases/mozilla-beta", "mozilla-beta"]
    },
    "mozilla-release": {
      repo: "releases/mozilla-release",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["releases/mozilla-release", "mozilla-release"]
    },
    "mozilla-esr10": {
      repo: "releases/mozilla-esr10",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["esr10", "releases/mozilla-esr10", "mozilla-esr10"]
    },
    "mozilla-esr17": {
      repo: "releases/mozilla-esr17",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["esr17", "releases/mozilla-esr17", "mozilla-esr17"]
    },
    "jetpack": {
      repo: "projects/addon-sdk",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["projects/addon-sdk", "addon-sdk"]
    },
    "build-system": {
      repo: "projects/build-system",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["build-system", "b-s", "bs"]
    },
    "fx-team": {
      repo: "integration/fx-team",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["fx-team"],
      additions: "[fixed-in-fx-team]"
    },
    "graphics": {
      repo: "projects/graphics",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/graphics", "graphics"]
    },
    "ionmonkey": {
      repo: "projects/ionmonkey",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/ionmonkey", "ionmonkey", "im"]
    },
    "jaegermonkey": {
      repo: "projects/jaegermonkey",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/jaegermonkey", "jm"]
    },
    "profiling": {
      repo: "projects/profiling",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["profiling", "projects/profiling"]
    },
    "services-central": {
      repo: "services/services-central",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["services-central", "s-c", "sc", "services/services-central"],
      additions: "[fixed in services]"
    },
    "ux": {
      unconditionalFlag: false,
      repo: "projects/ux",
      synonyms: ["ux", "projects/ux"]
    },
    "alder": {
      repo: "projects/alder",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["alder", "projects/alder"]
    },
    "ash": {
      repo: "projects/ash",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["ash", "projects/ash"]
    },
    "birch": {
      repo: "projects/birch",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/birch", "birch"]
    },
    "cedar": {
      repo: "projects/cedar",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/cedar", "cedar"]
    },
    "cypress": {
      repo: "projects/cypress",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/cypress", "cypress"]
    },
    "date": {
      repo: "projects/date",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/date", "date"]
    },
    "elm": {
      repo: "projects/elm",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/elm", "elm"]
    },
    "fig": {
      repo: "projects/fig",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/fig", "fig"],
      additions: "[fixed-in-fig]"
    },
    "gum": {
      repo: "projects/gum",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/gum", "gum"]
    },
    "holly": {
      repo: "projects/holly",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/holly", "holly"]
    },
    "jamun": {
      repo: "projects/jamun",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/jamun", "jamun"]
    },
    "larch": {
      repo: "projects/larch",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/larch", "larch"]
    },
    "maple": {
      repo: "projects/maple",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/maple", "maple"]
    },
    "oak": {
      repo: "projects/oak",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/oak", "oak"]
    },
    "pine": {
      repo: "projects/pine",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/pine", "pine"]
    },
    "accessibility": {
      repo: "projects/accessibility",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/accessibility", "accessibility"]
    },
    "devtools": {
      repo: "projects/devtools",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/devtools", "devtools"]
    },
    "electrolysis": {
      repo: "projects/electrolysis",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/electrolysis", "electrolysis", "e10s"]
    },
    "places": {
      repo: "projects/places",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/places", "places"]
    },
    "comm-central": {
      repo: "comm-central",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["comm-central", "c-c"]
    },
    "comm-aurora": {
      repo: "releases/comm-aurora",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-aurora", "comm-aurora"]
    },
    "comm-beta": {
      repo: "releases/comm-beta",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-beta", "comm-beta"]
    },
    "comm-release": {
      repo: "releases/comm-release",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-release", "comm-release"]
    },
    "comm-esr10": {
      repo: "releases/comm-esr10",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-esr10", "comm-esr10"]
    },
    "comm-esr17": {
      repo: "releases/comm-esr17",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-esr17", "comm-esr17"]
    },
    "mozilla-b2g18": {
      repo: "releases/mozilla-b2g18",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g", "b2g18", "mozilla-b2g18"]
    }
  }
};

Config.bugNumberREs = [Config.bugRE1, Config.bugRE2, Config.bugRE3,
                       Config.bugRE4, Config.bugRE5, Config.bugRE6, Config.bugRE7];

(function () {
  var base = Config.hgBaseURL;
  for (var treeName in Config.treeInfo) {
    var repo = Config.treeInfo[treeName].repo;
    Config.treeInfo[treeName]['hgURL'] = base + repo + '/';
    Config.treeInfo[treeName]['hgRevURL'] = base + repo + '/rev/';
    Config.treeInfo[treeName]['hgPushlogURL'] = base + repo + '/pushloghtml?changeset=';
    Config.treeInfo[treeName]['hgRevRE'] = new RegExp('https?:\/\/hg.mozilla.org\/' + repo + '\/rev\/', 'ig');
    Config.treeInfo[treeName]['hgRevFullRE'] = new RegExp('https?:\/\/hg.mozilla.org\/' + repo + '\/rev\/[\\da-f]{12}', 'ig');
    Config.treeInfo[treeName]['hgPushlogRE'] = new RegExp('https?:\/\/hg.mozilla.org\/' + repo + '\/pushloghtml\\?changeset=', 'ig');
  }
})();

// Detect input type="email" support
$(document).ready(function() {
  Config.needsValidation = $('#username').prop('type') != 'email';
});
<commit_msg>Bug 900585 - Remove support for obsolete repositories in mcMerge. r=graememcc<commit_after>"use strict";

var Config = {
  supportsHistory : !!(window.history && history.pushState),
  inMaintenanceMode : false,

  treeName: 'mozilla-central',
  hgBaseURL: "https://hg.mozilla.org/",
  hgURL: "https://hg.mozilla.org/mozilla-central/",
  hgRevURL: "https://hg.mozilla.org/mozilla-central/rev/",
  hgPushlogURL: "https://hg.mozilla.org/mozilla-central/pushloghtml?changeset=",
  showBugURL: "https://bugzilla.mozilla.org/show_bug.cgi?id=",
  productionURL: "https://tbpl.mozilla.org/mcmerge/",

  // Here be dragons
  versionRE: /^mozilla\d+$/i,
  csetInputRE: /^(tip|[\da-f]{12,40})$/i,
  csetIDRE: /\b([\da-f]{12,40})\b/ig,
  leaveOpenRE: /(?:leave|keep)(?:-|\s+)open/i,
  bugNumRE: /\b(\d{4,7})\b/g,
  strictBugNumRE: /^(\d{4,7})$/,
  mergeRE: /\bmerg(ed?|ing)\b/i,
  backoutRE: /back(ing|ed)?\s*out/i,
  revertRE: /revert(ing)?/i,
  partialRevertRE: /(?:from|in)(?:\s+(?:rev(?:ision)?|c(?:hange)?set))?\s+([\da-f]{12,40})/i,
  partialTestRE: /test\s+for/i,
  revertRangeRE: /revert(?:ing)?\s+(?:\S+\s+)?to(?:\s+(?:rev(?:ision)?|c(?:hange)set))?\s+([\da-f]{12,40})/i,
  csetRangeRE: /\b([\da-f]{12,40})\s*(?:to|:|-|through)\s*([\da-f]{12,40})/i,
  hgRevRE: /https?:\/\/hg.mozilla.org\/mozilla-central\/rev\//ig,
  hgRevFullRE: /https?:\/\/hg.mozilla.org\/mozilla-central\/rev\/[\da-f]{12}/ig,
  hgPushlogRE: /https?:\/\/hg.mozilla.org\/mozilla-central\/pushloghtml\?changeset=/ig,
  emailRE: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i,   // I'm not looking for RFC 822 compliance here!

  // The many ways bug numbers are specified
  bugRE1: /b(?:ug)?=(\d{4,7})\b/i,                       // e.g. b=XXXXXX
  bugRE2: /^fix(?:es)?\s*(?:for\s*)?(\d{4,7})\b/i,                         // Fix is sometimes used as a synonym for bug
  bugRE3: /to\s+fix\s+bug\s+(\d{4,7})/i,
  bugRE4: /\((\d{4,7}), r=/i,                          // e.g. JS-team style (XXXXXX, r=foo)
  bugRE5: /but\s*(\d{4,7})/i,                          // The typo but XXXXXX happens quite often
  bugRE6: /\bb(?:u?g(?:zilla)?)?:?\s*#?(\d{4,7})\b/i,  // The catchall
  bugRE7: /^(\d{4,7})\b/i,                              // e.g. XXXXXX,

  repoMergeRE: "\\s+(&|and|with|<->|<>|-?->|(in|on)?to)\\s+",

  mcSynonyms: ["mozilla-central", "central", "m-c", "mc", "mozilla central"],

  rewriteTrees: {
    "thunderbird-trunk": "comm-central",
    "thunderbird-aurora": "comm-aurora",
    "thunderbird-beta": "comm-beta",
    "thunderbird-release": "comm-release",
    "thunderbird-esr10": "comm-esr10",
  },

  treeInfo: {
    "mozilla-central": {
      repo: "mozilla-central",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["mozilla-central", "central", "m-c", "mc", "mozilla central"]
    },
    "mozilla-inbound": {
      repo: "integration/mozilla-inbound",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["mozilla-inbound", "inbound", "m-i", "mi"]
    },
    "mozilla-aurora": {
      repo: "releases/mozilla-aurora",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["releases/mozilla-aurora", "mozilla-aurora"]
    },
    "mozilla-beta": {
      repo: "releases/mozilla-beta",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["releases/mozilla-beta", "mozilla-beta"]
    },
    "mozilla-release": {
      repo: "releases/mozilla-release",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["releases/mozilla-release", "mozilla-release"]
    },
    "mozilla-esr17": {
      repo: "releases/mozilla-esr17",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["esr17", "releases/mozilla-esr17", "mozilla-esr17"]
    },
    "jetpack": {
      repo: "projects/addon-sdk",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["projects/addon-sdk", "addon-sdk"]
    },
    "build-system": {
      repo: "projects/build-system",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["build-system", "b-s", "bs"]
    },
    "fx-team": {
      repo: "integration/fx-team",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["fx-team"],
      additions: "[fixed-in-fx-team]"
    },
    "graphics": {
      repo: "projects/graphics",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/graphics", "graphics"]
    },
    "ionmonkey": {
      repo: "projects/ionmonkey",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/ionmonkey", "ionmonkey", "im"]
    },
    "profiling": {
      repo: "projects/profiling",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["profiling", "projects/profiling"]
    },
    "services-central": {
      repo: "services/services-central",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["services-central", "s-c", "sc", "services/services-central"],
      additions: "[fixed in services]"
    },
    "ux": {
      unconditionalFlag: false,
      repo: "projects/ux",
      synonyms: ["ux", "projects/ux"]
    },
    "alder": {
      repo: "projects/alder",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["alder", "projects/alder"]
    },
    "ash": {
      repo: "projects/ash",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["ash", "projects/ash"]
    },
    "birch": {
      repo: "projects/birch",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/birch", "birch"]
    },
    "cedar": {
      repo: "projects/cedar",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/cedar", "cedar"]
    },
    "cypress": {
      repo: "projects/cypress",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/cypress", "cypress"]
    },
    "date": {
      repo: "projects/date",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/date", "date"]
    },
    "elm": {
      repo: "projects/elm",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/elm", "elm"]
    },
    "fig": {
      repo: "projects/fig",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/fig", "fig"],
      additions: "[fixed-in-fig]"
    },
    "gum": {
      repo: "projects/gum",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/gum", "gum"]
    },
    "holly": {
      repo: "projects/holly",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/holly", "holly"]
    },
    "jamun": {
      repo: "projects/jamun",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/jamun", "jamun"]
    },
    "larch": {
      repo: "projects/larch",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/larch", "larch"]
    },
    "maple": {
      repo: "projects/maple",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/maple", "maple"]
    },
    "oak": {
      repo: "projects/oak",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/oak", "oak"]
    },
    "pine": {
      repo: "projects/pine",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/pine", "pine"]
    },
    "comm-central": {
      repo: "comm-central",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["comm-central", "c-c"]
    },
    "comm-aurora": {
      repo: "releases/comm-aurora",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-aurora", "comm-aurora"]
    },
    "comm-beta": {
      repo: "releases/comm-beta",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-beta", "comm-beta"]
    },
    "comm-release": {
      repo: "releases/comm-release",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-release", "comm-release"]
    },
    "comm-esr17": {
      repo: "releases/comm-esr17",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-esr17", "comm-esr17"]
    },
    "mozilla-b2g18": {
      repo: "releases/mozilla-b2g18",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g", "b2g18", "mozilla-b2g18"]
    }
  }
};

Config.bugNumberREs = [Config.bugRE1, Config.bugRE2, Config.bugRE3,
                       Config.bugRE4, Config.bugRE5, Config.bugRE6, Config.bugRE7];

(function () {
  var base = Config.hgBaseURL;
  for (var treeName in Config.treeInfo) {
    var repo = Config.treeInfo[treeName].repo;
    Config.treeInfo[treeName]['hgURL'] = base + repo + '/';
    Config.treeInfo[treeName]['hgRevURL'] = base + repo + '/rev/';
    Config.treeInfo[treeName]['hgPushlogURL'] = base + repo + '/pushloghtml?changeset=';
    Config.treeInfo[treeName]['hgRevRE'] = new RegExp('https?:\/\/hg.mozilla.org\/' + repo + '\/rev\/', 'ig');
    Config.treeInfo[treeName]['hgRevFullRE'] = new RegExp('https?:\/\/hg.mozilla.org\/' + repo + '\/rev\/[\\da-f]{12}', 'ig');
    Config.treeInfo[treeName]['hgPushlogRE'] = new RegExp('https?:\/\/hg.mozilla.org\/' + repo + '\/pushloghtml\\?changeset=', 'ig');
  }
})();

// Detect input type="email" support
$(document).ready(function() {
  Config.needsValidation = $('#username').prop('type') != 'email';
});
<|endoftext|>


<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 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--------|                                        |-r-|  X  |---c---|
      |        e        |   Title                                | i |_____|   l   |
      |        f        |________________________________________| g           o   |
      |        t        |   |                                    | h           s   |
      |        M        |   ySpacing                             | t           e   |
      |        a        |___|____________________________________| M           B   |
      |        r        |                                        | a           u   |
      |        g        |   Content                              | r           t   |
      |        i        |                                        | g           t   |
      |        n        |                                        | i           o   |
      |                 |                                        | n           n   |
      |                 |                                        |             R   |
      |                 |                                        |             i   |
      |                 |                                        |             g   |
      |                 |                                        |             h   |
      |                 |                                        |             M   |
      |                 |________________________________________|             a   |
      |                                     |                                  r   |
      |                                     |                                  g   |
      |                                     bottomMargin                       i   |
      |                                     |                                  n   |
      |_____________________________________|______________________________________|
       */

      xSpacing: 10, // {number} how far the title is placed to the left of the close button // TODO: remove
      ySpacing: 10, // {number}
      topMargin: 10, // {number}
      bottomMargin: 10, // {number}
      rightMargin: 10, // {number}
      leftMargin: null, // {number|null} If null, this is computed so that we have the same spacing on the left and
      // right of the content.

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

      // {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.

      // {number} margin to the right of the close button. Don't confuse this with the margin to the right of
      // the content.  That margin is the sum of rightMargin, xSpacing, and the width of the close button.

      // {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.rightMargin + CLOSE_BUTTON_WIDTH + options.xSpacing;
    }

    // @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;

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

    // 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 vertical content and close button
    var contentAndClosebutton = new HBox( {
      children: [ verticalContent, closeButton ],
      spacing: options.xSpacing,
      align: 'top'
    } );

    // add custom margins
    var dialogContent = new AlignBox( contentAndClosebutton, {
      leftMargin: options.leftMargin,
      rightMargin: options.rightMargin,
      topMargin: options.topMargin,
      bottomMargin: options.bottomMargin
    } );

    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.setAriaLabelledByNode( options.title );
    }

    // 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>xSpacing not rightMargin, https://github.com/phetsims/joist/issues/413<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 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 is placed to the left of the close button
      ySpacing: 10, // {number}
      topMargin: 10, // {number}
      bottomMargin: 10, // {number}
      rightMargin: 10, // {number} // TODO: remove
      leftMargin: null, // {number|null} If null, this is computed so that we have the same spacing on the left and
      // right of the content.

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

      // {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.

      // {number} margin to the right of the close button. Don't confuse this with the margin to the right of
      // the content.  That margin is the sum of rightMargin, xSpacing, and the width of the close button.

      // {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.rightMargin + CLOSE_BUTTON_WIDTH + options.xSpacing;
    }

    // @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;

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

    // 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 vertical content and close button
    var contentAndClosebutton = new HBox( {
      children: [ verticalContent, closeButton ],
      spacing: options.xSpacing,
      align: 'top'
    } );

    // add custom margins
    var dialogContent = new AlignBox( contentAndClosebutton, {
      leftMargin: options.leftMargin,
      rightMargin: options.rightMargin,
      topMargin: options.topMargin,
      bottomMargin: options.bottomMargin
    } );

    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.setAriaLabelledByNode( options.title );
    }

    // 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;
} );<|endoftext|>


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

/**
 * General dialog type
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var inherit = require( 'PHET_CORE/inherit' );
  var Shape = require( 'KITE/Shape' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Panel = require( 'SUN/Panel' );
  var RectangularPushButton = require( 'SUN/buttons/RectangularPushButton' );
  var AccessiblePeer = require( 'SCENERY/accessibility/AccessiblePeer' );
  var joist = require( 'JOIST/joist' );

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

    var dialog = this;
    options = _.extend( {

      // Dialog-specific options
      modal: false, // {boolean} modal dialogs prevent interaction with the rest of the sim while open
      title: null, // {Node} title to be displayed at top
      titleAlign: 'center', // horizontal alignment of the title: {string} left, right or center
      titleSpacing: 20, // {number} how far the title is placed above the content
      hasCloseButton: true, // whether to put a close 'X' button is upper-right corner

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

      // 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,
      xMargin: 20,
      yMargin: 20,
      closeButtonMargin: 5, // {number} how far away should the close button be from the panel border
      accessibleContent: {
        createPeer: function( accessibleInstance ) {
          return new DialogAccessiblePeer( accessibleInstance, dialog );
        }
      }
    }, options );

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

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

    var dialogContent = new Node( {
      children: [ content ]
    } );

    if ( options.title ) {

      var titleNode = options.title;

      dialogContent.addChild( titleNode );

      var updateTitlePosition = function() {
        switch( options.titleAlign ) {
          case 'center':
            titleNode.centerX = content.centerX;
            break;
          case 'left':
            titleNode.left = content.left;
            break;
          case 'right':
            titleNode.right = content.right;
            break;
          default:
            throw new Error( 'unknown titleAlign for Dialog: ' + options.titleAlign );
        }
        titleNode.bottom = content.top - options.titleSpacing;
      };

      if ( options.resize ) {
        content.addEventListener( 'bounds', updateTitlePosition );
        titleNode.addEventListener( 'localBounds', updateTitlePosition );
      }
      updateTitlePosition();
    }

    Panel.call( this, dialogContent, options );

    if ( options.hasCloseButton ) {

      var crossSize = 10;
      var crossNode = new Path( new Shape().moveTo( 0, 0 ).lineTo( crossSize, crossSize ).moveTo( 0, crossSize ).lineTo( crossSize, 0 ), {
        stroke: '#fff',
        lineWidth: 3
      } );

      var closeButton = new RectangularPushButton( {
        content: crossNode,
        baseColor: '#d00', // TODO: color dependent on scheme?
        xMargin: 5,
        yMargin: 5,
        listener: function() {
          dialog.hide();
        }
      } );
      this.addChild( closeButton );

      var updateClosePosition = function() {
        closeButton.right = dialogContent.right + options.xMargin - options.closeButtonMargin;
        closeButton.top = dialogContent.top - options.xMargin + options.closeButtonMargin;
      };

      if ( options.resize ) {
        dialogContent.addEventListener( 'bounds', updateClosePosition );
        if ( options.title ) {
          options.title.addEventListener( 'bounds', updateClosePosition );
        }
      }
      updateClosePosition();
    }

    var sim = window.phet.joist.sim;

    function updateLayout() {
      options.layoutStrategy( dialog, sim.bounds, sim.screenBounds, sim.scale );
    }

    sim.on( 'resized', updateLayout );
    updateLayout();
  }

  joist.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;
      }
    },

    // @public
    hide: function() {
      if ( this.isShowing ) {
        window.phet.joist.sim.hidePopup( this, this.isModal );
        this.isShowing = false;
      }
    }
  }, {

    // @public (accessibility)
    DialogAccessiblePeer: function( accessibleInstance, dialog ) {
      return new DialogAccessiblePeer( accessibleInstance, dialog );
    }
  } );

  function DialogAccessiblePeer( accessibleInstance, dialog ) {
    this.initialize( accessibleInstance, dialog );
  }

  inherit( AccessiblePeer, DialogAccessiblePeer, {

    /**
     * Initialize an accessible peer in the parallel DOM for a Dialog.
     *
     * @param {AccessibleInstance} accessibleInstance
     * @param dialog
     * @public (accessibility)
     */
    initialize: function( accessibleInstance, dialog ) {
      var trail = accessibleInstance.trail;
      var uniqueId = trail.getUniqueId();

      /*
       * We will want the parallel DOM element for a dialog to look like:
       *

       */

      // @private - create the dom element and initialize the peer.
      this.domElement = document.createElement( 'div' ); // @private
      this.initializeAccessiblePeer( accessibleInstance, this.domElement );

      // set dom element attributes
      this.domElement.id = 'dialog-' + uniqueId;
      this.domElement.setAttribute( 'role', 'dialog' );
      this.domElement.tabIndex = '0';

    }
  } );

  return Dialog;
} );<commit_msg>Assertion to fail out on non-modal dialogs, see https://github.com/phetsims/joist/issues/293<commit_after>// Copyright 2014-2015, University of Colorado Boulder

/**
 * General dialog type
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var inherit = require( 'PHET_CORE/inherit' );
  var Shape = require( 'KITE/Shape' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Panel = require( 'SUN/Panel' );
  var RectangularPushButton = require( 'SUN/buttons/RectangularPushButton' );
  var AccessiblePeer = require( 'SCENERY/accessibility/AccessiblePeer' );
  var joist = require( 'JOIST/joist' );

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

    var dialog = this;
    options = _.extend( {

      // Dialog-specific options
      modal: false, // {boolean} modal dialogs prevent interaction with the rest of the sim while open
      title: null, // {Node} title to be displayed at top
      titleAlign: 'center', // horizontal alignment of the title: {string} left, right or center
      titleSpacing: 20, // {number} how far the title is placed above the content
      hasCloseButton: true, // whether to put a close 'X' button is upper-right corner

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

      // 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,
      xMargin: 20,
      yMargin: 20,
      closeButtonMargin: 5, // {number} how far away should the close button be from the panel border
      accessibleContent: {
        createPeer: function( accessibleInstance ) {
          return new DialogAccessiblePeer( accessibleInstance, dialog );
        }
      }
    }, options );

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

    // 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;

    var dialogContent = new Node( {
      children: [ content ]
    } );

    if ( options.title ) {

      var titleNode = options.title;

      dialogContent.addChild( titleNode );

      var updateTitlePosition = function() {
        switch( options.titleAlign ) {
          case 'center':
            titleNode.centerX = content.centerX;
            break;
          case 'left':
            titleNode.left = content.left;
            break;
          case 'right':
            titleNode.right = content.right;
            break;
          default:
            throw new Error( 'unknown titleAlign for Dialog: ' + options.titleAlign );
        }
        titleNode.bottom = content.top - options.titleSpacing;
      };

      if ( options.resize ) {
        content.addEventListener( 'bounds', updateTitlePosition );
        titleNode.addEventListener( 'localBounds', updateTitlePosition );
      }
      updateTitlePosition();
    }

    Panel.call( this, dialogContent, options );

    if ( options.hasCloseButton ) {

      var crossSize = 10;
      var crossNode = new Path( new Shape().moveTo( 0, 0 ).lineTo( crossSize, crossSize ).moveTo( 0, crossSize ).lineTo( crossSize, 0 ), {
        stroke: '#fff',
        lineWidth: 3
      } );

      var closeButton = new RectangularPushButton( {
        content: crossNode,
        baseColor: '#d00', // TODO: color dependent on scheme?
        xMargin: 5,
        yMargin: 5,
        listener: function() {
          dialog.hide();
        }
      } );
      this.addChild( closeButton );

      var updateClosePosition = function() {
        closeButton.right = dialogContent.right + options.xMargin - options.closeButtonMargin;
        closeButton.top = dialogContent.top - options.xMargin + options.closeButtonMargin;
      };

      if ( options.resize ) {
        dialogContent.addEventListener( 'bounds', updateClosePosition );
        if ( options.title ) {
          options.title.addEventListener( 'bounds', updateClosePosition );
        }
      }
      updateClosePosition();
    }

    var sim = window.phet.joist.sim;

    function updateLayout() {
      options.layoutStrategy( dialog, sim.bounds, sim.screenBounds, sim.scale );
    }

    sim.on( 'resized', updateLayout );
    updateLayout();
  }

  joist.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;
      }
    },

    // @public
    hide: function() {
      if ( this.isShowing ) {
        window.phet.joist.sim.hidePopup( this, this.isModal );
        this.isShowing = false;
      }
    }
  }, {

    // @public (accessibility)
    DialogAccessiblePeer: function( accessibleInstance, dialog ) {
      return new DialogAccessiblePeer( accessibleInstance, dialog );
    }
  } );

  function DialogAccessiblePeer( accessibleInstance, dialog ) {
    this.initialize( accessibleInstance, dialog );
  }

  inherit( AccessiblePeer, DialogAccessiblePeer, {

    /**
     * Initialize an accessible peer in the parallel DOM for a Dialog.
     *
     * @param {AccessibleInstance} accessibleInstance
     * @param dialog
     * @public (accessibility)
     */
    initialize: function( accessibleInstance, dialog ) {
      var trail = accessibleInstance.trail;
      var uniqueId = trail.getUniqueId();

      /*
       * We will want the parallel DOM element for a dialog to look like:
       *

       */

      // @private - create the dom element and initialize the peer.
      this.domElement = document.createElement( 'div' ); // @private
      this.initializeAccessiblePeer( accessibleInstance, this.domElement );

      // set dom element attributes
      this.domElement.id = 'dialog-' + uniqueId;
      this.domElement.setAttribute( 'role', 'dialog' );
      this.domElement.tabIndex = '0';

    }
  } );

  return Dialog;
} );<|endoftext|>