<commit_before>import { el, list } from 'redom';

import { dispatch } from './dispatch';

const gameModeVerboseNames = {
  'puyo:duel': 'Puyo duel',
  'puyo:endless': 'Puyo endless',
  'panel-league-duel': 'Panel League duel',
  'panel-league-sandbox': 'Panel League sandbox',
};

const creatableGameModes = ['puyo:duel', 'puyo:endless', 'panel-league-duel'];

class GameInfo {
  constructor() {
    this.el = el('tr',
      this.gameModeDisplay = el('td'),
      this.playerCountDisplay = el('td')
    );

    this.el.onclick = (ev) => {
      ev.preventDefault();
      this.el.classList.toggle('selected');
    };

    this.el.ondblclick = (ev) => {
      ev.preventDefault();
      if (!this.id) {
        return;
      }
      if (this.isJoinable) {
        dispatch(this, 'JoinGame', { id: this.id, mode: this.mode });
      } else {
        dispatch(this, 'SpectateGame', { id: this.id, mode: this.mode });
      }
    };
  }

  get isJoinable() {
    return this.playerCount < this.maximumPlayerCount;
  }

  update({ id, mode, playerCount, maximumPlayerCount }) {
    this.id = id;
    this.mode = mode;
    this.playerCount = playerCount;
    this.maximumPlayerCount = maximumPlayerCount;
    const maximumPlayerCountText = (maximumPlayerCount > 1000) ? '∞' : maximumPlayerCount;

    this.gameModeDisplay.innerText = `${gameModeVerboseNames[this.mode]}`;
    this.playerCountDisplay.innerText = `${this.playerCount} / ${maximumPlayerCountText}`;
  }
}

export default class Lobby {
  constructor(socket) {
    this.socket = socket;

    const gameModeOptions = creatableGameModes.map(mode =>
      el('option', { value: mode }, gameModeVerboseNames[mode])
    );

    this.el = el('.lobby',
      el('',
        this.refreshButton = el('button', { type: 'button' }, 'Refresh'),
        this.createGameButton = el('button', { type: 'button' }, 'Create new game'),
        el('.select-container',
          this.gameModeSelect = el('select', gameModeOptions)
        )
      ),
      el('table',
        el('thead',
          el('tr',
            el('th', 'Game mode'),
            el('th', 'Players')
          )
        ),
        this.games = list('tbody', GameInfo, 'id')
      )
    );

    this.refreshButton.onclick = (ev) => {
      ev.preventDefault();
      dispatch(this, 'RequestGameList');
    };

    this.createGameButton.onclick = (ev) => {
      ev.preventDefault();
      dispatch(this, 'CreateNewGame', { mode: this.gameModeSelect.value });
    };
  }

  onmount() {
    dispatch(this, 'RequestGameList');
  }

  onremount() {
    dispatch(this, 'RequestGameList');
  }

  update(games) {
    this.games.update(games);
  }
}
<commit_msg>Disable Panel League Duel creation<commit_after>import { el, list } from 'redom';

import { dispatch } from './dispatch';

const gameModeVerboseNames = {
  'puyo:duel': 'Puyo duel',
  'puyo:endless': 'Puyo endless',
  'panel-league-duel': 'Panel League duel',
  'panel-league-sandbox': 'Panel League sandbox',
};

const creatableGameModes = ['puyo:duel', 'puyo:endless'];

class GameInfo {
  constructor() {
    this.el = el('tr',
      this.gameModeDisplay = el('td'),
      this.playerCountDisplay = el('td')
    );

    this.el.onclick = (ev) => {
      ev.preventDefault();
      this.el.classList.toggle('selected');
    };

    this.el.ondblclick = (ev) => {
      ev.preventDefault();
      if (!this.id) {
        return;
      }
      if (this.isJoinable) {
        dispatch(this, 'JoinGame', { id: this.id, mode: this.mode });
      } else {
        dispatch(this, 'SpectateGame', { id: this.id, mode: this.mode });
      }
    };
  }

  get isJoinable() {
    return this.playerCount < this.maximumPlayerCount;
  }

  update({ id, mode, playerCount, maximumPlayerCount }) {
    this.id = id;
    this.mode = mode;
    this.playerCount = playerCount;
    this.maximumPlayerCount = maximumPlayerCount;
    const maximumPlayerCountText = (maximumPlayerCount > 1000) ? '∞' : maximumPlayerCount;

    this.gameModeDisplay.innerText = `${gameModeVerboseNames[this.mode]}`;
    this.playerCountDisplay.innerText = `${this.playerCount} / ${maximumPlayerCountText}`;
  }
}

export default class Lobby {
  constructor(socket) {
    this.socket = socket;

    const gameModeOptions = creatableGameModes.map(mode =>
      el('option', { value: mode }, gameModeVerboseNames[mode])
    );

    this.el = el('.lobby',
      el('',
        this.refreshButton = el('button', { type: 'button' }, 'Refresh'),
        this.createGameButton = el('button', { type: 'button' }, 'Create new game'),
        el('.select-container',
          this.gameModeSelect = el('select', gameModeOptions)
        )
      ),
      el('table',
        el('thead',
          el('tr',
            el('th', 'Game mode'),
            el('th', 'Players')
          )
        ),
        this.games = list('tbody', GameInfo, 'id')
      )
    );

    this.refreshButton.onclick = (ev) => {
      ev.preventDefault();
      dispatch(this, 'RequestGameList');
    };

    this.createGameButton.onclick = (ev) => {
      ev.preventDefault();
      dispatch(this, 'CreateNewGame', { mode: this.gameModeSelect.value });
    };
  }

  onmount() {
    dispatch(this, 'RequestGameList');
  }

  onremount() {
    dispatch(this, 'RequestGameList');
  }

  update(games) {
    this.games.update(games);
  }
}
<|endoftext|>


<commit_before>// Copyright 2002-2012, University of Colorado

/**
 * Text
 *
 * TODO: newlines
 *
 * Useful specs:
 * http://www.w3.org/TR/css3-text/
 * http://www.w3.org/TR/css3-fonts/
 * http://www.w3.org/TR/SVG/text.html
 *
 * @author Jonathan Olson <olsonsjc@gmail.com>
 */

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

  var assert = require( 'ASSERT/assert' )( 'scenery' );

  var inherit = require( 'PHET_CORE/inherit' );
  var Bounds2 = require( 'DOT/Bounds2' );

  var scenery = require( 'SCENERY/scenery' );

  var Node = require( 'SCENERY/nodes/Node' ); // inherits from Node
  var Renderer = require( 'SCENERY/layers/Renderer' );
  var fillable = require( 'SCENERY/nodes/Fillable' );
  var strokable = require( 'SCENERY/nodes/Strokable' );
  var objectCreate = require( 'SCENERY/util/Util' ).objectCreate; // i.e. Object.create
  require( 'SCENERY/util/Font' );
  require( 'SCENERY/util/Util' ); // for canvasAccurateBounds

  scenery.Text = function Text( text, options ) {
    this._text         = '';                 // filled in with mutator
    this._font         = new scenery.Font(); // default font, usually 10px sans-serif
    this._textAlign    = 'start';            // start, end, left, right, center
    this._textBaseline = 'alphabetic';       // top, hanging, middle, alphabetic, ideographic, bottom
    this._direction    = 'ltr';              // ltr, rtl, inherit -- consider inherit deprecated, due to how we compute text bounds in an off-screen canvas

    // ensure we have a parameter object
    options = options || {};

    // default to black filled text
    if ( options.fill === undefined ) {
      options.fill = '#000000';
    }

    if ( text !== undefined ) {
      // set the text parameter so that setText( text ) is effectively called in the mutator from the super call
      options.text = text;
    }

    this.initializeStrokable();

    Node.call( this, options );
  };
  var Text = scenery.Text;

  inherit( Text, Node, {
    setText: function( text ) {
      if ( text !== this._text ) {
        this._text = text;
        this.invalidateText();
      }
      return this;
    },

    getText: function() {
      return this._text;
    },

    invalidateText: function() {
      // TODO: faster bounds determination? getBBox()?
      // investigate http://mudcu.be/journal/2011/01/html5-typographic-metrics/
      this.invalidateSelf( this.accurateCanvasBounds() );
    },

    paintCanvas: function( wrapper ) {
      var context = wrapper.context;

      // extra parameters we need to set, but should avoid setting if we aren't drawing anything
      if ( this.hasFill() || this.hasStroke() ) {
        wrapper.setFont( this._font.getFont() );
        wrapper.setTextAlign( this._textAlign );
        wrapper.setTextBaseline( this._textBaseline );
        wrapper.setDirection( this._direction );
      }

      if ( this.hasFill() ) {
        this.beforeCanvasFill( wrapper ); // defined in Fillable
        context.fillText( this._text, 0, 0 );
        this.afterCanvasFill( wrapper ); // defined in Fillable
      }
      if ( this.hasStroke() ) {
        this.beforeCanvasStroke( wrapper ); // defined in Strokable
        context.strokeText( this._text, 0, 0 );
        this.afterCanvasStroke( wrapper ); // defined in Strokable
      }
    },

    paintWebGL: function( state ) {
      throw new Error( 'Text.prototype.paintWebGL unimplemented' );
    },

    createSVGFragment: function( svg, defs, group ) {
      return document.createElementNS( 'http://www.w3.org/2000/svg', 'text' );
    },

    updateSVGFragment: function( element ) {
      var isRTL = this._direction === 'rtl';

      // make the text the only child
      while ( element.hasChildNodes() ) {
        element.removeChild( element.lastChild );
      }
      element.appendChild( document.createTextNode( this._text ) );

      element.setAttribute( 'style', this.getSVGFillStyle() + this.getSVGStrokeStyle() );

      switch ( this._textAlign ) {
        case 'start':
        case 'end':
          element.setAttribute( 'text-anchor', this._textAlign ); break;
        case 'left':
          element.setAttribute( 'text-anchor', isRTL ? 'end' : 'start' ); break;
        case 'right':
          element.setAttribute( 'text-anchor', !isRTL ? 'end' : 'start' ); break;
        case 'center':
          element.setAttribute( 'text-anchor', 'middle' ); break;
      }
      switch ( this._textBaseline ) {
        case 'alphabetic':
        case 'ideographic':
        case 'hanging':
        case 'middle':
          element.setAttribute( 'dominant-baseline', this._textBaseline ); break;
        default:
          throw new Error( 'impossible to get the SVG approximate bounds for textBaseline: ' + this._textBaseline );
      }
      element.setAttribute( 'direction', this._direction );

      // set all of the font attributes, since we can't use the combined one
      element.setAttribute( 'font-family', this._font.getFamily() );
      element.setAttribute( 'font-size', this._font.getSize() );
      element.setAttribute( 'font-style', this._font.getStyle() );
      element.setAttribute( 'font-weight', this._font.getWeight() );
      if ( this._font.getStretch() ) {
        element.setAttribute( 'font-stretch', this._font.getStretch() );
      }
    },

    // support patterns, gradients, and anything else we need to put in the <defs> block
    updateSVGDefs: function( svg, defs ) {
      // remove old definitions if they exist
      this.removeSVGDefs( svg, defs );

      // add new ones if applicable
      this.addSVGFillDef( svg, defs );
      this.addSVGStrokeDef( svg, defs );
    },

    // cleans up references created with udpateSVGDefs()
    removeSVGDefs: function( svg, defs ) {
      this.removeSVGFillDef( svg, defs );
      this.removeSVGStrokeDef( svg, defs );
    },

    /*---------------------------------------------------------------------------*
    * Bounds
    *----------------------------------------------------------------------------*/

    accurateCanvasBounds: function() {
      var node = this;
      var svgBounds = this.approximateSVGBounds();
      return scenery.Util.canvasAccurateBounds( function( context ) {
        context.font = node.font;
        context.textAlign = node.textAlign;
        context.textBaseline = node.textBaseline;
        context.direction = node.direction;
        context.fillText( node.text, 0, 0 );
      }, {
        precision: 0.5,
        resolution: 128,
        initialScale: 32 / Math.max( Math.abs( svgBounds.minX ), Math.abs( svgBounds.minY ), Math.abs( svgBounds.maxX ), Math.abs( svgBounds.maxY ) )
      } );
    },

    approximateCanvasWidth: function() {
      // TODO: consider caching a scratch 1x1 canvas for this purpose
      var context = document.createElement( 'canvas' ).getContext( '2d' );
      context.font = this.font;
      context.textAlign = this.textAlign;
      context.textBaseline = this.textBaseline;
      context.direction = this.direction;
      return context.measureText( this.text ).width;
    },

    approximateSVGBounds: function() {
      var isRTL = this._direction === 'rtl';

      var svg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' );
      svg.setAttribute( 'width', '1024' );
      svg.setAttribute( 'height', '1024' );
      svg.setAttribute( 'style', 'display: hidden;' ); // so we don't flash it in a visible way to the user

      var textElement = document.createElementNS( 'http://www.w3.org/2000/svg', 'text' );
      this.updateSVGFragment( textElement );

      svg.appendChild( textElement );

      document.body.appendChild( svg );
      var rect = textElement.getBBox();
      var result = new Bounds2( rect.x, rect.y, rect.x + rect.width, rect.y + rect.height );
      document.body.removeChild( svg );

      return result;
    },

    approximateDOMBounds: function() {
      // TODO: we can also technically support 'top' using vertical-align: top and line-height: 0 with the image, but it won't usually render otherwise
      assert && assert( this._textBaseline === 'alphabetic' );

      var maxHeight = 1024; // technically this will fail if the font is taller than this!
      var isRTL = this.direction === 'rtl';

      //
QuipTaQiy

      var div = document.createElement( 'div' );
      $( div ).css( {
        position: 'absolute',
        left: 0,
        top: 0,
        padding: '0 !important',
        margin: '0 !important',
        display: 'hidden'
      } );

      var span = document.createElement( 'span' );
      $( span ).css( 'font', this.getFont() );
      span.appendChild( document.createTextNode( this.text ) );
      span.setAttribute( 'direction', this._direction );

      var fakeImage = document.createElement( 'div' );
      $( fakeImage ).css( {
        'vertical-align': 'baseline',
        display: 'inline-block',
        width: 0,
        height: maxHeight + 'px',
        margin: '0 !important',
        padding: '0 !important'
      } );

      div.appendChild( span );
      div.appendChild( fakeImage );

      document.body.appendChild( div );
      var rect = span.getBoundingClientRect();
      var divRect = div.getBoundingClientRect();
      var result = new Bounds2( rect.left, rect.top - maxHeight, rect.right, rect.bottom - maxHeight ).shifted( -divRect.left, -divRect.top );
      document.body.removeChild( div );

      var width = rect.right - rect.left;
      switch ( this._textAlign ) {
        case 'start':
          result = result.shiftedX( isRTL ? -width : 0 );
          break;
        case 'end':
          result = result.shiftedX( !isRTL ? -width : 0 );
          break;
        case 'left':
          break;
        case 'right':
          result = result.shiftedX( -width );
          break;
        case 'center':
          result = result.shiftedX( -width / 2 );
          break;
      }

      return result;
    },

    /*---------------------------------------------------------------------------*
    * Self setters / getters
    *----------------------------------------------------------------------------*/

    setFont: function( font ) {
      // if font is a Font instance, we actually create another copy so that modification on the original will not change this font.
      // in the future we can consider adding listeners to the font to get font change notifications.
      this._font = font instanceof scenery.Font ? new scenery.Font( font.getFont() ) : new scenery.Font( font );
      this.invalidateText();
      return this;
    },

    // NOTE: returns mutable copy for now, consider either immutable version, defensive copy, or note about invalidateText()
    getFont: function() {
      return this._font.getFont();
    },

    setTextAlign: function( textAlign ) {
      this._textAlign = textAlign;
      this.invalidateText();
      return this;
    },

    getTextAlign: function() {
      return this._textAlign;
    },

    setTextBaseline: function( textBaseline ) {
      this._textBaseline = textBaseline;
      this.invalidateText();
      return this;
    },

    getTextBaseline: function() {
      return this._textBaseline;
    },

    setDirection: function( direction ) {
      this._direction = direction;
      this.invalidateText();
      return this;
    },

    getDirection: function() {
      return this._direction;
    },

    isPainted: function() {
      return true;
    },

    getBasicConstructor: function( propLines ) {
      return 'new scenery.Text( \'' + this._text.replace( /'/g, '\\\'' ) + '\', {' + propLines + '} )';
    },

    getPropString: function( spaces ) {
      var result = Node.prototype.getPropString.call( this, spaces );
      result = this.appendFillablePropString( spaces, result );
      result = this.appendStrokablePropString( spaces, result );

      // TODO: if created again, deduplicate with Node's getPropString
      function addProp( key, value, nowrap ) {
        if ( result ) {
          result += ',\n';
        }
        if ( !nowrap && typeof value === 'string' ) {
          result += spaces + key + ': \'' + value + '\'';
        } else {
          result += spaces + key + ': ' + value;
        }
      }

      if ( this.font !== new scenery.Font().getFont() ) {
        addProp( 'font', this.font );
      }

      if ( this._textAlign !== 'start' ) {
        addProp( 'textAlign', this._textAlign );
      }

      if ( this._textBaseline !== 'alphabetic' ) {
        addProp( 'textBaseline', this._textBaseline );
      }

      if ( this._direction !== 'ltr' ) {
        addProp( 'direction', this._direction );
      }

      return result;
    }
  } );

  /*---------------------------------------------------------------------------*
  * Font setters / getters
  *----------------------------------------------------------------------------*/

  function addFontForwarding( propertyName, fullCapitalized, shortUncapitalized ) {
    var getterName = 'get' + fullCapitalized;
    var setterName = 'set' + fullCapitalized;

    Text.prototype[getterName] = function() {
      // use the ES5 getter to retrieve the property. probably somewhat slow.
      return this._font[ shortUncapitalized ];
    };

    Text.prototype[setterName] = function( value ) {
      // use the ES5 setter. probably somewhat slow.
      this._font[ shortUncapitalized ] = value;
      this.invalidateText();
      return this;
    };

    Object.defineProperty( Text.prototype, propertyName, { set: Text.prototype[setterName], get: Text.prototype[getterName] } );
  }

  addFontForwarding( 'fontWeight', 'FontWeight', 'weight' );
  addFontForwarding( 'fontFamily', 'FontFamily', 'family' );
  addFontForwarding( 'fontStretch', 'FontStretch', 'stretch' );
  addFontForwarding( 'fontStyle', 'FontStyle', 'style' );
  addFontForwarding( 'fontSize', 'FontSize', 'size' );
  addFontForwarding( 'lineHeight', 'LineHeight', 'lineHeight' );

  Text.prototype._mutatorKeys = [ 'text', 'font', 'fontWeight', 'fontFamily', 'fontStretch', 'fontStyle', 'fontSize', 'lineHeight',
                                  'textAlign', 'textBaseline', 'direction' ].concat( Node.prototype._mutatorKeys );

  Text.prototype._supportedRenderers = [ Renderer.Canvas, Renderer.SVG ];

  // font-specific ES5 setters and getters are defined using addFontForwarding above
  Object.defineProperty( Text.prototype, 'font', { set: Text.prototype.setFont, get: Text.prototype.getFont } );
  Object.defineProperty( Text.prototype, 'text', { set: Text.prototype.setText, get: Text.prototype.getText } );
  Object.defineProperty( Text.prototype, 'textAlign', { set: Text.prototype.setTextAlign, get: Text.prototype.getTextAlign } );
  Object.defineProperty( Text.prototype, 'textBaseline', { set: Text.prototype.setTextBaseline, get: Text.prototype.getTextBaseline } );
  Object.defineProperty( Text.prototype, 'direction', { set: Text.prototype.setDirection, get: Text.prototype.getDirection } );

  // mix in support for fills and strokes
  fillable( Text );
  strokable( Text );

  return Text;
} );

<commit_msg>Escaping fix for serializing Text nodes<commit_after>// Copyright 2002-2012, University of Colorado

/**
 * Text
 *
 * TODO: newlines
 *
 * Useful specs:
 * http://www.w3.org/TR/css3-text/
 * http://www.w3.org/TR/css3-fonts/
 * http://www.w3.org/TR/SVG/text.html
 *
 * @author Jonathan Olson <olsonsjc@gmail.com>
 */

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

  var assert = require( 'ASSERT/assert' )( 'scenery' );

  var inherit = require( 'PHET_CORE/inherit' );
  var Bounds2 = require( 'DOT/Bounds2' );

  var scenery = require( 'SCENERY/scenery' );

  var Node = require( 'SCENERY/nodes/Node' ); // inherits from Node
  var Renderer = require( 'SCENERY/layers/Renderer' );
  var fillable = require( 'SCENERY/nodes/Fillable' );
  var strokable = require( 'SCENERY/nodes/Strokable' );
  var objectCreate = require( 'SCENERY/util/Util' ).objectCreate; // i.e. Object.create
  require( 'SCENERY/util/Font' );
  require( 'SCENERY/util/Util' ); // for canvasAccurateBounds

  scenery.Text = function Text( text, options ) {
    this._text         = '';                 // filled in with mutator
    this._font         = new scenery.Font(); // default font, usually 10px sans-serif
    this._textAlign    = 'start';            // start, end, left, right, center
    this._textBaseline = 'alphabetic';       // top, hanging, middle, alphabetic, ideographic, bottom
    this._direction    = 'ltr';              // ltr, rtl, inherit -- consider inherit deprecated, due to how we compute text bounds in an off-screen canvas

    // ensure we have a parameter object
    options = options || {};

    // default to black filled text
    if ( options.fill === undefined ) {
      options.fill = '#000000';
    }

    if ( text !== undefined ) {
      // set the text parameter so that setText( text ) is effectively called in the mutator from the super call
      options.text = text;
    }

    this.initializeStrokable();

    Node.call( this, options );
  };
  var Text = scenery.Text;

  inherit( Text, Node, {
    setText: function( text ) {
      if ( text !== this._text ) {
        this._text = text;
        this.invalidateText();
      }
      return this;
    },

    getText: function() {
      return this._text;
    },

    invalidateText: function() {
      // TODO: faster bounds determination? getBBox()?
      // investigate http://mudcu.be/journal/2011/01/html5-typographic-metrics/
      this.invalidateSelf( this.accurateCanvasBounds() );
    },

    paintCanvas: function( wrapper ) {
      var context = wrapper.context;

      // extra parameters we need to set, but should avoid setting if we aren't drawing anything
      if ( this.hasFill() || this.hasStroke() ) {
        wrapper.setFont( this._font.getFont() );
        wrapper.setTextAlign( this._textAlign );
        wrapper.setTextBaseline( this._textBaseline );
        wrapper.setDirection( this._direction );
      }

      if ( this.hasFill() ) {
        this.beforeCanvasFill( wrapper ); // defined in Fillable
        context.fillText( this._text, 0, 0 );
        this.afterCanvasFill( wrapper ); // defined in Fillable
      }
      if ( this.hasStroke() ) {
        this.beforeCanvasStroke( wrapper ); // defined in Strokable
        context.strokeText( this._text, 0, 0 );
        this.afterCanvasStroke( wrapper ); // defined in Strokable
      }
    },

    paintWebGL: function( state ) {
      throw new Error( 'Text.prototype.paintWebGL unimplemented' );
    },

    createSVGFragment: function( svg, defs, group ) {
      return document.createElementNS( 'http://www.w3.org/2000/svg', 'text' );
    },

    updateSVGFragment: function( element ) {
      var isRTL = this._direction === 'rtl';

      // make the text the only child
      while ( element.hasChildNodes() ) {
        element.removeChild( element.lastChild );
      }
      element.appendChild( document.createTextNode( this._text ) );

      element.setAttribute( 'style', this.getSVGFillStyle() + this.getSVGStrokeStyle() );

      switch ( this._textAlign ) {
        case 'start':
        case 'end':
          element.setAttribute( 'text-anchor', this._textAlign ); break;
        case 'left':
          element.setAttribute( 'text-anchor', isRTL ? 'end' : 'start' ); break;
        case 'right':
          element.setAttribute( 'text-anchor', !isRTL ? 'end' : 'start' ); break;
        case 'center':
          element.setAttribute( 'text-anchor', 'middle' ); break;
      }
      switch ( this._textBaseline ) {
        case 'alphabetic':
        case 'ideographic':
        case 'hanging':
        case 'middle':
          element.setAttribute( 'dominant-baseline', this._textBaseline ); break;
        default:
          throw new Error( 'impossible to get the SVG approximate bounds for textBaseline: ' + this._textBaseline );
      }
      element.setAttribute( 'direction', this._direction );

      // set all of the font attributes, since we can't use the combined one
      element.setAttribute( 'font-family', this._font.getFamily() );
      element.setAttribute( 'font-size', this._font.getSize() );
      element.setAttribute( 'font-style', this._font.getStyle() );
      element.setAttribute( 'font-weight', this._font.getWeight() );
      if ( this._font.getStretch() ) {
        element.setAttribute( 'font-stretch', this._font.getStretch() );
      }
    },

    // support patterns, gradients, and anything else we need to put in the <defs> block
    updateSVGDefs: function( svg, defs ) {
      // remove old definitions if they exist
      this.removeSVGDefs( svg, defs );

      // add new ones if applicable
      this.addSVGFillDef( svg, defs );
      this.addSVGStrokeDef( svg, defs );
    },

    // cleans up references created with udpateSVGDefs()
    removeSVGDefs: function( svg, defs ) {
      this.removeSVGFillDef( svg, defs );
      this.removeSVGStrokeDef( svg, defs );
    },

    /*---------------------------------------------------------------------------*
    * Bounds
    *----------------------------------------------------------------------------*/

    accurateCanvasBounds: function() {
      var node = this;
      var svgBounds = this.approximateSVGBounds();
      return scenery.Util.canvasAccurateBounds( function( context ) {
        context.font = node.font;
        context.textAlign = node.textAlign;
        context.textBaseline = node.textBaseline;
        context.direction = node.direction;
        context.fillText( node.text, 0, 0 );
      }, {
        precision: 0.5,
        resolution: 128,
        initialScale: 32 / Math.max( Math.abs( svgBounds.minX ), Math.abs( svgBounds.minY ), Math.abs( svgBounds.maxX ), Math.abs( svgBounds.maxY ) )
      } );
    },

    approximateCanvasWidth: function() {
      // TODO: consider caching a scratch 1x1 canvas for this purpose
      var context = document.createElement( 'canvas' ).getContext( '2d' );
      context.font = this.font;
      context.textAlign = this.textAlign;
      context.textBaseline = this.textBaseline;
      context.direction = this.direction;
      return context.measureText( this.text ).width;
    },

    approximateSVGBounds: function() {
      var isRTL = this._direction === 'rtl';

      var svg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' );
      svg.setAttribute( 'width', '1024' );
      svg.setAttribute( 'height', '1024' );
      svg.setAttribute( 'style', 'display: hidden;' ); // so we don't flash it in a visible way to the user

      var textElement = document.createElementNS( 'http://www.w3.org/2000/svg', 'text' );
      this.updateSVGFragment( textElement );

      svg.appendChild( textElement );

      document.body.appendChild( svg );
      var rect = textElement.getBBox();
      var result = new Bounds2( rect.x, rect.y, rect.x + rect.width, rect.y + rect.height );
      document.body.removeChild( svg );

      return result;
    },

    approximateDOMBounds: function() {
      // TODO: we can also technically support 'top' using vertical-align: top and line-height: 0 with the image, but it won't usually render otherwise
      assert && assert( this._textBaseline === 'alphabetic' );

      var maxHeight = 1024; // technically this will fail if the font is taller than this!
      var isRTL = this.direction === 'rtl';

      //
QuipTaQiy

      var div = document.createElement( 'div' );
      $( div ).css( {
        position: 'absolute',
        left: 0,
        top: 0,
        padding: '0 !important',
        margin: '0 !important',
        display: 'hidden'
      } );

      var span = document.createElement( 'span' );
      $( span ).css( 'font', this.getFont() );
      span.appendChild( document.createTextNode( this.text ) );
      span.setAttribute( 'direction', this._direction );

      var fakeImage = document.createElement( 'div' );
      $( fakeImage ).css( {
        'vertical-align': 'baseline',
        display: 'inline-block',
        width: 0,
        height: maxHeight + 'px',
        margin: '0 !important',
        padding: '0 !important'
      } );

      div.appendChild( span );
      div.appendChild( fakeImage );

      document.body.appendChild( div );
      var rect = span.getBoundingClientRect();
      var divRect = div.getBoundingClientRect();
      var result = new Bounds2( rect.left, rect.top - maxHeight, rect.right, rect.bottom - maxHeight ).shifted( -divRect.left, -divRect.top );
      document.body.removeChild( div );

      var width = rect.right - rect.left;
      switch ( this._textAlign ) {
        case 'start':
          result = result.shiftedX( isRTL ? -width : 0 );
          break;
        case 'end':
          result = result.shiftedX( !isRTL ? -width : 0 );
          break;
        case 'left':
          break;
        case 'right':
          result = result.shiftedX( -width );
          break;
        case 'center':
          result = result.shiftedX( -width / 2 );
          break;
      }

      return result;
    },

    /*---------------------------------------------------------------------------*
    * Self setters / getters
    *----------------------------------------------------------------------------*/

    setFont: function( font ) {
      // if font is a Font instance, we actually create another copy so that modification on the original will not change this font.
      // in the future we can consider adding listeners to the font to get font change notifications.
      this._font = font instanceof scenery.Font ? new scenery.Font( font.getFont() ) : new scenery.Font( font );
      this.invalidateText();
      return this;
    },

    // NOTE: returns mutable copy for now, consider either immutable version, defensive copy, or note about invalidateText()
    getFont: function() {
      return this._font.getFont();
    },

    setTextAlign: function( textAlign ) {
      this._textAlign = textAlign;
      this.invalidateText();
      return this;
    },

    getTextAlign: function() {
      return this._textAlign;
    },

    setTextBaseline: function( textBaseline ) {
      this._textBaseline = textBaseline;
      this.invalidateText();
      return this;
    },

    getTextBaseline: function() {
      return this._textBaseline;
    },

    setDirection: function( direction ) {
      this._direction = direction;
      this.invalidateText();
      return this;
    },

    getDirection: function() {
      return this._direction;
    },

    isPainted: function() {
      return true;
    },

    getBasicConstructor: function( propLines ) {
      return 'new scenery.Text( \'' + this._text.replace( /'/g, '\\\'' ) + '\', {' + propLines + '} )';
    },

    getPropString: function( spaces ) {
      var result = Node.prototype.getPropString.call( this, spaces );
      result = this.appendFillablePropString( spaces, result );
      result = this.appendStrokablePropString( spaces, result );

      // TODO: if created again, deduplicate with Node's getPropString
      function addProp( key, value, nowrap ) {
        if ( result ) {
          result += ',\n';
        }
        if ( !nowrap && typeof value === 'string' ) {
          result += spaces + key + ': \'' + value + '\'';
        } else {
          result += spaces + key + ': ' + value;
        }
      }

      if ( this.font !== new scenery.Font().getFont() ) {
        addProp( 'font', this.font.replace( /'/g, '\\\'' ) );
      }

      if ( this._textAlign !== 'start' ) {
        addProp( 'textAlign', this._textAlign );
      }

      if ( this._textBaseline !== 'alphabetic' ) {
        addProp( 'textBaseline', this._textBaseline );
      }

      if ( this._direction !== 'ltr' ) {
        addProp( 'direction', this._direction );
      }

      return result;
    }
  } );

  /*---------------------------------------------------------------------------*
  * Font setters / getters
  *----------------------------------------------------------------------------*/

  function addFontForwarding( propertyName, fullCapitalized, shortUncapitalized ) {
    var getterName = 'get' + fullCapitalized;
    var setterName = 'set' + fullCapitalized;

    Text.prototype[getterName] = function() {
      // use the ES5 getter to retrieve the property. probably somewhat slow.
      return this._font[ shortUncapitalized ];
    };

    Text.prototype[setterName] = function( value ) {
      // use the ES5 setter. probably somewhat slow.
      this._font[ shortUncapitalized ] = value;
      this.invalidateText();
      return this;
    };

    Object.defineProperty( Text.prototype, propertyName, { set: Text.prototype[setterName], get: Text.prototype[getterName] } );
  }

  addFontForwarding( 'fontWeight', 'FontWeight', 'weight' );
  addFontForwarding( 'fontFamily', 'FontFamily', 'family' );
  addFontForwarding( 'fontStretch', 'FontStretch', 'stretch' );
  addFontForwarding( 'fontStyle', 'FontStyle', 'style' );
  addFontForwarding( 'fontSize', 'FontSize', 'size' );
  addFontForwarding( 'lineHeight', 'LineHeight', 'lineHeight' );

  Text.prototype._mutatorKeys = [ 'text', 'font', 'fontWeight', 'fontFamily', 'fontStretch', 'fontStyle', 'fontSize', 'lineHeight',
                                  'textAlign', 'textBaseline', 'direction' ].concat( Node.prototype._mutatorKeys );

  Text.prototype._supportedRenderers = [ Renderer.Canvas, Renderer.SVG ];

  // font-specific ES5 setters and getters are defined using addFontForwarding above
  Object.defineProperty( Text.prototype, 'font', { set: Text.prototype.setFont, get: Text.prototype.getFont } );
  Object.defineProperty( Text.prototype, 'text', { set: Text.prototype.setText, get: Text.prototype.getText } );
  Object.defineProperty( Text.prototype, 'textAlign', { set: Text.prototype.setTextAlign, get: Text.prototype.getTextAlign } );
  Object.defineProperty( Text.prototype, 'textBaseline', { set: Text.prototype.setTextBaseline, get: Text.prototype.getTextBaseline } );
  Object.defineProperty( Text.prototype, 'direction', { set: Text.prototype.setDirection, get: Text.prototype.getDirection } );

  // mix in support for fills and strokes
  fillable( Text );
  strokable( Text );

  return Text;
} );

<|endoftext|>


<commit_before>// Copyright 2002-2012, University of Colorado

/**
 * Text
 *
 * TODO: newlines
 *
 * Useful specs:
 * http://www.w3.org/TR/css3-text/
 * http://www.w3.org/TR/css3-fonts/
 * http://www.w3.org/TR/SVG/text.html
 *
 * @author Jonathan Olson <olsonsjc@gmail.com>
 */

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

  var assert = require( 'ASSERT/assert' )( 'scenery' );

  var inherit = require( 'PHET_CORE/inherit' );
  var Bounds2 = require( 'DOT/Bounds2' );

  var scenery = require( 'SCENERY/scenery' );

  var Node = require( 'SCENERY/nodes/Node' ); // inherits from Node
  var Renderer = require( 'SCENERY/layers/Renderer' );
  var fillable = require( 'SCENERY/nodes/Fillable' );
  var objectCreate = require( 'SCENERY/util/Util' ).objectCreate; // i.e. Object.create
  require( 'SCENERY/util/Font' );
  require( 'SCENERY/util/Util' ); // for canvasAccurateBounds

  scenery.Text = function Text( text, options ) {
    this._text         = '';                 // filled in with mutator
    this._font         = new scenery.Font(); // default font, usually 10px sans-serif
    this._textAlign    = 'start';            // start, end, left, right, center
    this._textBaseline = 'alphabetic';       // top, hanging, middle, alphabetic, ideographic, bottom
    this._direction    = 'ltr';              // ltr, rtl, inherit -- consider inherit deprecated, due to how we compute text bounds in an off-screen canvas

    // ensure we have a parameter object
    options = options || {};

    // default to black filled text
    if ( options.fill === undefined ) {
      options.fill = '#000000';
    }

    if ( text !== undefined ) {
      // set the text parameter so that setText( text ) is effectively called in the mutator from the super call
      options.text = text;
    }
    Node.call( this, options );
  };
  var Text = scenery.Text;

  inherit( Text, Node, {
    setText: function( text ) {
      if ( text !== this._text ) {
        this._text = text;
        this.invalidateText();
      }
      return this;
    },

    getText: function() {
      return this._text;
    },

    invalidateText: function() {
      // TODO: faster bounds determination? getBBox()?
      // investigate http://mudcu.be/journal/2011/01/html5-typographic-metrics/
      this.invalidateSelf( this.accurateCanvasBounds() );
    },

    // TODO: add SVG / DOM support
    paintCanvas: function( state ) {
      var layer = state.layer;
      var context = layer.context;
      if ( this.hasFill() ) {
        layer.setFillStyle( this.getFill() );
        layer.setFont( this._font.getFont() );
        layer.setTextAlign( this._textAlign );
        layer.setTextBaseline( this._textBaseline );
        layer.setDirection( this._direction );

        context.fillText( this._text, 0, 0 );
      }
    },

    paintWebGL: function( state ) {
      throw new Error( 'Text.prototype.paintWebGL unimplemented' );
    },

    createSVGFragment: function( svg, defs, group ) {
      return document.createElementNS( 'http://www.w3.org/2000/svg', 'text' );
    },

    updateSVGFragment: function( element ) {
      var isRTL = this._direction === 'rtl';

      // make the text the only child
      while ( element.hasChildNodes() ) {
        element.removeChild( element.lastChild );
      }
      element.appendChild( document.createTextNode( this._text ) );

      element.setAttribute( 'fill', this._fill );

      switch ( this._textAlign ) {
        case 'start':
        case 'end':
          element.setAttribute( 'text-anchor', this._textAlign ); break;
        case 'left':
          element.setAttribute( 'text-anchor', isRTL ? 'end' : 'start' ); break;
        case 'right':
          element.setAttribute( 'text-anchor', !isRTL ? 'end' : 'start' ); break;
        case 'center':
          element.setAttribute( 'text-anchor', 'middle' ); break;
      }
      switch ( this._textBaseline ) {
        case 'alphabetic':
        case 'ideographic':
        case 'hanging':
        case 'middle':
          element.setAttribute( 'dominant-baseline', this._textBaseline ); break;
        default:
          throw new Error( 'impossible to get the SVG approximate bounds for textBaseline: ' + this._textBaseline );
      }
      element.setAttribute( 'direction', this._direction );

      // set all of the font attributes, since we can't use the combined one
      element.setAttribute( 'font-family', this._font.getFamily() );
      element.setAttribute( 'font-size', this._font.getSize() );
      element.setAttribute( 'font-style', this._font.getStyle() );
      element.setAttribute( 'font-weight', this._font.getWeight() );
      if ( this._font.getStretch() ) {
        element.setAttribute( 'font-stretch', this._font.getStretch() );
      }
    },

    /*---------------------------------------------------------------------------*
    * Bounds
    *----------------------------------------------------------------------------*/

    accurateCanvasBounds: function() {
      var node = this;
      var svgBounds = this.approximateSVGBounds();
      return scenery.Util.canvasAccurateBounds( function( context ) {
        context.font = node.font;
        context.textAlign = node.textAlign;
        context.textBaseline = node.textBaseline;
        context.direction = node.direction;
        context.fillText( node.text, 0, 0 );
      }, {
        precision: 0.5,
        resolution: 128,
        initialScale: 32 / Math.max( Math.abs( svgBounds.minX ), Math.abs( svgBounds.minY ), Math.abs( svgBounds.maxX ), Math.abs( svgBounds.maxY ) )
      } );
    },

    approximateCanvasWidth: function() {
      // TODO: consider caching a scratch 1x1 canvas for this purpose
      var context = document.createElement( 'canvas' ).getContext( '2d' );
      context.font = this.font;
      context.textAlign = this.textAlign;
      context.textBaseline = this.textBaseline;
      context.direction = this.direction;
      return context.measureText( this.text ).width;
    },

    approximateSVGBounds: function() {
      var isRTL = this._direction === 'rtl';

      var svg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' );
      svg.setAttribute( 'width', '1024' );
      svg.setAttribute( 'height', '1024' );
      svg.setAttribute( 'style', 'display: hidden;' ); // so we don't flash it in a visible way to the user

      var textElement = document.createElementNS( 'http://www.w3.org/2000/svg', 'text' );
      this.updateSVGFragment( textElement );

      svg.appendChild( textElement );

      document.body.appendChild( svg );
      var rect = textElement.getBBox();
      var result = new Bounds2( rect.x, rect.y, rect.x + rect.width, rect.y + rect.height );
      document.body.removeChild( svg );

      return result;
    },

    approximateDOMBounds: function() {
      // TODO: we can also technically support 'top' using vertical-align: top and line-height: 0 with the image, but it won't usually render otherwise
      assert && assert( this._textBaseline === 'alphabetic' );

      var maxHeight = 1024; // technically this will fail if the font is taller than this!
      var isRTL = this.direction === 'rtl';

      //
QuipTaQiy

      var div = document.createElement( 'div' );
      $( div ).css( {
        position: 'absolute',
        left: 0,
        top: 0,
        padding: '0 !important',
        margin: '0 !important',
        display: 'hidden'
      } );

      var span = document.createElement( 'span' );
      $( span ).css( 'font', this.getFont() );
      span.appendChild( document.createTextNode( this.text ) );
      span.setAttribute( 'direction', this._direction );

      var fakeImage = document.createElement( 'div' );
      $( fakeImage ).css( {
        'vertical-align': 'baseline',
        display: 'inline-block',
        width: 0,
        height: maxHeight + 'px',
        margin: '0 !important',
        padding: '0 !important'
      } );

      div.appendChild( span );
      div.appendChild( fakeImage );

      document.body.appendChild( div );
      var rect = span.getBoundingClientRect();
      var divRect = div.getBoundingClientRect();
      var result = new Bounds2( rect.left, rect.top - maxHeight, rect.right, rect.bottom - maxHeight ).shifted( -divRect.left, -divRect.top );
      document.body.removeChild( div );

      var width = rect.right - rect.left;
      switch ( this._textAlign ) {
        case 'start':
          result = result.shiftedX( isRTL ? -width : 0 );
          break;
        case 'end':
          result = result.shiftedX( !isRTL ? -width : 0 );
          break;
        case 'left':
          break;
        case 'right':
          result = result.shiftedX( -width );
          break;
        case 'center':
          result = result.shiftedX( -width / 2 );
          break;
      }

      return result;
    },

    /*---------------------------------------------------------------------------*
    * Self setters / getters
    *----------------------------------------------------------------------------*/

    setFont: function( font ) {
      this._font = font instanceof scenery.Font ? font : new scenery.Font( font );
      this.invalidateText();
      return this;
    },

    // NOTE: returns mutable copy for now, consider either immutable version, defensive copy, or note about invalidateText()
    getFont: function() {
      return this._font.getFont();
    },

    setTextAlign: function( textAlign ) {
      this._textAlign = textAlign;
      this.invalidateText();
      return this;
    },

    getTextAlign: function() {
      return this._textAlign;
    },

    setTextBaseline: function( textBaseline ) {
      this._textBaseline = textBaseline;
      this.invalidateText();
      return this;
    },

    getTextBaseline: function() {
      return this._textBaseline;
    },

    setDirection: function( direction ) {
      this._direction = direction;
      this.invalidateText();
      return this;
    },

    getDirection: function() {
      return this._direction;
    },

    hasSelf: function() {
      return true;
    }
  } );

  /*---------------------------------------------------------------------------*
  * Font setters / getters
  *----------------------------------------------------------------------------*/

  function addFontForwarding( propertyName, fullCapitalized, shortUncapitalized ) {
    var getterName = 'get' + fullCapitalized;
    var setterName = 'set' + fullCapitalized;

    Text.prototype[getterName] = function() {
      // use the ES5 getter to retrieve the property. probably somewhat slow.
      return this._font[ shortUncapitalized ];
    };

    Text.prototype[setterName] = function( value ) {
      // use the ES5 setter. probably somewhat slow.
      this._font[ shortUncapitalized ] = value;
      this.invalidateText();
      return this;
    };

    Object.defineProperty( Text.prototype, propertyName, { set: Text.prototype[setterName], get: Text.prototype[getterName] } );
  }

  addFontForwarding( 'fontWeight', 'fontWeight', 'weight' );
  addFontForwarding( 'fontFamily', 'fontFamily', 'family' );
  addFontForwarding( 'fontStretch', 'fontStretch', 'stretch' );
  addFontForwarding( 'fontStyle', 'fontStyle', 'style' );
  addFontForwarding( 'fontSize', 'fontSize', 'size' );
  addFontForwarding( 'lineHeight', 'LineHeight', 'lineHeight' );

  Text.prototype._mutatorKeys = [ 'text', 'font', 'fontWeight', 'fontFamily', 'fontStretch', 'fontStyle', 'fontSize', 'lineHeight',
                                  'textAlign', 'textBaseline', 'direction' ].concat( Node.prototype._mutatorKeys );

  Text.prototype._supportedRenderers = [ Renderer.Canvas, Renderer.SVG ];

  // font-specific ES5 setters and getters are defined using addFontForwarding above
  Object.defineProperty( Text.prototype, 'font', { set: Text.prototype.setFont, get: Text.prototype.getFont } );
  Object.defineProperty( Text.prototype, 'text', { set: Text.prototype.setText, get: Text.prototype.getText } );
  Object.defineProperty( Text.prototype, 'textAlign', { set: Text.prototype.setTextAlign, get: Text.prototype.getTextAlign } );
  Object.defineProperty( Text.prototype, 'textBaseline', { set: Text.prototype.setTextBaseline, get: Text.prototype.getTextBaseline } );
  Object.defineProperty( Text.prototype, 'direction', { set: Text.prototype.setDirection, get: Text.prototype.getDirection } );

  // mix in support for fills
  fillable( Text );

  return Text;
} );

<commit_msg>Fixing font getter/setter on Text<commit_after>// Copyright 2002-2012, University of Colorado

/**
 * Text
 *
 * TODO: newlines
 *
 * Useful specs:
 * http://www.w3.org/TR/css3-text/
 * http://www.w3.org/TR/css3-fonts/
 * http://www.w3.org/TR/SVG/text.html
 *
 * @author Jonathan Olson <olsonsjc@gmail.com>
 */

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

  var assert = require( 'ASSERT/assert' )( 'scenery' );

  var inherit = require( 'PHET_CORE/inherit' );
  var Bounds2 = require( 'DOT/Bounds2' );

  var scenery = require( 'SCENERY/scenery' );

  var Node = require( 'SCENERY/nodes/Node' ); // inherits from Node
  var Renderer = require( 'SCENERY/layers/Renderer' );
  var fillable = require( 'SCENERY/nodes/Fillable' );
  var objectCreate = require( 'SCENERY/util/Util' ).objectCreate; // i.e. Object.create
  require( 'SCENERY/util/Font' );
  require( 'SCENERY/util/Util' ); // for canvasAccurateBounds

  scenery.Text = function Text( text, options ) {
    this._text         = '';                 // filled in with mutator
    this._font         = new scenery.Font(); // default font, usually 10px sans-serif
    this._textAlign    = 'start';            // start, end, left, right, center
    this._textBaseline = 'alphabetic';       // top, hanging, middle, alphabetic, ideographic, bottom
    this._direction    = 'ltr';              // ltr, rtl, inherit -- consider inherit deprecated, due to how we compute text bounds in an off-screen canvas

    // ensure we have a parameter object
    options = options || {};

    // default to black filled text
    if ( options.fill === undefined ) {
      options.fill = '#000000';
    }

    if ( text !== undefined ) {
      // set the text parameter so that setText( text ) is effectively called in the mutator from the super call
      options.text = text;
    }
    Node.call( this, options );
  };
  var Text = scenery.Text;

  inherit( Text, Node, {
    setText: function( text ) {
      if ( text !== this._text ) {
        this._text = text;
        this.invalidateText();
      }
      return this;
    },

    getText: function() {
      return this._text;
    },

    invalidateText: function() {
      // TODO: faster bounds determination? getBBox()?
      // investigate http://mudcu.be/journal/2011/01/html5-typographic-metrics/
      this.invalidateSelf( this.accurateCanvasBounds() );
    },

    // TODO: add SVG / DOM support
    paintCanvas: function( state ) {
      var layer = state.layer;
      var context = layer.context;
      if ( this.hasFill() ) {
        layer.setFillStyle( this.getFill() );
        layer.setFont( this._font.getFont() );
        layer.setTextAlign( this._textAlign );
        layer.setTextBaseline( this._textBaseline );
        layer.setDirection( this._direction );

        context.fillText( this._text, 0, 0 );
      }
    },

    paintWebGL: function( state ) {
      throw new Error( 'Text.prototype.paintWebGL unimplemented' );
    },

    createSVGFragment: function( svg, defs, group ) {
      return document.createElementNS( 'http://www.w3.org/2000/svg', 'text' );
    },

    updateSVGFragment: function( element ) {
      var isRTL = this._direction === 'rtl';

      // make the text the only child
      while ( element.hasChildNodes() ) {
        element.removeChild( element.lastChild );
      }
      element.appendChild( document.createTextNode( this._text ) );

      element.setAttribute( 'fill', this._fill );

      switch ( this._textAlign ) {
        case 'start':
        case 'end':
          element.setAttribute( 'text-anchor', this._textAlign ); break;
        case 'left':
          element.setAttribute( 'text-anchor', isRTL ? 'end' : 'start' ); break;
        case 'right':
          element.setAttribute( 'text-anchor', !isRTL ? 'end' : 'start' ); break;
        case 'center':
          element.setAttribute( 'text-anchor', 'middle' ); break;
      }
      switch ( this._textBaseline ) {
        case 'alphabetic':
        case 'ideographic':
        case 'hanging':
        case 'middle':
          element.setAttribute( 'dominant-baseline', this._textBaseline ); break;
        default:
          throw new Error( 'impossible to get the SVG approximate bounds for textBaseline: ' + this._textBaseline );
      }
      element.setAttribute( 'direction', this._direction );

      // set all of the font attributes, since we can't use the combined one
      element.setAttribute( 'font-family', this._font.getFamily() );
      element.setAttribute( 'font-size', this._font.getSize() );
      element.setAttribute( 'font-style', this._font.getStyle() );
      element.setAttribute( 'font-weight', this._font.getWeight() );
      if ( this._font.getStretch() ) {
        element.setAttribute( 'font-stretch', this._font.getStretch() );
      }
    },

    /*---------------------------------------------------------------------------*
    * Bounds
    *----------------------------------------------------------------------------*/

    accurateCanvasBounds: function() {
      var node = this;
      var svgBounds = this.approximateSVGBounds();
      return scenery.Util.canvasAccurateBounds( function( context ) {
        context.font = node.font;
        context.textAlign = node.textAlign;
        context.textBaseline = node.textBaseline;
        context.direction = node.direction;
        context.fillText( node.text, 0, 0 );
      }, {
        precision: 0.5,
        resolution: 128,
        initialScale: 32 / Math.max( Math.abs( svgBounds.minX ), Math.abs( svgBounds.minY ), Math.abs( svgBounds.maxX ), Math.abs( svgBounds.maxY ) )
      } );
    },

    approximateCanvasWidth: function() {
      // TODO: consider caching a scratch 1x1 canvas for this purpose
      var context = document.createElement( 'canvas' ).getContext( '2d' );
      context.font = this.font;
      context.textAlign = this.textAlign;
      context.textBaseline = this.textBaseline;
      context.direction = this.direction;
      return context.measureText( this.text ).width;
    },

    approximateSVGBounds: function() {
      var isRTL = this._direction === 'rtl';

      var svg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' );
      svg.setAttribute( 'width', '1024' );
      svg.setAttribute( 'height', '1024' );
      svg.setAttribute( 'style', 'display: hidden;' ); // so we don't flash it in a visible way to the user

      var textElement = document.createElementNS( 'http://www.w3.org/2000/svg', 'text' );
      this.updateSVGFragment( textElement );

      svg.appendChild( textElement );

      document.body.appendChild( svg );
      var rect = textElement.getBBox();
      var result = new Bounds2( rect.x, rect.y, rect.x + rect.width, rect.y + rect.height );
      document.body.removeChild( svg );

      return result;
    },

    approximateDOMBounds: function() {
      // TODO: we can also technically support 'top' using vertical-align: top and line-height: 0 with the image, but it won't usually render otherwise
      assert && assert( this._textBaseline === 'alphabetic' );

      var maxHeight = 1024; // technically this will fail if the font is taller than this!
      var isRTL = this.direction === 'rtl';

      //
QuipTaQiy

      var div = document.createElement( 'div' );
      $( div ).css( {
        position: 'absolute',
        left: 0,
        top: 0,
        padding: '0 !important',
        margin: '0 !important',
        display: 'hidden'
      } );

      var span = document.createElement( 'span' );
      $( span ).css( 'font', this.getFont() );
      span.appendChild( document.createTextNode( this.text ) );
      span.setAttribute( 'direction', this._direction );

      var fakeImage = document.createElement( 'div' );
      $( fakeImage ).css( {
        'vertical-align': 'baseline',
        display: 'inline-block',
        width: 0,
        height: maxHeight + 'px',
        margin: '0 !important',
        padding: '0 !important'
      } );

      div.appendChild( span );
      div.appendChild( fakeImage );

      document.body.appendChild( div );
      var rect = span.getBoundingClientRect();
      var divRect = div.getBoundingClientRect();
      var result = new Bounds2( rect.left, rect.top - maxHeight, rect.right, rect.bottom - maxHeight ).shifted( -divRect.left, -divRect.top );
      document.body.removeChild( div );

      var width = rect.right - rect.left;
      switch ( this._textAlign ) {
        case 'start':
          result = result.shiftedX( isRTL ? -width : 0 );
          break;
        case 'end':
          result = result.shiftedX( !isRTL ? -width : 0 );
          break;
        case 'left':
          break;
        case 'right':
          result = result.shiftedX( -width );
          break;
        case 'center':
          result = result.shiftedX( -width / 2 );
          break;
      }

      return result;
    },

    /*---------------------------------------------------------------------------*
    * Self setters / getters
    *----------------------------------------------------------------------------*/

    setFont: function( font ) {
      this._font = font instanceof scenery.Font ? font : new scenery.Font( font );
      this.invalidateText();
      return this;
    },

    // NOTE: returns mutable copy for now, consider either immutable version, defensive copy, or note about invalidateText()
    getFont: function() {
      return this._font.getFont();
    },

    setTextAlign: function( textAlign ) {
      this._textAlign = textAlign;
      this.invalidateText();
      return this;
    },

    getTextAlign: function() {
      return this._textAlign;
    },

    setTextBaseline: function( textBaseline ) {
      this._textBaseline = textBaseline;
      this.invalidateText();
      return this;
    },

    getTextBaseline: function() {
      return this._textBaseline;
    },

    setDirection: function( direction ) {
      this._direction = direction;
      this.invalidateText();
      return this;
    },

    getDirection: function() {
      return this._direction;
    },

    hasSelf: function() {
      return true;
    }
  } );

  /*---------------------------------------------------------------------------*
  * Font setters / getters
  *----------------------------------------------------------------------------*/

  function addFontForwarding( propertyName, fullCapitalized, shortUncapitalized ) {
    var getterName = 'get' + fullCapitalized;
    var setterName = 'set' + fullCapitalized;

    Text.prototype[getterName] = function() {
      // use the ES5 getter to retrieve the property. probably somewhat slow.
      return this._font[ shortUncapitalized ];
    };

    Text.prototype[setterName] = function( value ) {
      // use the ES5 setter. probably somewhat slow.
      this._font[ shortUncapitalized ] = value;
      this.invalidateText();
      return this;
    };

    Object.defineProperty( Text.prototype, propertyName, { set: Text.prototype[setterName], get: Text.prototype[getterName] } );
  }

  addFontForwarding( 'fontWeight', 'FontWeight', 'weight' );
  addFontForwarding( 'fontFamily', 'FontFamily', 'family' );
  addFontForwarding( 'fontStretch', 'FontStretch', 'stretch' );
  addFontForwarding( 'fontStyle', 'FontStyle', 'style' );
  addFontForwarding( 'fontSize', 'FontSize', 'size' );
  addFontForwarding( 'lineHeight', 'LineHeight', 'lineHeight' );

  Text.prototype._mutatorKeys = [ 'text', 'font', 'fontWeight', 'fontFamily', 'fontStretch', 'fontStyle', 'fontSize', 'lineHeight',
                                  'textAlign', 'textBaseline', 'direction' ].concat( Node.prototype._mutatorKeys );

  Text.prototype._supportedRenderers = [ Renderer.Canvas, Renderer.SVG ];

  // font-specific ES5 setters and getters are defined using addFontForwarding above
  Object.defineProperty( Text.prototype, 'font', { set: Text.prototype.setFont, get: Text.prototype.getFont } );
  Object.defineProperty( Text.prototype, 'text', { set: Text.prototype.setText, get: Text.prototype.getText } );
  Object.defineProperty( Text.prototype, 'textAlign', { set: Text.prototype.setTextAlign, get: Text.prototype.getTextAlign } );
  Object.defineProperty( Text.prototype, 'textBaseline', { set: Text.prototype.setTextBaseline, get: Text.prototype.getTextBaseline } );
  Object.defineProperty( Text.prototype, 'direction', { set: Text.prototype.setDirection, get: Text.prototype.getDirection } );

  // mix in support for fills
  fillable( Text );

  return Text;
} );

<|endoftext|>


<commit_before>var _ = require('lodash'),
    ucfirst = function(str) {
        return str.charAt(0).toUpperCase() + str.slice(1);
    };

/*jshint multistr: true */
var types = 'array boolean date element empty finite function null \
            number object plainObject regexp string'.match(/(\w+)/g),
    typeConstraints = {
        isWhole: function(value) {
            // a whole nunmber ( int )
            return _.isNumber(value) && value % 1 === 0;
        },
        isReal: function(value) {
            // a real number ( float )
            return _.isNumber(value) && !isNaN(value);
        },
        isNatural: function(value) {
            // e.g. positive int
            return typeConstraints.isWhole(value) && 0 <= value;
        }
    },
    _normalizeValidationSpec = function(validationSpec) {
        var normalized = {};

        _.keys(validationSpec).forEach(function(key) {
            normalized[key] = _.extend({
                    optional: false,
                    isa: null
                }, validationSpec[key]);
        });

        return normalized;
    },
    validationObject = function(invalidKeys, values, normalizedSpec) {
        return {
            isValid: function() {
                return invalidKeys.length === 0;
            },

            errors: function() {
                return invalidKeys;
            },

            get: function(name) {
                if (values) {
                    return values[name];
                }
                return null;
            },

            values: function() {
                if (_.isObject(values)) return _.values(values);
                if (_.isArray(values)) return values;

                return [];
            },

            errorString: function() {
                if (_.isEmpty(normalizedSpec)) {
                    return invalidKeys[0];
                }

                var prefix = _.isObject(values) ? 'named' : 'positional';

                var errors = _.map(invalidKeys, function(key) {
                    if (_.isEmpty(values[key]))
                        return 'missing ' + prefix + ' argument ' + key;

                    return prefix + ' argument ' + key + ' is not a "' + normalizedSpec[key].isa + '"';
                });

                return errors.length ? errors.join(', ') : '';
            }
        };
    };

var Validate = {
    isValidOfType: function(value, typeName) {
        var method = 'is' + ucfirst(typeName);

        if (typeConstraints[method] !== undefined) return typeConstraints[method](value);
        if (types.indexOf(typeName) === -1) return false;

        return _[method](value);
    },

    isValid: function(value, rawSpec) {
        var spec = _.extend({
                optional: false,
                isa: null
            }, rawSpec);

        if (_.isUndefined(value) || _.isNull(value)) {
            return spec.optional;
        }

        if (_.isString(spec.isa))
            return Validate.isValidOfType(value, spec.isa);

        return (value instanceof spec.isa);
    },

    validatePositional: function(posArguments, validationSpec) {
        var errors = [],
            normalized = _normalizeValidationSpec(validationSpec);

        if (_.isEmpty(validationSpec)) {
            return validationObject(['missing validation spec']);
        }

        if ((_.isArray(posArguments) || _.isArguments(posArguments))) {
            var validation = validationSpec.map(function(validation, index) {
                return Validate.isValid(posArguments[index], validation) ? null : index;
            });

            errors = validation.filter(_.isNumber);

            return validationObject(errors, posArguments, normalized);
        }

        return validationObject(['missing positional arguments']);
    },

    validateObject: function(namedArguments, validationSpec) {
        var errors = [],
            normalized = _normalizeValidationSpec(validationSpec);

        if (_.isEmpty(validationSpec)) {
            return validationObject(['missing validation spec']);
        }

        if (_.isPlainObject(namedArguments)) {
            errors = _.keys(validationSpec).filter(function(key) {
                return Validate.isValid(namedArguments[key], validationSpec[key]) ? false : true;
            });

            return validationObject(errors, namedArguments, normalized);
        }

        return validationObject(['missing named arguments']);
    }
};

module.exports = Validate;
<commit_msg>use lodash _.map and _.filter, be friendly with browsers, adds documentation<commit_after>/** Validate arguments: validate your args.
 * @module validate-arguments
 */
var _ = require('lodash'),
    ucfirst = function(str) {
        return str.charAt(0).toUpperCase() + str.slice(1);
    };

/*jshint multistr: true */
var types = 'array boolean date element empty finite function null \
            number object plainObject regexp string'.match(/(\w+)/g),
    typeConstraints = {
        isWhole: function(value) {
            // a whole nunmber ( int )
            return _.isNumber(value) && value % 1 === 0;
        },
        isReal: function(value) {
            // a real number ( float )
            return _.isNumber(value) && !isNaN(value);
        },
        isNatural: function(value) {
            // e.g. positive int
            return typeConstraints.isWhole(value) && 0 <= value;
        }
    },
    _normalizeValidationSpec = function(validationSpec) {
        var normalized = {};

        _.keys(validationSpec).forEach(function(key) {
            normalized[key] = _.extend({
                    optional: false,
                    isa: null
                }, validationSpec[key]);
        });

        return normalized;
    },
    validationObject = function(invalidKeys, values, normalizedSpec) {
        return {
            isValid: function() {
                return invalidKeys.length === 0;
            },

            errors: function() {
                return invalidKeys;
            },

            get: function(name) {
                if (values) {
                    return values[name];
                }
                return null;
            },

            values: function() {
                if (_.isObject(values)) return _.values(values);
                if (_.isArray(values)) return values;

                return [];
            },

            errorString: function() {
                if (_.isEmpty(normalizedSpec)) {
                    return invalidKeys[0];
                }

                var prefix = _.isObject(values) ? 'named' : 'positional';

                var errors = _.map(invalidKeys, function(key) {
                    if (_.isEmpty(values[key]))
                        return 'missing ' + prefix + ' argument ' + key;

                    return prefix + ' argument ' + key + ' is not a "' + normalizedSpec[key].isa + '"';
                });

                return errors.length ? errors.join(', ') : '';
            }
        };
    };

var Validate = {
    /**
     * Perform validation against one of the builtin types: whole, real, natural
     *
     * @param {number} value A number to check
     * @param {string} name Type name: whole, real, natural
     * @return {bool} True or false, false if name is not one of "whole, real, natural"
     */
    isValidOfType: function(value, typeName) {
        var method = 'is' + ucfirst(typeName);

        if (typeConstraints[method] !== undefined) return typeConstraints[method](value);
        if (types.indexOf(typeName) === -1) return false;

        return _[method](value);
    },

    /**
     * Validate a single value against a given validation spec
     *
     * @param {any} value Value to validate
     * @param {object} Validation spec { isa: 'what', optional: bool }
     * @return {bool} true or false
     */
    isValid: function(value, rawSpec) {
        var spec = _.extend({
                optional: false,
                isa: null
            }, rawSpec);

        if (_.isUndefined(value) || _.isNull(value)) {
            return spec.optional;
        }

        if (_.isString(spec.isa))
            return Validate.isValidOfType(value, spec.isa);

        if(  typeof spec.isa == 'function' )
            return (value instanceof spec.isa);

        return false;
    },

    /**
     * Validate positional arguments, either an array or arguments object.
     *
     * @param {array|arguments} posArguments Array or arguments object to validate
     * @param {array} validationSpec Validation spec (array of objects) to validate against
     * @return {validationObject} A validation object telling you what happened
     */
    validatePositional: function(posArguments, validationSpec) {
        var errors = [],
            normalized = _normalizeValidationSpec(validationSpec);

        if (_.isEmpty(validationSpec)) {
            return validationObject(['missing validation spec']);
        }

        if ((_.isArray(posArguments) || _.isArguments(posArguments))) {
            var validation = _.map( validationSpec, function(validation, index) {
                return Validate.isValid(posArguments[index], validation) ? null : index;
            });

            // filter out nulls
            errors = _.filter( validation, _.isNumber);

            return validationObject(errors, posArguments, normalized);
        }

        return validationObject(['missing positional arguments']);
    },

    /**
     * Validate named arguments, a plain object of key => value pairs
     *
     * @param {object} namedArguments Object of key => value pairs to validate
     * @param {object} validationSpec Validation spec ( key => validation ) to validate against
     * @return {validationObject} A validation object telling you what happened
     */
    validateObject: function(namedArguments, validationSpec) {
        var errors = [],
            normalized = _normalizeValidationSpec(validationSpec);

        if (_.isEmpty(validationSpec)) {
            return validationObject(['missing validation spec']);
        }

        if (_.isPlainObject(namedArguments)) {
            errors = _.filter(_.keys(validationSpec), function(key) {
                return Validate.isValid(namedArguments[key], validationSpec[key]) ? false : true;
            });

            return validationObject(errors, namedArguments, normalized);
        }

        return validationObject(['missing named arguments']);
    }
};

module.exports = Validate;
<|endoftext|>


<commit_before>// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

'use strict';

var fs = require('fs'),
  path = require('path'),
  msrest = require('ms-rest'),
  msrestazure = require('ms-rest-azure'),
  ResourceManagementClient = require('azure-arm-resource').ResourceManagementClient,
  log = require('./util/logging'),
  utils = require('./util/utils'),
  path = require('path'),
  SpecValidator = require('./validators/specValidator'),
  WireFormatGenerator = require('./wireFormatGenerator'),
  XMsExampleExtractor = require('./xMsExampleExtractor'),
  SpecResolver = require('./validators/specResolver'),
  UmlGenerator = require('./umlGenerator');

exports = module.exports;

exports.finalValidationResult = { validityStatus: true };

exports.getDocumentsFromCompositeSwagger = function getDocumentsFromCompositeSwagger(compositeSpecPath) {
  let compositeSwagger;
  let finalDocs = [];
  return utils.parseJson(compositeSpecPath).then(function (result) {
    compositeSwagger = result;
    if (!(compositeSwagger.documents && Array.isArray(compositeSwagger.documents) && compositeSwagger.documents.length > 0)) {
      throw new Error(`CompositeSwagger - ${compositeSpecPath} must contain a documents property and it must be of type array and it must be a non empty array.`);
    }
    let docs = compositeSwagger.documents;
    let basePath = path.dirname(compositeSpecPath);
    for (let i = 0; i < docs.length; i++) {
      if (docs[i].startsWith('.')) {
        docs[i] = docs[i].substring(1);
      }
      let individualPath = '';
      if (docs[i].startsWith('http')) {
        individualPath = docs[i];
      } else {
        individualPath = basePath + docs[i];
      }
      finalDocs.push(individualPath);
    }
    return finalDocs;
  }).catch(function (err) {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.validateSpec = function validateSpec(specPath, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  // As a part of resolving discriminators we replace all the parent references
  // with a oneof array containing references to the parent and its children.
  // This breaks the swagger specification 2.0 schema since oneOf is not supported.
  // Hence we disable it since it is not required for semantic check.

  options.shouldResolveDiscriminator = false;
  // parameters in 'x-ms-parameterized-host' extension need not be resolved for semantic
  // validation as that would not match the path parameters defined in the path template
  // and cause the semantic validation to fail.
  options.shouldResolveParameterizedHost = false;

  // We shoudln't be resolving nullable types for semantic validaiton as we'll replace nodes
  // with oneof arrays which are not semantically valid in swagger 2.0 schema.
  options.shouldResolveNullableTypes = false;
  let validator = new SpecValidator(specPath, null, options);
  exports.finalValidationResult[specPath] = validator.specValidationResult;
  return validator.initialize().then(function () {
    log.info(`Semantically validating  ${specPath}:\n`);
    return validator.validateSpec().then(function (result) {
      exports.updateEndResultOfSingleValidation(validator);
      exports.logDetailedInfo(validator);
      return Promise.resolve(validator.specValidationResult);
    });
  }).catch(function (err) {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.validateCompositeSpec = function validateCompositeSpec(compositeSpecPath, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  return exports.getDocumentsFromCompositeSwagger(compositeSpecPath).then(function (docs) {
    options.consoleLogLevel = log.consoleLogLevel;
    options.logFilepath = log.filepath;
    let promiseFactories = docs.map(function (doc) {
      return function () { return exports.validateSpec(doc, options); };
    });
    return utils.executePromisesSequentially(promiseFactories);
  }).catch(function (err) {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.validateExamples = function validateExamples(specPath, operationIds, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  let validator = new SpecValidator(specPath, null, options);
  exports.finalValidationResult[specPath] = validator.specValidationResult;
  return validator.initialize().then(function () {
    log.info(`Validating "examples" and "x-ms-examples" in  ${specPath}:\n`);
    validator.validateOperations(operationIds);
    exports.updateEndResultOfSingleValidation(validator);
    exports.logDetailedInfo(validator);
    return Promise.resolve(validator.specValidationResult);
  }).catch(function (err) {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.validateExamplesInCompositeSpec = function validateExamplesInCompositeSpec(compositeSpecPath, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  return exports.getDocumentsFromCompositeSwagger(compositeSpecPath).then(function (docs) {
    options.consoleLogLevel = log.consoleLogLevel;
    options.logFilepath = log.filepath;
    let promiseFactories = docs.map(function (doc) {
      return function () { return exports.validateExamples(doc, options); };
    });
    return utils.executePromisesSequentially(promiseFactories);
  }).catch(function (err) {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.resolveSpec = function resolveSpec(specPath, outputDir, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  let specFileName = path.basename(specPath);
  let resolver;
  return utils.parseJson(specPath).then((result) => {
    resolver = new SpecResolver(specPath, result, options);
    return resolver.resolve();
  }).then(() => {
    let resolvedSwagger = JSON.stringify(resolver.specInJson, null, 2);
    if (outputDir !== './' && !fs.existsSync(outputDir)) {
      fs.mkdirSync(outputDir);
    }
    let outputFilepath = `${path.join(outputDir, specFileName)}`;
    fs.writeFileSync(`${path.join(outputDir, specFileName)}`, resolvedSwagger, { encoding: 'utf8' });
    console.log(`Saved the resolved spec at "${outputFilepath}".`);
    return Promise.resolve();
  }).catch((err) => {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.resolveCompositeSpec = function resolveCompositeSpec(specPath, outputDir, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  return exports.getDocumentsFromCompositeSwagger(specPath).then(function (docs) {
    options.consoleLogLevel = log.consoleLogLevel;
    options.logFilepath = log.filepath;
    let promiseFactories = docs.map(function (doc) {
      return function () { return exports.resolveSpec(doc, outputDir, options); };
    });
    return utils.executePromisesSequentially(promiseFactories);
  }).catch(function (err) {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.generateWireFormat = function generateWireFormat(specPath, outDir, emitYaml, operationIds, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  let wfGenerator = new WireFormatGenerator(specPath, null, outDir, emitYaml);
  return wfGenerator.initialize().then(function () {
    log.info(`Generating wire format request and responses for swagger spec: "${specPath}":\n`);
    wfGenerator.processOperations(operationIds);
    return Promise.resolve();
  }).catch(function (err) {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.generateWireFormatInCompositeSpec = function generateWireFormatInCompositeSpec(compositeSpecPath, outDir, emitYaml, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  return exports.getDocumentsFromCompositeSwagger(compositeSpecPath).then(function (docs) {
    options.consoleLogLevel = log.consoleLogLevel;
    options.logFilepath = log.filepath;
    let promiseFactories = docs.map(function (doc) {
      return function () { return exports.generateWireFormat(doc, outDir, emitYaml, null, options); };
    });
    return utils.executePromisesSequentially(promiseFactories);
  }).catch(function (err) {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.generateUml = function generateUml(specPath, outputDir, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  let specFileName = path.basename(specPath);
  let resolver;
  let resolverOptions = {};
  resolverOptions.shouldResolveRelativePaths = true;
  resolverOptions.shouldResolveXmsExamples = false;
  resolverOptions.shouldResolveAllOf = false;
  resolverOptions.shouldSetAdditionalPropertiesFalse = false;
  resolverOptions.shouldResolvePureObjects = false;
  resolverOptions.shouldResolveDiscriminator = false;
  resolverOptions.shouldResolveParameterizedHost = false;
  resolverOptions.shouldResolveNullableTypes = false;
  return utils.parseJson(specPath).then((result) => {
    resolver = new SpecResolver(specPath, result, resolverOptions);
    return resolver.resolve();
  }).then(() => {
    let umlGenerator = new UmlGenerator(resolver.specInJson, options);
    return umlGenerator.generateDiagramFromGraph();
  }).then((svgGraph) => {
    if (outputDir !== './' && !fs.existsSync(outputDir)) {
      fs.mkdirSync(outputDir);
    }
    let svgFile = specFileName.replace(path.extname(specFileName), '.svg');
    let outputFilepath = `${path.join(outputDir, svgFile)}`;
    fs.writeFileSync(`${path.join(outputDir, svgFile)}`, svgGraph, { encoding: 'utf8' });
    console.log(`Saved the uml at "${outputFilepath}". Please open the file in a browser.`);
    return Promise.resolve();
  }).catch((err) => {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.updateEndResultOfSingleValidation = function updateEndResultOfSingleValidation(validator) {
  if (validator.specValidationResult.validityStatus) {
    if (!(log.consoleLogLevel === 'json' || log.consoleLogLevel === 'off')) {
      let consoleLevel = log.consoleLogLevel;
      log.consoleLogLevel = 'info';
      log.info('No Errors were found.');
      log.consoleLogLevel = consoleLevel;
    }
  }
  if (!validator.specValidationResult.validityStatus) {
    process.exitCode = 1;
    exports.finalValidationResult.validityStatus = validator.specValidationResult.validityStatus;
  }
  return;
};

exports.logDetailedInfo = function logDetailedInfo(validator) {
  if (log.consoleLogLevel === 'json') {
    console.dir(validator.specValidationResult, { depth: null, colors: true });
  }
  log.silly('############################');
  log.silly(validator.specValidationResult);
  log.silly('----------------------------');
};

exports.extractXMsExamples = function extractXMsExamples(specPath, recordings, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  let xMsExampleExtractor = new XMsExampleExtractor(specPath, recordings, options);
  return xMsExampleExtractor.extract();
};
<commit_msg>Respect desired logLevel<commit_after>// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

'use strict';

var fs = require('fs'),
  path = require('path'),
  msrest = require('ms-rest'),
  msrestazure = require('ms-rest-azure'),
  ResourceManagementClient = require('azure-arm-resource').ResourceManagementClient,
  log = require('./util/logging'),
  utils = require('./util/utils'),
  path = require('path'),
  SpecValidator = require('./validators/specValidator'),
  WireFormatGenerator = require('./wireFormatGenerator'),
  XMsExampleExtractor = require('./xMsExampleExtractor'),
  SpecResolver = require('./validators/specResolver'),
  UmlGenerator = require('./umlGenerator');

exports = module.exports;

exports.finalValidationResult = { validityStatus: true };

exports.getDocumentsFromCompositeSwagger = function getDocumentsFromCompositeSwagger(compositeSpecPath) {
  let compositeSwagger;
  let finalDocs = [];
  return utils.parseJson(compositeSpecPath).then(function (result) {
    compositeSwagger = result;
    if (!(compositeSwagger.documents && Array.isArray(compositeSwagger.documents) && compositeSwagger.documents.length > 0)) {
      throw new Error(`CompositeSwagger - ${compositeSpecPath} must contain a documents property and it must be of type array and it must be a non empty array.`);
    }
    let docs = compositeSwagger.documents;
    let basePath = path.dirname(compositeSpecPath);
    for (let i = 0; i < docs.length; i++) {
      if (docs[i].startsWith('.')) {
        docs[i] = docs[i].substring(1);
      }
      let individualPath = '';
      if (docs[i].startsWith('http')) {
        individualPath = docs[i];
      } else {
        individualPath = basePath + docs[i];
      }
      finalDocs.push(individualPath);
    }
    return finalDocs;
  }).catch(function (err) {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.validateSpec = function validateSpec(specPath, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  // As a part of resolving discriminators we replace all the parent references
  // with a oneof array containing references to the parent and its children.
  // This breaks the swagger specification 2.0 schema since oneOf is not supported.
  // Hence we disable it since it is not required for semantic check.

  options.shouldResolveDiscriminator = false;
  // parameters in 'x-ms-parameterized-host' extension need not be resolved for semantic
  // validation as that would not match the path parameters defined in the path template
  // and cause the semantic validation to fail.
  options.shouldResolveParameterizedHost = false;

  // We shoudln't be resolving nullable types for semantic validaiton as we'll replace nodes
  // with oneof arrays which are not semantically valid in swagger 2.0 schema.
  options.shouldResolveNullableTypes = false;
  let validator = new SpecValidator(specPath, null, options);
  exports.finalValidationResult[specPath] = validator.specValidationResult;
  return validator.initialize().then(function () {
    log.info(`Semantically validating  ${specPath}:\n`);
    return validator.validateSpec().then(function (result) {
      exports.updateEndResultOfSingleValidation(validator);
      exports.logDetailedInfo(validator);
      return Promise.resolve(validator.specValidationResult);
    });
  }).catch(function (err) {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.validateCompositeSpec = function validateCompositeSpec(compositeSpecPath, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  return exports.getDocumentsFromCompositeSwagger(compositeSpecPath).then(function (docs) {
    options.consoleLogLevel = log.consoleLogLevel;
    options.logFilepath = log.filepath;
    let promiseFactories = docs.map(function (doc) {
      return function () { return exports.validateSpec(doc, options); };
    });
    return utils.executePromisesSequentially(promiseFactories);
  }).catch(function (err) {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.validateExamples = function validateExamples(specPath, operationIds, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  let validator = new SpecValidator(specPath, null, options);
  exports.finalValidationResult[specPath] = validator.specValidationResult;
  return validator.initialize().then(function () {
    log.info(`Validating "examples" and "x-ms-examples" in  ${specPath}:\n`);
    validator.validateOperations(operationIds);
    exports.updateEndResultOfSingleValidation(validator);
    exports.logDetailedInfo(validator);
    return Promise.resolve(validator.specValidationResult);
  }).catch(function (err) {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.validateExamplesInCompositeSpec = function validateExamplesInCompositeSpec(compositeSpecPath, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  return exports.getDocumentsFromCompositeSwagger(compositeSpecPath).then(function (docs) {
    options.consoleLogLevel = log.consoleLogLevel;
    options.logFilepath = log.filepath;
    let promiseFactories = docs.map(function (doc) {
      return function () { return exports.validateExamples(doc, options); };
    });
    return utils.executePromisesSequentially(promiseFactories);
  }).catch(function (err) {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.resolveSpec = function resolveSpec(specPath, outputDir, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  let specFileName = path.basename(specPath);
  let resolver;
  return utils.parseJson(specPath).then((result) => {
    resolver = new SpecResolver(specPath, result, options);
    return resolver.resolve();
  }).then(() => {
    let resolvedSwagger = JSON.stringify(resolver.specInJson, null, 2);
    if (outputDir !== './' && !fs.existsSync(outputDir)) {
      fs.mkdirSync(outputDir);
    }
    let outputFilepath = `${path.join(outputDir, specFileName)}`;
    fs.writeFileSync(`${path.join(outputDir, specFileName)}`, resolvedSwagger, { encoding: 'utf8' });
    console.log(`Saved the resolved spec at "${outputFilepath}".`);
    return Promise.resolve();
  }).catch((err) => {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.resolveCompositeSpec = function resolveCompositeSpec(specPath, outputDir, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  return exports.getDocumentsFromCompositeSwagger(specPath).then(function (docs) {
    options.consoleLogLevel = log.consoleLogLevel;
    options.logFilepath = log.filepath;
    let promiseFactories = docs.map(function (doc) {
      return function () { return exports.resolveSpec(doc, outputDir, options); };
    });
    return utils.executePromisesSequentially(promiseFactories);
  }).catch(function (err) {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.generateWireFormat = function generateWireFormat(specPath, outDir, emitYaml, operationIds, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  let wfGenerator = new WireFormatGenerator(specPath, null, outDir, emitYaml);
  return wfGenerator.initialize().then(function () {
    log.info(`Generating wire format request and responses for swagger spec: "${specPath}":\n`);
    wfGenerator.processOperations(operationIds);
    return Promise.resolve();
  }).catch(function (err) {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.generateWireFormatInCompositeSpec = function generateWireFormatInCompositeSpec(compositeSpecPath, outDir, emitYaml, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  return exports.getDocumentsFromCompositeSwagger(compositeSpecPath).then(function (docs) {
    options.consoleLogLevel = log.consoleLogLevel;
    options.logFilepath = log.filepath;
    let promiseFactories = docs.map(function (doc) {
      return function () { return exports.generateWireFormat(doc, outDir, emitYaml, null, options); };
    });
    return utils.executePromisesSequentially(promiseFactories);
  }).catch(function (err) {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.generateUml = function generateUml(specPath, outputDir, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  let specFileName = path.basename(specPath);
  let resolver;
  let resolverOptions = {};
  resolverOptions.shouldResolveRelativePaths = true;
  resolverOptions.shouldResolveXmsExamples = false;
  resolverOptions.shouldResolveAllOf = false;
  resolverOptions.shouldSetAdditionalPropertiesFalse = false;
  resolverOptions.shouldResolvePureObjects = false;
  resolverOptions.shouldResolveDiscriminator = false;
  resolverOptions.shouldResolveParameterizedHost = false;
  resolverOptions.shouldResolveNullableTypes = false;
  return utils.parseJson(specPath).then((result) => {
    resolver = new SpecResolver(specPath, result, resolverOptions);
    return resolver.resolve();
  }).then(() => {
    let umlGenerator = new UmlGenerator(resolver.specInJson, options);
    return umlGenerator.generateDiagramFromGraph();
  }).then((svgGraph) => {
    if (outputDir !== './' && !fs.existsSync(outputDir)) {
      fs.mkdirSync(outputDir);
    }
    let svgFile = specFileName.replace(path.extname(specFileName), '.svg');
    let outputFilepath = `${path.join(outputDir, svgFile)}`;
    fs.writeFileSync(`${path.join(outputDir, svgFile)}`, svgGraph, { encoding: 'utf8' });
    console.log(`Saved the uml at "${outputFilepath}". Please open the file in a browser.`);
    return Promise.resolve();
  }).catch((err) => {
    log.error(err);
    return Promise.reject(err);
  });
};

exports.updateEndResultOfSingleValidation = function updateEndResultOfSingleValidation(validator) {
  log.info('No Errors were found.');
  if (!validator.specValidationResult.validityStatus) {
    process.exitCode = 1;
    exports.finalValidationResult.validityStatus = validator.specValidationResult.validityStatus;
  }
  return;
};

exports.logDetailedInfo = function logDetailedInfo(validator) {
  if (log.consoleLogLevel === 'json') {
    console.dir(validator.specValidationResult, { depth: null, colors: true });
  }
  log.silly('############################');
  log.silly(validator.specValidationResult);
  log.silly('----------------------------');
};

exports.extractXMsExamples = function extractXMsExamples(specPath, recordings, options) {
  if (!options) options = {};
  log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
  log.filepath = options.logFilepath || log.filepath;
  let xMsExampleExtractor = new XMsExampleExtractor(specPath, recordings, options);
  return xMsExampleExtractor.extract();
};
<|endoftext|>


<commit_before>module.exports.complete = false;

module.exports.test = function (testType) {
    var TESTS_URL = "https://github.com/NissimHadar/hifi_tests/blob/firstTest/tests/";
    var SUFFIX = "?raw=true";
    var autoTester = Script.require(TESTS_URL + "utils/autoTester.js" + SUFFIX);
    var spectatorCameraConfig = autoTester.setupTests(Script.resolvePath("."));

    // Create the zone centered at the avatar position
    var pos = MyAvatar.position;

    // As a 5 meters cube box
    var dim = { x: 5.0, y: 5.0, z: 5.0};

    // Define zone properties
    var properties = {
        lifetime: 60,
        type: "Zone",
        name: "test create zone",
        position: pos,
        dimensions: dim,
        keyLight:{"color": {"red":0,"green":255,"blue":0}},

        skyboxMode: "enabled",
        skybox:{"color":{"red":0,"green":0,"blue":255}}
    };
    var zone = Entities.addEntity(properties);

    // An array of tests is created.  These may be called via the timing mechanism for auto-testing,
    // or stepped through with the space bar
    var steps = [
        function () {
            spectatorCameraConfig.position = {x: pos.x, y: pos.y + 0.6, z: pos.z};
        },

        // Take snapshot
        function () {
        },

        // Clean up after test
        function () {
            Entities.deleteEntity(zone);
            module.exports.complete = true;
        }
    ]

    var result = autoTester.runTests(testType, steps);
};
<commit_msg>adjusting the url system<commit_after>module.exports.complete = false;

module.exports.test = function (testType) {
    var autoTester = Script.require("https://raw.githubusercontent.com/highfidelity/hifi_tests/master/tests/utils/autoTester.js" );
    var spectatorCameraConfig = autoTester.setupTests(Script.resolvePath("."));

    // Create the zone centered at the avatar position
    var pos = MyAvatar.position;

    // As a 5 meters cube box
    var dim = { x: 5.0, y: 5.0, z: 5.0};

    // Define zone properties
    var properties = {
        lifetime: 60,
        type: "Zone",
        name: "test create zone",
        position: pos,
        dimensions: dim,
        keyLight:{"color": {"red":0,"green":255,"blue":0}},

        skyboxMode: "enabled",
        skybox:{"color":{"red":0,"green":0,"blue":255}}
    };
    var zone = Entities.addEntity(properties);

    // An array of tests is created.  These may be called via the timing mechanism for auto-testing,
    // or stepped through with the space bar
    var steps = [
        function () {
            spectatorCameraConfig.position = {x: pos.x, y: pos.y + 0.6, z: pos.z};
        },

        // Take snapshot
        function () {
        },

        // Clean up after test
        function () {
            Entities.deleteEntity(zone);
            module.exports.complete = true;
        }
    ]

    var result = autoTester.runTests(testType, steps);
};
<|endoftext|>