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

/**
 * WavelengthSlider is a slider-like control used for setting visible wavelength.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var ArrowButton = require( 'SCENERY_PHET/buttons/ArrowButton' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var SpectrumNode = require( 'SCENERY_PHET/SpectrumNode' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Shape = require( 'KITE/Shape' );
  var TandemDragHandler = require( 'TANDEM/scenery/input/TandemDragHandler' );
  var StringUtils = require( 'PHETCOMMON/util/StringUtils' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Util = require( 'DOT/Util' );
  var VisibleColor = require( 'SCENERY_PHET/VisibleColor' );
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );
  var Tandem = require( 'TANDEM/Tandem' );

  // strings
  var wavelengthSliderPattern0Wavelength1UnitsString = require( 'string!SCENERY_PHET/WavelengthSlider.pattern_0wavelength_1units' );
  var unitsNmString = require( 'string!SCENERY_PHET/units_nm' );

  /**
   * @param {Property.<number>} wavelength - in nm
   * @param {Object} [options]
   * @constructor
   */
  function WavelengthSlider( wavelength, options ) {

    options = options || {};

    // calculate default touchAreaExpand parameters as half of the width
    var defaultThumbWidth = 35;
    var defaultThumbHeight = 45;
    var thumbWidth = options.thumbWidth || defaultThumbWidth;
    var thumbHeight = options.thumbHeight || defaultThumbHeight;
    var thumbTouchAreaXDilation = options.thumbTouchAreaXDilation || 0.5 * thumbWidth;
    var thumbTouchAreaYDilation = options.thumbTouchAreaYDilation || 0.5 * thumbHeight;

    // options that are specific to this type
    options = _.extend( {
      minWavelength: VisibleColor.MIN_WAVELENGTH,
      maxWavelength: VisibleColor.MAX_WAVELENGTH,
      trackWidth: 150,
      trackHeight: 30,
      trackOpacity: 1,
      trackBorderStroke: 'black',
      thumbWidth: defaultThumbWidth,
      thumbHeight: defaultThumbHeight,
      valueFont: new PhetFont( 20 ),
      valueFill: 'black',
      valueVisible: true,
      tweakersVisible: true,
      cursorVisible: true,
      cursorStroke: 'black',
      pointerAreasOverTrack: false,
      tandem: null
    }, options );

    Tandem.validateOptions( options ); // The tandem is required when brand==='phet-io'

    // validate wavelengths
    assert && assert( options.minWavelength < options.maxWavelength );
    assert && assert( options.minWavelength >= VisibleColor.MIN_WAVELENGTH && options.minWavelength <= VisibleColor.MAX_WAVELENGTH );
    assert && assert( options.maxWavelength >= VisibleColor.MIN_WAVELENGTH && options.maxWavelength <= VisibleColor.MAX_WAVELENGTH );

    var thisNode = this;
    Node.call( thisNode );

    var thumb = new Thumb( options.thumbWidth, options.thumbHeight, thumbTouchAreaXDilation, thumbTouchAreaYDilation, options.pointerAreasOverTrack, options.trackHeight );
    var valueDisplay = ( options.valueVisible ) ? new ValueDisplay( wavelength, options.valueFont, options.valueFill ) : null;
    var track = new SpectrumNode( options.trackWidth, options.trackHeight, options.minWavelength, options.maxWavelength, options.trackOpacity );
    var cursor = ( options.cursorVisible ) ? new Cursor( 3, track.height, options.cursorStroke ) : null;

    // tweaker buttons for single-unit increments
    var plusButton;
    var minusButton;
    if ( options.tweakersVisible ) {

      var plusButtonOptions = {};
      var minusButtonOptions = {};
      if ( options.tandem ) {
        plusButtonOptions.tandem = options.tandem.createTandem( 'plusButton' );
        minusButtonOptions.tandem = options.tandem.createTandem( 'minusButton' );
      }

      plusButton = new ArrowButton( 'right', function() {
        wavelength.set( wavelength.get() + 1 );
      }, plusButtonOptions );
      minusButton = new ArrowButton( 'left', function() {
        wavelength.set( wavelength.get() - 1 );
      }, minusButtonOptions );
    }

    /*
     * Put a border around the track.
     * We don't stroke the track itself because stroking the track will affect its bounds,
     * and will thus affect the drag handle behavior.
     * Having a separate border also gives subclasses a place to add markings (eg, tick marks)
     * without affecting the track's bounds.
     */
    var trackBorder = new Rectangle( 0, 0, track.width, track.height, {
      stroke: options.trackBorderStroke,
      lineWidth: 1,
      pickable: false
    } );

    // rendering order
    thisNode.addChild( track );
    thisNode.addChild( trackBorder );
    thisNode.addChild( thumb );
    if ( valueDisplay ) { thisNode.addChild( valueDisplay ); }
    if ( cursor ) { thisNode.addChild( cursor ); }
    if ( options.tweakersVisible ) {
      thisNode.addChild( plusButton );
      thisNode.addChild( minusButton );
    }

    // layout
    if ( cursor ) { cursor.top = track.top; }
    thumb.top = track.bottom;
    if ( valueDisplay ) { valueDisplay.bottom = track.top - 2; }
    if ( options.tweakersVisible ) {
      plusButton.left = track.right + 8;
      plusButton.centerY = track.centerY;
      minusButton.right = track.left - 8;
      minusButton.centerY = track.centerY;
    }

    // transforms between position and wavelength
    var positionToWavelength = function( x ) {
      return Math.floor( Util.clamp( Util.linear( 0, track.width, options.minWavelength, options.maxWavelength, x ), options.minWavelength, options.maxWavelength ) );
    };
    var wavelengthToPosition = function( wavelength ) {
      return Math.floor( Util.clamp( Util.linear( options.minWavelength, options.maxWavelength, 0, track.width, wavelength ), 0, track.width ) );
    };

    // track interactivity
    // TODO: Other tracks continue to drag instead of just setting a single value, why not this?
    track.cursor = 'pointer';
    track.addInputListener( new TandemDragHandler( {
      tandem: options.tandem ? options.tandem.createTandem( 'trackInputListener' ) : null,
      start: function( event ) {
        var x = track.globalToParentPoint( event.pointer.point ).x;
        var newValue = positionToWavelength( x );
        wavelength.set( newValue );
      }
    } ) );

    // thumb interactivity
    thumb.cursor = 'pointer';
    var clickXOffset = 0; // x-offset between initial click and thumb's origin
    thumb.addInputListener( new TandemDragHandler( {
      tandem: options.tandem ? options.tandem.createTandem( 'thumbInputListener' ) : null,
      allowTouchSnag: true,

      start: function( event ) {
        clickXOffset = thumb.globalToParentPoint( event.pointer.point ).x - thumb.x;
      },

      drag: function( event ) {
        var x = thumb.globalToParentPoint( event.pointer.point ).x - clickXOffset;
        var value = positionToWavelength( x );
        wavelength.set( value );
      }
    } ) );

    // sync with model
    var updateUI = function( wavelength ) {
      // positions
      var x = wavelengthToPosition( wavelength );
      thumb.centerX = x;
      if ( cursor ) { cursor.centerX = x; }
      if ( valueDisplay ) { valueDisplay.centerX = x; }
      // thumb color
      thumb.fill = VisibleColor.wavelengthToColor( wavelength );
      // tweaker buttons
      if ( options.tweakersVisible ) {
        plusButton.enabled = ( wavelength < options.maxWavelength );
        minusButton.enabled = ( wavelength > options.minWavelength );
      }
    };
    var wavelengthListener = function( wavelength ) {
      updateUI( wavelength );
    };
    wavelength.link( wavelengthListener );

    /*
     * The horizontal bounds of the wavelength control changes as the slider knob is dragged.
     * To prevent this, we determine the extents of the control's bounds at min and max values,
     * then add an invisible horizontal strut.
     */
    // determine bounds at min and max wavelength settings
    updateUI( options.minWavelength );
    var minX = thisNode.left;
    updateUI( options.maxWavelength );
    var maxX = thisNode.right;

    // restore the wavelength
    updateUI( wavelength.get() );

    // add a horizontal strut
    var strut = new Rectangle( minX, 0, maxX - minX, 1, { pickable: false } );
    thisNode.addChild( strut );
    strut.moveToBack();

    thisNode.mutate( options );
    options.tandem && options.tandem.addInstance( this );

    this.disposeWavelengthSlider = function() {
      options.tandem && options.tandem.removeInstance( this );
      wavelength.unlink( wavelengthListener );
    };
  }

  sceneryPhet.register( 'WavelengthSlider', WavelengthSlider );

  /**
   * The slider thumb (aka knob)
   * @param {number} width
   * @param {number} height
   * @param {number} touchAreaXDilation
   * @param {number} touchAreaYDilation
   * @param {Boolean} pointerAreasOverTrack whether or not the pointer areas for dragging should extend to the top of the track
   * @param {number} trackHeight only used if pointerAreasOverTrack is true
   * @constructor
   */
  function Thumb( width, height, touchAreaXDilation, touchAreaYDilation, pointerAreasOverTrack, trackHeight ) {
    // set the radius of the arcs based on the height or width, whichever is smaller
    var radiusScale = 0.15;
    var radius = ( width < height ) ? radiusScale * width : radiusScale * height;

    // calculate some parameters of the upper triangles of the thumb for getting arc offsets
    var hypotenuse = Math.sqrt( Math.pow( 0.5 * width, 2 ) + Math.pow( 0.3 * height, 2 ) );
    var angle = Math.acos( width * 0.5 / hypotenuse );
    var heightOffset = radius * Math.sin( angle );

    // draw the thumb shape starting at the right upper corner of the pentagon below the arc,
    // this way we can get the arc coordinates for the arc in this corner from the other side, which
    // will be easier to calculate arcing from bottom to top
    var shape = new Shape()
      .moveTo( 0.5 * width, 0.3 * height + heightOffset )
      .lineTo( 0.5 * width, 1 * height - radius )
      .arc( 0.5 * width - radius, 1 * height - radius, radius, 0, Math.PI / 2 )
      .lineTo( -0.5 * width + radius, 1 * height )
      .arc( -0.5 * width + radius, 1 * height - radius, radius, Math.PI / 2, Math.PI )
      .lineTo( -0.5 * width, 0.3 * height + heightOffset )
      .arc( -0.5 * width + radius, 0.3 * height + heightOffset, radius, Math.PI, Math.PI + angle );

    // save the coordinates for the point above the left side arc, for use on the other side
    var sideArcPoint = shape.getLastPoint();

    shape.lineTo( 0, 0 )
      .lineTo( -sideArcPoint.x, sideArcPoint.y )
      .arc( 0.5 * width - radius, 0.3 * height + heightOffset, radius, -angle, 0 )
      .close();

    Path.call( this, shape, { stroke: 'black', lineWidth: 1, fill: 'black' } );

    // compute mouse/touch areas, extend up to top of track if pointerAreasOverTrack is true
    var bounds = shape.bounds.copy();
    if ( pointerAreasOverTrack ) {
      this.touchArea = Shape.rectangle( bounds.minX - touchAreaXDilation, bounds.minY - trackHeight, bounds.width + 2 * touchAreaXDilation, bounds.height + 2 * touchAreaYDilation + trackHeight );
      this.mouseArea = Shape.rectangle( bounds.minX, bounds.minY - trackHeight, bounds.width, bounds.height + trackHeight );
    }

    // don't extend above the thumb so that we don't encroach on slider track if pointerAreasOverTrack is false
    else {
      this.touchArea = Shape.rectangle( bounds.minX - touchAreaXDilation, bounds.minY, bounds.width + 2 * touchAreaXDilation, bounds.height + 2 * touchAreaYDilation );
    }
  }

  sceneryPhet.register( 'WavelengthSlider.Thumb', Thumb );

  inherit( Path, Thumb );

  /**
   * Displays the value and units.
   * @param property
   * @param {string} font
   * @param {string} fill
   * @constructor
   */
  function ValueDisplay( property, font, fill ) {
    var thisNode = this;
    Text.call( this, '?', { font: font, fill: fill } );
    property.link( function( value ) {
      thisNode.text = StringUtils.format( wavelengthSliderPattern0Wavelength1UnitsString, Util.toFixed( value, 0 ), unitsNmString );
    } );
  }

  sceneryPhet.register( 'WavelengthSlider.ValueDisplay', ValueDisplay );

  inherit( Text, ValueDisplay );

  /**
   * Rectangular 'cursor' that appears in the track directly above the thumb. Origin is at top center of cursor.
   * @param {number} width
   * @param {number} height
   * @constructor
   */
  function Cursor( width, height, stroke ) {
    Rectangle.call( this, -width / 2, 0, width, height, { stroke: stroke, lineWidth: 1 } );
  }

  sceneryPhet.register( 'WavelengthSlider.Cursor', Cursor );

  inherit( Rectangle, Cursor );

  return inherit( Node, WavelengthSlider, {

    // @public
    dispose: function() {
      this.disposeWavelengthSlider();
    }
  } );
} );
<commit_msg>Instrumented types for beers-law-lab, see https://github.com/phetsims/beers-law-lab/issues/181<commit_after>// Copyright 2013-2015, University of Colorado Boulder

/**
 * WavelengthSlider is a slider-like control used for setting visible wavelength.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var ArrowButton = require( 'SCENERY_PHET/buttons/ArrowButton' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var SpectrumNode = require( 'SCENERY_PHET/SpectrumNode' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Shape = require( 'KITE/Shape' );
  var TandemDragHandler = require( 'TANDEM/scenery/input/TandemDragHandler' );
  var StringUtils = require( 'PHETCOMMON/util/StringUtils' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Util = require( 'DOT/Util' );
  var VisibleColor = require( 'SCENERY_PHET/VisibleColor' );
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );
  var Tandem = require( 'TANDEM/Tandem' );

  // strings
  var wavelengthSliderPattern0Wavelength1UnitsString = require( 'string!SCENERY_PHET/WavelengthSlider.pattern_0wavelength_1units' );
  var unitsNmString = require( 'string!SCENERY_PHET/units_nm' );

  // phet-io modules
  var TNode = require( 'PHET_IO/types/scenery/nodes/TNode' );

  /**
   * @param {Property.<number>} wavelength - in nm
   * @param {Object} [options]
   * @constructor
   */
  function WavelengthSlider( wavelength, options ) {

    options = options || {};

    // calculate default touchAreaExpand parameters as half of the width
    var defaultThumbWidth = 35;
    var defaultThumbHeight = 45;
    var thumbWidth = options.thumbWidth || defaultThumbWidth;
    var thumbHeight = options.thumbHeight || defaultThumbHeight;
    var thumbTouchAreaXDilation = options.thumbTouchAreaXDilation || 0.5 * thumbWidth;
    var thumbTouchAreaYDilation = options.thumbTouchAreaYDilation || 0.5 * thumbHeight;

    // options that are specific to this type
    options = _.extend( {
      minWavelength: VisibleColor.MIN_WAVELENGTH,
      maxWavelength: VisibleColor.MAX_WAVELENGTH,
      trackWidth: 150,
      trackHeight: 30,
      trackOpacity: 1,
      trackBorderStroke: 'black',
      thumbWidth: defaultThumbWidth,
      thumbHeight: defaultThumbHeight,
      valueFont: new PhetFont( 20 ),
      valueFill: 'black',
      valueVisible: true,
      tweakersVisible: true,
      cursorVisible: true,
      cursorStroke: 'black',
      pointerAreasOverTrack: false,
      tandem: null
    }, options );

    Tandem.validateOptions( options ); // The tandem is required when brand==='phet-io'

    // validate wavelengths
    assert && assert( options.minWavelength < options.maxWavelength );
    assert && assert( options.minWavelength >= VisibleColor.MIN_WAVELENGTH && options.minWavelength <= VisibleColor.MAX_WAVELENGTH );
    assert && assert( options.maxWavelength >= VisibleColor.MIN_WAVELENGTH && options.maxWavelength <= VisibleColor.MAX_WAVELENGTH );

    var thisNode = this;
    Node.call( thisNode );

    var thumb = new Thumb( options.thumbWidth, options.thumbHeight, thumbTouchAreaXDilation, thumbTouchAreaYDilation, options.pointerAreasOverTrack, options.trackHeight );
    var valueDisplay = ( options.valueVisible ) ? new ValueDisplay( wavelength, options.valueFont, options.valueFill ) : null;
    var track = new SpectrumNode( options.trackWidth, options.trackHeight, options.minWavelength, options.maxWavelength, options.trackOpacity );
    var cursor = ( options.cursorVisible ) ? new Cursor( 3, track.height, options.cursorStroke ) : null;

    // tweaker buttons for single-unit increments
    var plusButton;
    var minusButton;
    if ( options.tweakersVisible ) {

      var plusButtonOptions = {};
      var minusButtonOptions = {};
      if ( options.tandem ) {
        plusButtonOptions.tandem = options.tandem.createTandem( 'plusButton' );
        minusButtonOptions.tandem = options.tandem.createTandem( 'minusButton' );
      }

      plusButton = new ArrowButton( 'right', function() {
        wavelength.set( wavelength.get() + 1 );
      }, plusButtonOptions );
      minusButton = new ArrowButton( 'left', function() {
        wavelength.set( wavelength.get() - 1 );
      }, minusButtonOptions );
    }

    /*
     * Put a border around the track.
     * We don't stroke the track itself because stroking the track will affect its bounds,
     * and will thus affect the drag handle behavior.
     * Having a separate border also gives subclasses a place to add markings (eg, tick marks)
     * without affecting the track's bounds.
     */
    var trackBorder = new Rectangle( 0, 0, track.width, track.height, {
      stroke: options.trackBorderStroke,
      lineWidth: 1,
      pickable: false
    } );

    // rendering order
    thisNode.addChild( track );
    thisNode.addChild( trackBorder );
    thisNode.addChild( thumb );
    if ( valueDisplay ) { thisNode.addChild( valueDisplay ); }
    if ( cursor ) { thisNode.addChild( cursor ); }
    if ( options.tweakersVisible ) {
      thisNode.addChild( plusButton );
      thisNode.addChild( minusButton );
    }

    // layout
    if ( cursor ) { cursor.top = track.top; }
    thumb.top = track.bottom;
    if ( valueDisplay ) { valueDisplay.bottom = track.top - 2; }
    if ( options.tweakersVisible ) {
      plusButton.left = track.right + 8;
      plusButton.centerY = track.centerY;
      minusButton.right = track.left - 8;
      minusButton.centerY = track.centerY;
    }

    // transforms between position and wavelength
    var positionToWavelength = function( x ) {
      return Math.floor( Util.clamp( Util.linear( 0, track.width, options.minWavelength, options.maxWavelength, x ), options.minWavelength, options.maxWavelength ) );
    };
    var wavelengthToPosition = function( wavelength ) {
      return Math.floor( Util.clamp( Util.linear( options.minWavelength, options.maxWavelength, 0, track.width, wavelength ), 0, track.width ) );
    };

    // track interactivity
    // TODO: Other tracks continue to drag instead of just setting a single value, why not this?
    track.cursor = 'pointer';
    track.addInputListener( new TandemDragHandler( {
      tandem: options.tandem ? options.tandem.createTandem( 'trackInputListener' ) : null,
      start: function( event ) {
        var x = track.globalToParentPoint( event.pointer.point ).x;
        var newValue = positionToWavelength( x );
        wavelength.set( newValue );
      }
    } ) );

    // thumb interactivity
    thumb.cursor = 'pointer';
    var clickXOffset = 0; // x-offset between initial click and thumb's origin
    thumb.addInputListener( new TandemDragHandler( {
      tandem: options.tandem ? options.tandem.createTandem( 'thumbInputListener' ) : null,
      allowTouchSnag: true,

      start: function( event ) {
        clickXOffset = thumb.globalToParentPoint( event.pointer.point ).x - thumb.x;
      },

      drag: function( event ) {
        var x = thumb.globalToParentPoint( event.pointer.point ).x - clickXOffset;
        var value = positionToWavelength( x );
        wavelength.set( value );
      }
    } ) );

    // sync with model
    var updateUI = function( wavelength ) {
      // positions
      var x = wavelengthToPosition( wavelength );
      thumb.centerX = x;
      if ( cursor ) { cursor.centerX = x; }
      if ( valueDisplay ) { valueDisplay.centerX = x; }
      // thumb color
      thumb.fill = VisibleColor.wavelengthToColor( wavelength );
      // tweaker buttons
      if ( options.tweakersVisible ) {
        plusButton.enabled = ( wavelength < options.maxWavelength );
        minusButton.enabled = ( wavelength > options.minWavelength );
      }
    };
    var wavelengthListener = function( wavelength ) {
      updateUI( wavelength );
    };
    wavelength.link( wavelengthListener );

    /*
     * The horizontal bounds of the wavelength control changes as the slider knob is dragged.
     * To prevent this, we determine the extents of the control's bounds at min and max values,
     * then add an invisible horizontal strut.
     */
    // determine bounds at min and max wavelength settings
    updateUI( options.minWavelength );
    var minX = thisNode.left;
    updateUI( options.maxWavelength );
    var maxX = thisNode.right;

    // restore the wavelength
    updateUI( wavelength.get() );

    // add a horizontal strut
    var strut = new Rectangle( minX, 0, maxX - minX, 1, { pickable: false } );
    thisNode.addChild( strut );
    strut.moveToBack();

    thisNode.mutate( options );
    options.tandem && options.tandem.addInstance( this, TNode );

    this.disposeWavelengthSlider = function() {
      options.tandem && options.tandem.removeInstance( this );
      wavelength.unlink( wavelengthListener );
    };
  }

  sceneryPhet.register( 'WavelengthSlider', WavelengthSlider );

  /**
   * The slider thumb (aka knob)
   * @param {number} width
   * @param {number} height
   * @param {number} touchAreaXDilation
   * @param {number} touchAreaYDilation
   * @param {Boolean} pointerAreasOverTrack whether or not the pointer areas for dragging should extend to the top of the track
   * @param {number} trackHeight only used if pointerAreasOverTrack is true
   * @constructor
   */
  function Thumb( width, height, touchAreaXDilation, touchAreaYDilation, pointerAreasOverTrack, trackHeight ) {
    // set the radius of the arcs based on the height or width, whichever is smaller
    var radiusScale = 0.15;
    var radius = ( width < height ) ? radiusScale * width : radiusScale * height;

    // calculate some parameters of the upper triangles of the thumb for getting arc offsets
    var hypotenuse = Math.sqrt( Math.pow( 0.5 * width, 2 ) + Math.pow( 0.3 * height, 2 ) );
    var angle = Math.acos( width * 0.5 / hypotenuse );
    var heightOffset = radius * Math.sin( angle );

    // draw the thumb shape starting at the right upper corner of the pentagon below the arc,
    // this way we can get the arc coordinates for the arc in this corner from the other side, which
    // will be easier to calculate arcing from bottom to top
    var shape = new Shape()
      .moveTo( 0.5 * width, 0.3 * height + heightOffset )
      .lineTo( 0.5 * width, 1 * height - radius )
      .arc( 0.5 * width - radius, 1 * height - radius, radius, 0, Math.PI / 2 )
      .lineTo( -0.5 * width + radius, 1 * height )
      .arc( -0.5 * width + radius, 1 * height - radius, radius, Math.PI / 2, Math.PI )
      .lineTo( -0.5 * width, 0.3 * height + heightOffset )
      .arc( -0.5 * width + radius, 0.3 * height + heightOffset, radius, Math.PI, Math.PI + angle );

    // save the coordinates for the point above the left side arc, for use on the other side
    var sideArcPoint = shape.getLastPoint();

    shape.lineTo( 0, 0 )
      .lineTo( -sideArcPoint.x, sideArcPoint.y )
      .arc( 0.5 * width - radius, 0.3 * height + heightOffset, radius, -angle, 0 )
      .close();

    Path.call( this, shape, { stroke: 'black', lineWidth: 1, fill: 'black' } );

    // compute mouse/touch areas, extend up to top of track if pointerAreasOverTrack is true
    var bounds = shape.bounds.copy();
    if ( pointerAreasOverTrack ) {
      this.touchArea = Shape.rectangle( bounds.minX - touchAreaXDilation, bounds.minY - trackHeight, bounds.width + 2 * touchAreaXDilation, bounds.height + 2 * touchAreaYDilation + trackHeight );
      this.mouseArea = Shape.rectangle( bounds.minX, bounds.minY - trackHeight, bounds.width, bounds.height + trackHeight );
    }

    // don't extend above the thumb so that we don't encroach on slider track if pointerAreasOverTrack is false
    else {
      this.touchArea = Shape.rectangle( bounds.minX - touchAreaXDilation, bounds.minY, bounds.width + 2 * touchAreaXDilation, bounds.height + 2 * touchAreaYDilation );
    }
  }

  sceneryPhet.register( 'WavelengthSlider.Thumb', Thumb );

  inherit( Path, Thumb );

  /**
   * Displays the value and units.
   * @param property
   * @param {string} font
   * @param {string} fill
   * @constructor
   */
  function ValueDisplay( property, font, fill ) {
    var thisNode = this;
    Text.call( this, '?', { font: font, fill: fill } );
    property.link( function( value ) {
      thisNode.text = StringUtils.format( wavelengthSliderPattern0Wavelength1UnitsString, Util.toFixed( value, 0 ), unitsNmString );
    } );
  }

  sceneryPhet.register( 'WavelengthSlider.ValueDisplay', ValueDisplay );

  inherit( Text, ValueDisplay );

  /**
   * Rectangular 'cursor' that appears in the track directly above the thumb. Origin is at top center of cursor.
   * @param {number} width
   * @param {number} height
   * @constructor
   */
  function Cursor( width, height, stroke ) {
    Rectangle.call( this, -width / 2, 0, width, height, { stroke: stroke, lineWidth: 1 } );
  }

  sceneryPhet.register( 'WavelengthSlider.Cursor', Cursor );

  inherit( Rectangle, Cursor );

  return inherit( Node, WavelengthSlider, {

    // @public
    dispose: function() {
      this.disposeWavelengthSlider();
    }
  } );
} );
<|endoftext|>


<commit_before>define(['jquery', 'underscore', 'backbone',
	'text!./app/views/templates/search-empty.html',
	'text!./app/views/templates/search-listing.html',
	'text!./app/views/templates/search-listings.html',
	'models', 'utils'], function($, _, Backbone, emptySearchTemplate, searchListingTemplate, searchContainerTemplate, Models, Utils) {
	var Views = {};

	Views.SearchResult = Backbone.View.extend({
		template: _.template(searchListingTemplate),

		events: {
			"click button#btn-pledge": "pledgeClick"
		},

		pledgeClick: function() {

		},

		initialize: function() {
			this.listenTo(this.model, "change", this.render);
			this.id = "search-" + this.model.attributes.id;
		},

		render: function() {
			this.$el.html(this.template(this.model.attributes));
			return this;
		}
	});

	Views.SearchResultListing = Backbone.View.extend({
		initialize: function() {
			this.listenTo(this.collection, 'add', this.collectionAdded);
			this.listenTo(this.collection, 'change', this.collectionChanged);
			this.listenTo(this.collection, 'remove', this.collectionRemoved);

			this.childViews = [];

			this._addAllModels();
		},

		render: function() {
			var fragment = document.createDocumentFragment();

			if (this.childViews.length > 0) {
				_(this.childViews).each(function(currentView) {
					fragment.appendChild(currentView.render().el);
				});
			} else {
				$(emptySearchTemplate).appendTo(fragment);
			}

			this.$el.html(fragment);
			return this;
		},

		collectionAdded: function(item) {
			this._addViewForModel(item);
			this.render();
		},

		collectionRemoved: function(item) {
			this.childViews = this.childViews.filter(function(view) {
				return view.model.id !== item.id;
			});
			this.render();
		},

		collectionChanged: function() {
			this.childViews = [];
			this._addAllModels();
		},

		_addViewForModel: function(item) {
			this.childViews.push(new Views.SearchResult({
				model: item
			}));
		},

		_addAllModels: function() {
			var that = this;
			this.collection.each(function(item) {
				that._addViewForModel(item);
			});
			this.render();
		}
	});

	Views.SearchSession = Backbone.View.extend({
		initialize: function() {
			var searchUrl = '/service/products/search' + Utils.urlencode(Utils.urlencode(this.options.searchTerm));
			var resultCollection = new Models.SearchResults({
				url: searchUrl
			});
			this.searchResultView = new Views.SearchResultListing({
				collection: resultCollection
			});
			this.render();
		},

		render: function() {
			this.$el.html(_.template(searchContainerTemplate, {
				searchTerm: this.options.searchTerm
			}));

			this.searchResultView.setElement('#search-listing');
			this.searchResultView.render();

			$('#search-section').fadeIn();
		}
	});

	Views.SearchForm = Backbone.View.extend({
		events: {
			"click a#btn-search": "searchClick",
            "keypress input[type=text]": "keypress"
		},

        keypress: function(e) {
            switch (e.keyCode) {
                case 13:
                this.searchClick();
            }
        },

		searchClick: function() {
			var searchListingView = new Views.SearchSession({
				searchTerm: $('#txt-search').val()
			});

			Utils.loadView(searchListingView);
		},

		initialize: function() {}
	});

	return Views;
});<commit_msg>Add missing trailing slash.<commit_after>define(['jquery', 'underscore', 'backbone',
	'text!./app/views/templates/search-empty.html',
	'text!./app/views/templates/search-listing.html',
	'text!./app/views/templates/search-listings.html',
	'models', 'utils'], function($, _, Backbone, emptySearchTemplate, searchListingTemplate, searchContainerTemplate, Models, Utils) {
	var Views = {};

	Views.SearchResult = Backbone.View.extend({
		template: _.template(searchListingTemplate),

		events: {
			"click button#btn-pledge": "pledgeClick"
		},

		pledgeClick: function() {

		},

		initialize: function() {
			this.listenTo(this.model, "change", this.render);
			this.id = "search-" + this.model.attributes.id;
		},

		render: function() {
			this.$el.html(this.template(this.model.attributes));
			return this;
		}
	});

	Views.SearchResultListing = Backbone.View.extend({
		initialize: function() {
			this.listenTo(this.collection, 'add', this.collectionAdded);
			this.listenTo(this.collection, 'change', this.collectionChanged);
			this.listenTo(this.collection, 'remove', this.collectionRemoved);

			this.childViews = [];

			this._addAllModels();
		},

		render: function() {
			var fragment = document.createDocumentFragment();

			if (this.childViews.length > 0) {
				_(this.childViews).each(function(currentView) {
					fragment.appendChild(currentView.render().el);
				});
			} else {
				$(emptySearchTemplate).appendTo(fragment);
			}

			this.$el.html(fragment);
			return this;
		},

		collectionAdded: function(item) {
			this._addViewForModel(item);
			this.render();
		},

		collectionRemoved: function(item) {
			this.childViews = this.childViews.filter(function(view) {
				return view.model.id !== item.id;
			});
			this.render();
		},

		collectionChanged: function() {
			this.childViews = [];
			this._addAllModels();
		},

		_addViewForModel: function(item) {
			this.childViews.push(new Views.SearchResult({
				model: item
			}));
		},

		_addAllModels: function() {
			var that = this;
			this.collection.each(function(item) {
				that._addViewForModel(item);
			});
			this.render();
		}
	});

	Views.SearchSession = Backbone.View.extend({
		initialize: function() {
			var searchUrl = '/service/products/search/' + Utils.urlencode(Utils.urlencode(this.options.searchTerm));
			var resultCollection = new Models.SearchResults({
				url: searchUrl
			});
			this.searchResultView = new Views.SearchResultListing({
				collection: resultCollection
			});
			this.render();
		},

		render: function() {
			this.$el.html(_.template(searchContainerTemplate, {
				searchTerm: this.options.searchTerm
			}));

			this.searchResultView.setElement('#search-listing');
			this.searchResultView.render();

			$('#search-section').fadeIn();
		}
	});

	Views.SearchForm = Backbone.View.extend({
		events: {
			"click a#btn-search": "searchClick",
            "keypress input[type=text]": "keypress"
		},

        keypress: function(e) {
            switch (e.keyCode) {
                case 13:
                this.searchClick();
            }
        },

		searchClick: function() {
			var searchListingView = new Views.SearchSession({
				searchTerm: $('#txt-search').val()
			});

			Utils.loadView(searchListingView);
		},

		initialize: function() {}
	});

	return Views;
});<|endoftext|>


<commit_before>/* global console, Backbone, _, $ */
angular
	.module('indx')
	.factory('collection', function () {
		'use strict';

		var Model = function () {};
		Model.extend = function (obj) {
			/// @ignore -- TODO
			var _F = function () {};
			_F.prototype = _.extend({}, Model.prototype, obj);
			return _F;
		};
		_.extend(Model.prototype, {
			is_new: false,
			is_editing: false,
			is_selected: false,
			create: function () {
				var that = this;
				console.log('creating item');
				this.box.get_obj(this.id).then(function (new_obj) {
					console.log('new item');
					new_obj.save(that.newAttributes).then(function () {
						console.log('saved');
						that.trigger('created', new_obj);
					});
				});
				return this;
			},
			remove: function () {
				console.log('remove item', this);
				this.destroy().then(function () {
					console.log('destroyed item');
					//that.trigger('destroyed', this);
				});
			},
			edit: function (is_new) {
				console.log('edit item', this);
				this.newAttributes = _.clone(this.attributes);
				this.is_new = is_new;
				this.is_editing = true;
				return this;
			},
			stage_and_save: function () {
				var that = this;
				console.log('stage and save');
				if (this.is_new) {
					console.log('new, so create');
					return this.create();
				} else {
					this.save(this.newAttributes).then(function () {
						console.log('saved');
						that.restore();
						// u.safe_apply($scope); TODO
					});
				}
			},
			save: function () {
				if (this.is_new) {
					console.warn('supressing save');
				} else {
					return Backbone.Model.prototype.save.apply(this, arguments);
				}
			},
			restore: function () {
				this.is_editing = false;
				this.trigger('restore');
			},
			select: function (selected, options) {
				options = options || {};
				console.log('select', selected);
				selected = _.isBoolean(selected) ? selected : true;
				if (this.is_selected !== selected) {
					console.log('selecting', selected);
					this.is_selected = selected;
					this.trigger('select', selected, options);
				}
			}
		});
		var Collection = Backbone.Collection.extend({
			/// the collection.Model the cast each model to
			model: Model,
			selected: undefined,
			initialize: function (models, options) {
				var that = this;
				this.options = options || {};
				this.box = this.options.box;
				this.obj = this.options.obj;
				this.on('add remove', function () {
					console.log('something in the collection has changed');
					that.save();
				}).on('add remove reset', function () {
					that._set_new_model(that._new_model);
				});
				this.reset(models);
				this.populate();
				this._set_new_model();
			},
			///
			/// @opt <{}> attributes Attributes of new model
			/// @opt <{}> options
			///
			/// @return <model> Model inheriting collection.model
			///
			/// Make a new instance of the model. When the model is saved,
			/// it will be put in the box as an obj and appended to the array.
			new_model: function (attributes, options) {
				var that = this,
					model = new Backbone.Model(attributes);
				options = options || {};
				this._extend_model(model);
				model
					.edit(true)
					.set({ timestamp: now() })
					.on('created', function (model) {
						console.log('going for the add');
						that.add(model);
						that._set_new_model();
						if (options.select) { model.select(); }
					})
					.on('restore', function () {
						that._set_new_model();
						if (options.select) { that.selected = undefined; }
					});
				this._set_new_model(model);
				if (options.select) { model.select(true, { save: false }); }
				return model;
			},
			_set_new_model: function (model) {
				this._new_model = model;
				this.all_models = model ? this.models.concat([ model ]) : this.models;
				this.filter();
				//console.log('update');
			},
			/// Remove the model from the array
			remove: function (model) {
				console.log('!!!remove!!!', arguments);
				if (this.selected === model) { this.selected = undefined; }
				return Backbone.Collection.prototype.remove.apply(this, arguments);
			},
			_extend_model: function (model) {
				var that = this,
					prototype = this.model.prototype;
				_.extend(model, prototype);
				if (prototype.initialize) {
					prototype.initialize.apply(model);
				}
				if (prototype.defaults) {
					_.defaults(model.attributes, prototype.defaults);
				}
				model.box = this.box;
				model.on('select', function (selected, options) {
					options = options || {};
					if (selected && that.selected === model) { return; }
					if (that.selected) { that.selected.select(false); }
					if (selected) {
						that.selected = model;
						that.trigger('select', model);
					}
					if (options.save !== false &&
							(that.options.save_selected || that.options.sync_selected)) {
						model.save({ selected: selected });
						that.each(function (m) {
							if (m !== model && pop(m.get('selected'))) {
								m.save({ selected: false });
							}
						});
					}
				}).on('restore', function () {
					that.trigger('update');
				});
				/*
				model.on('destroyed', function () { // TODO: unbind to prevent memory leaks
					console.log('detected destroy, removing model');
					that.remove(model);
				});*/
			},
			add: function (model, options) {
				var that = this;
				if (_.isArray(model)) {
					_.each(model, function (model) { that.add(model, options); });
				} else {
					if (!_.isObject(model)) {
						console.warn('There was a non-object stored??', model);
						return;
					}
					this._extend_model(model);
					if (this.options.save_selected || this.options.sync_selected) {
						if (pop(model.get('selected')) === true) {
							model.select();
						}
						if (this.options.sync_selected) {
							model.on('change:selected', function () {
								model.select(pop(model.get('selected')));
							});
						}
					}
					return Backbone.Collection.prototype.add.apply(this, arguments);
				}
			},
			/// Fetch the object and update the collection with models in the array
			fetch: function () {
				var that = this,
					promise = $.Deferred();
				this.obj.fetch().then(function () {
					that.populate();
					promise.resolve(that);
				});
				return promise;
			},
			/// Update the collection with models in the array. This will happen automatically when the array changes.
			populate: function () {
				if (!this.obj || !this.options.array_key) { return; }
				var that = this,
					obj = this.obj,
					array_key = this.options.array_key,
					arr = obj.get(array_key) || [];
				this.reset(arr);
				obj.on('change:' + array_key, function (obj, arr) {
					console.log('updating list -->', arguments);
					that.reset(arr);
				});
				//console.log('obj --> ', obj, array_key);
				//console.log('list --> ', arr);
			},
			/// Save the obj with the current array state. This will happen automatically when the array changes.
			save: function () {
				var that = this,
					promise = $.Deferred(),
					array_key = that.options.array_key;
				console.log('trying to save list = ', that.models);
				this.obj.save(array_key, that.models).then(function () {
					promise.resolve();
				}).fail(function () { promise.reject('Could not save'); });
				return promise;
			},
			filter: function (fn) {
				if (fn) { this._filter_fn = fn; }
				if (this._filter_fn) {
					this.filtered_models = _.filter(this.all_models, fn);
				} else {
					this.filtered_models = this.all_models;
				}
				this.trigger('update', this);
				return this;
			}
		});

		var now = function () {
			return (new Date()).valueOf();
		};
		var pop = function (arr) {
			if (!_.isArray(arr)) { return arr; }
			return _.first(arr);
		};

		return {
			Model: Model,
			Collection: Collection,
			now: now
		};

	});<commit_msg>added trigger for item edit<commit_after>/* global console, Backbone, _, $ */
angular
	.module('indx')
	.factory('collection', function () {
		'use strict';

		var Model = function () {};
		Model.extend = function (obj) {
			/// @ignore -- TODO
			var _F = function () {};
			_F.prototype = _.extend({}, Model.prototype, obj);
			return _F;
		};
		_.extend(Model.prototype, {
			is_new: false,
			is_editing: false,
			is_selected: false,
			initialize: function () {},
			create: function () {
				var that = this;
				console.log('creating item');
				this.box.get_obj(this.id).then(function (new_obj) {
					console.log('new item');
					new_obj.save(that.newAttributes).then(function () {
						console.log('saved');
						that.trigger('created', new_obj);
					});
				});
				return this;
			},
			remove: function () {
				console.log('remove item', this);
				this.destroy().then(function () {
					console.log('destroyed item');
					//that.trigger('destroyed', this);
				});
			},
			edit: function (is_new) {
				console.log('edit item', this);
				this.newAttributes = _.clone(this.attributes);
				this.is_new = is_new;
				this.is_editing = true;
				this.trigger('edit');
				return this;
			},
			stage_and_save: function () {
				var that = this;
				console.log('stage and save');
				if (this.is_new) {
					console.log('new, so create');
					return this.create();
				} else {
					this.save(this.newAttributes).then(function () {
						console.log('saved');
						that.restore();
						// u.safe_apply($scope); TODO
					});
				}
			},
			save: function () {
				if (this.is_new) {
					console.warn('supressing save');
				} else {
					return Backbone.Model.prototype.save.apply(this, arguments);
				}
			},
			restore: function () {
				this.is_editing = false;
				this.trigger('restore');
				return this;
			},
			select: function (selected, options) {
				options = options || {};
				console.log('select', selected);
				selected = _.isBoolean(selected) ? selected : true;
				if (this.is_selected !== selected) {
					console.log('selecting', selected);
					this.is_selected = selected;
					this.trigger('select', selected, options);
				}
			}
		});
		var Collection = Backbone.Collection.extend({
			/// the collection.Model the cast each model to
			model: Model,
			selected: undefined,
			initialize: function (models, options) {
				var that = this;
				this.options = options || {};
				this.box = this.options.box;
				this.obj = this.options.obj;
				this.on('add remove', function () {
					console.log('something in the collection has changed');
					that.save();
				}).on('add remove reset', function () {
					that._set_new_model(that._new_model);
				});
				this.reset(models);
				this.populate();
				this._set_new_model();
			},
			///
			/// @opt <{}> attributes Attributes of new model
			/// @opt <{}> options
			///
			/// @return <model> Model inheriting collection.model
			///
			/// Make a new instance of the model. When the model is saved,
			/// it will be put in the box as an obj and appended to the array.
			new_model: function (attributes, options) {
				var that = this,
					model = new Backbone.Model(attributes);
				options = options || {};
				this._extend_model(model);
				model
					.edit(true)
					.set({ timestamp: now() })
					.on('created', function (model) {
						console.log('going for the add');
						that.add(model);
						that._set_new_model();
						if (options.select) { model.select(); }
					})
					.on('restore', function () {
						that._set_new_model();
						if (options.select) { that.selected = undefined; }
					});
				this._set_new_model(model);
				if (options.select) { model.select(true, { save: false }); }
				return model;
			},
			_set_new_model: function (model) {
				this._new_model = model;
				this.all_models = model ? this.models.concat([ model ]) : this.models;
				this.filter();
				//console.log('update');
			},
			/// Remove the model from the array
			remove: function (model) {
				console.log('!!!remove!!!', arguments);
				if (this.selected === model) { this.selected = undefined; }
				return Backbone.Collection.prototype.remove.apply(this, arguments);
			},
			_extend_model: function (model) {
				var that = this,
					prototype = this.model.prototype;
				_.extend(model, prototype);
				if (prototype.initialize) {
					prototype.initialize.apply(model);
				}
				if (prototype.defaults) {
					_.defaults(model.attributes, prototype.defaults);
				}
				model.box = this.box;
				model.on('select', function (selected, options) {
					options = options || {};
					if (selected && that.selected === model) { return; }
					if (that.selected) { that.selected.select(false); }
					if (selected) {
						that.selected = model;
						that.trigger('select', model);
					}
					if (options.save !== false &&
							(that.options.save_selected || that.options.sync_selected)) {
						model.save({ selected: selected });
						that.each(function (m) {
							if (m !== model && pop(m.get('selected'))) {
								m.save({ selected: false });
							}
						});
					}
				}).on('restore', function () {
					that.trigger('update');
				});
				/*
				model.on('destroyed', function () { // TODO: unbind to prevent memory leaks
					console.log('detected destroy, removing model');
					that.remove(model);
				});*/
			},
			add: function (model, options) {
				var that = this;
				if (_.isArray(model)) {
					_.each(model, function (model) { that.add(model, options); });
				} else {
					if (!_.isObject(model)) {
						console.warn('There was a non-object stored??', model);
						return;
					}
					this._extend_model(model);
					if (this.options.save_selected || this.options.sync_selected) {
						if (pop(model.get('selected')) === true) {
							model.select();
						}
						if (this.options.sync_selected) {
							model.on('change:selected', function () {
								model.select(pop(model.get('selected')));
							});
						}
					}
					return Backbone.Collection.prototype.add.apply(this, arguments);
				}
			},
			/// Fetch the object and update the collection with models in the array
			fetch: function () {
				var that = this,
					promise = $.Deferred();
				this.obj.fetch().then(function () {
					that.populate();
					promise.resolve(that);
				});
				return promise;
			},
			/// Update the collection with models in the array. This will happen automatically when the array changes.
			populate: function () {
				if (!this.obj || !this.options.array_key) { return; }
				var that = this,
					obj = this.obj,
					array_key = this.options.array_key,
					arr = obj.get(array_key) || [];
				this.reset(arr);
				obj.on('change:' + array_key, function (obj, arr) {
					console.log('updating list -->', arguments);
					that.reset(arr);
				});
				//console.log('obj --> ', obj, array_key);
				//console.log('list --> ', arr);
			},
			/// Save the obj with the current array state. This will happen automatically when the array changes.
			save: function () {
				var that = this,
					promise = $.Deferred(),
					array_key = that.options.array_key;
				console.log('trying to save list = ', that.models);
				this.obj.save(array_key, that.models).then(function () {
					promise.resolve();
				}).fail(function () { promise.reject('Could not save'); });
				return promise;
			},
			filter: function (fn) {
				if (fn) { this._filter_fn = fn; }
				if (this._filter_fn) {
					this.filtered_models = _.filter(this.all_models, fn);
				} else {
					this.filtered_models = this.all_models;
				}
				this.trigger('update', this);
				return this;
			}
		});

		var now = function () {
			return (new Date()).valueOf();
		};
		var pop = function (arr) {
			if (!_.isArray(arr)) { return arr; }
			return _.first(arr);
		};

		return {
			Model: Model,
			Collection: Collection,
			now: now
		};

	});<|endoftext|>


<commit_before>////
//// Namespace for large drawing routines.
////

var bbop_mme_widgets = {};

// Add edit model contents to descriptive table.
bbop_mme_widgets.repaint_table = function(ecore, aid, table_div){

    var each = bbop.core.each;

    // First, lets get the headers that we'll need by poking the
    // model and getting all of the possible categories.
    var cat_list = [];
    each(ecore.get_nodes(),
	 function(enode_id, enode){
	     each(enode.types(),
		  function(in_type){
		      cat_list.push(aid.categorize(in_type));
		  });
	 });
    // Dedupe list.
    var tmph = bbop.core.hashify(cat_list);
    cat_list = bbop.core.get_keys(tmph);

    // If we actually got something, render the table. Otherwise,
    // a message.
    if( bbop.core.is_empty(cat_list) ){

	// Add to display.
	jQuery(table_div).empty();
	jQuery(table_div).append('

no instances

');

    }else{

	// Sort list according to known priorities.
	cat_list = cat_list.sort(
	    function(a, b){
		return aid.priority(b) - aid.priority(a);
	    });

	// Convert the ids into readable headers.
	var nav_tbl_headers = [];
	each(cat_list,
	     function(cat_id){
		 nav_tbl_headers.push(aid.readable(cat_id));
	     });

	//	var nav_tbl_headers = cat_list;
	//	    ['enabled by', 'activity', 'unknown', 'process', 'location'];

	var nav_tbl =
	    new bbop.html.table(nav_tbl_headers, [],
				{'class': ['table', 'table-bordered',
					   'table-hover',
					   'table-condensed'].join(' ')});

	//each(ecore.get_nodes(),
	each(ecore.edit_node_order(),
	     function(enode_id){
		 var enode = ecore.get_node(enode_id);
		 if( enode.existential() == 'real' ){

		     // Now that we have an enode, we want to mimic
		     // the order that we created for the header
		     // (cat_list). Start by binning the types.
		     var bin = {};
		     each(enode.types(),
			  function(in_type){
			      var cat = aid.categorize(in_type);
			      if( ! bin[cat] ){ bin[cat] = []; }
			      bin[cat].push(in_type);
			  });

		     // Now unfold the binned types into the table row
		     // according to the sorted order.
		     var table_row = [];
		     each(cat_list,
			  function(cat_id){
			      var accumulated_types = bin[cat_id];
			      var cell_cache = [];
			      each(accumulated_types,
				   function(atype){
				       var tt = bme_type_to_text(atype);
				       cell_cache.push(tt);
				   });
			      table_row.push(cell_cache.join('
'));
			  });
		     nav_tbl.add_to(table_row);
		 }
	     });

	// Add to display.
	jQuery(table_div).empty();
	jQuery(table_div).append(nav_tbl.to_string());
    }
};

bbop_mme_widgets.wipe = function(div){
    jQuery(div).empty();
};

bbop_mme_widgets.add_virtual_node = function(ecore, enode, aid, graph_div){

    var div_id = ecore.get_node_elt_id(enode.id());
    var style_str =
	'top: ' + enode.y_init() + 'px; ' +
	'left: ' + enode.x_init() + 'px;';
    var v = new bbop.html.tag('div',
    			      {'id': div_id,
    			       'class': 'waypoint',
    			       'style': style_str});
    jQuery(graph_div).append(v.to_string());
};

bbop_mme_widgets.add_enode = function(ecore, enode, aid, graph_div){

    var each = bbop.core.each;

    // Node as table nested into bbop.html div.
    var div_id = ecore.get_node_elt_id(enode.id());
    var style_str = 'top: ' + enode.y_init() + 'px; ' +
	'left: ' + enode.x_init() + 'px;';
    //ll('style: ' + style_str);
    var w = new bbop.html.tag('div',
			      {'id': div_id,
			       'class': 'demo-window',
			       'style': style_str});

    // Takes a core edit node as the argument, categorize the
    // contained types, order them.
    function _enode_to_stack(enode){

	// Attach a category to each type.
	var bin_stack = [];
	each(enode.types(),
	     function(in_type){
		 var bin = aid.categorize(in_type);
		 bin_stack.push({'category': bin, 'type': in_type});
	     });

	// Sort the types within the stack according to the known
	// type priorities.
	bin_stack = bin_stack.sort(
	    function(a, b){
		return aid.priority(b) - aid.priority(a);
	    });

	return bin_stack;
    }

    // Create a colorful label stack into an individual table.
    var enode_stack_table = new bbop.html.tag('table',
					      {'class':'bbop-mme-stack-table'});
    each(_enode_to_stack(enode),
	 function(item){
	     var trstr = '
'
		 + bme_type_to_text(item['type']) + '
';
	     enode_stack_table.add_to(trstr);
	 });
    w.add_to(enode_stack_table);

    // Box to drag new connections from.
    var konn = new bbop.html.tag('div', {'class': 'konn'});
    w.add_to(konn);

    // Box to drag new connections from.
    var opend = new bbop.html.tag('div', {'class': 'open-dialog'});
    w.add_to(opend);

    jQuery(graph_div).append(w.to_string());
};

bbop_mme_widgets.render_edge_modal = function(aid, modal_edge_title_elt,
					      modal_edge_body_elt,
					     source_id, target_id){
    var each = bbop.core.each;

    // Get a sorted list of known rels.
    var rels = aid.all_entities();
    rels = rels.sort(
	function(a,b){
	    return aid.priority(b) - aid.priority(a);
	});
    var rellist = [];
    each(rels,
	 function(rel){
	     rellist.push([rel, aid.readable(rel)]);
	 });

    // Assemble modal content.
    var mete = modal_edge_title_elt;
    var mebe = modal_edge_body_elt;
    jQuery(mete).empty();
    jQuery(mete).append('Add Edge');
    jQuery(mebe).empty();
    jQuery(mebe).append('
Relation selection
');
    jQuery(mebe).append('Edge source: ' +
			source_id);
    jQuery(mebe).append('
');
    jQuery(mebe).append('Edge target: ' +
			target_id);
    var tcache = [];
    each(rellist,
	 function(tmp_rel, rel_ind){
	     tcache.push('
');
	     tcache.push('<input type="radio" ');
	     tcache.push('name="rel_val" ');
	     tcache.push('value="' + tmp_rel[0] +'"');
	     if( rel_ind == 0 ){
		 tcache.push('checked>');
	     }else{
				       tcache.push('>');
	     }
	     tcache.push(tmp_rel[1] + ' ');
	     tcache.push('(' + tmp_rel[0] + ')');
	     tcache.push('
');

	 });

    // Put up modal shield.
    jQuery(modal_edge_body_elt).append(tcache.join(''));
};<commit_msg>better readability function<commit_after>////
//// Namespace for large drawing routines.
////

var bbop_mme_widgets = {};

// Add edit model contents to descriptive table.
bbop_mme_widgets.repaint_table = function(ecore, aid, table_div){

    var each = bbop.core.each;

    // First, lets get the headers that we'll need by poking the
    // model and getting all of the possible categories.
    var cat_list = [];
    each(ecore.get_nodes(),
	 function(enode_id, enode){
	     each(enode.types(),
		  function(in_type){
		      cat_list.push(aid.categorize(in_type));
		  });
	 });
    // Dedupe list.
    var tmph = bbop.core.hashify(cat_list);
    cat_list = bbop.core.get_keys(tmph);

    // If we actually got something, render the table. Otherwise,
    // a message.
    if( bbop.core.is_empty(cat_list) ){

	// Add to display.
	jQuery(table_div).empty();
	jQuery(table_div).append('

no instances

');

    }else{

	// Sort list according to known priorities.
	cat_list = cat_list.sort(
	    function(a, b){
		return aid.priority(b) - aid.priority(a);
	    });

	// Convert the ids into readable headers.
	var nav_tbl_headers = [];
	each(cat_list,
	     function(cat_id){
		 nav_tbl_headers.push(aid.readable(cat_id));
	     });

	//	var nav_tbl_headers = cat_list;
	//	    ['enabled by', 'activity', 'unknown', 'process', 'location'];

	var nav_tbl =
	    new bbop.html.table(nav_tbl_headers, [],
				{'class': ['table', 'table-bordered',
					   'table-hover',
					   'table-condensed'].join(' ')});

	//each(ecore.get_nodes(),
	each(ecore.edit_node_order(),
	     function(enode_id){
		 var enode = ecore.get_node(enode_id);
		 if( enode.existential() == 'real' ){

		     // Now that we have an enode, we want to mimic
		     // the order that we created for the header
		     // (cat_list). Start by binning the types.
		     var bin = {};
		     each(enode.types(),
			  function(in_type){
			      var cat = aid.categorize(in_type);
			      if( ! bin[cat] ){ bin[cat] = []; }
			      bin[cat].push(in_type);
			  });

		     // Now unfold the binned types into the table row
		     // according to the sorted order.
		     var table_row = [];
		     each(cat_list,
			  function(cat_id){
			      var accumulated_types = bin[cat_id];
			      var cell_cache = [];
			      each(accumulated_types,
				   function(atype){
				       var tt = bme_type_to_text(atype, aid);
				       cell_cache.push(tt);
				   });
			      table_row.push(cell_cache.join('
'));
			  });
		     nav_tbl.add_to(table_row);
		 }
	     });

	// Add to display.
	jQuery(table_div).empty();
	jQuery(table_div).append(nav_tbl.to_string());
    }
};

bbop_mme_widgets.wipe = function(div){
    jQuery(div).empty();
};

bbop_mme_widgets.add_virtual_node = function(ecore, enode, aid, graph_div){

    var div_id = ecore.get_node_elt_id(enode.id());
    var style_str =
	'top: ' + enode.y_init() + 'px; ' +
	'left: ' + enode.x_init() + 'px;';
    var v = new bbop.html.tag('div',
    			      {'id': div_id,
    			       'class': 'waypoint',
    			       'style': style_str});
    jQuery(graph_div).append(v.to_string());
};

bbop_mme_widgets.add_enode = function(ecore, enode, aid, graph_div){

    var each = bbop.core.each;

    // Node as table nested into bbop.html div.
    var div_id = ecore.get_node_elt_id(enode.id());
    var style_str = 'top: ' + enode.y_init() + 'px; ' +
	'left: ' + enode.x_init() + 'px;';
    //ll('style: ' + style_str);
    var w = new bbop.html.tag('div',
			      {'id': div_id,
			       'class': 'demo-window',
			       'style': style_str});

    // Takes a core edit node as the argument, categorize the
    // contained types, order them.
    function _enode_to_stack(enode){

	// Attach a category to each type.
	var bin_stack = [];
	each(enode.types(),
	     function(in_type){
		 var bin = aid.categorize(in_type);
		 bin_stack.push({'category': bin, 'type': in_type});
	     });

	// Sort the types within the stack according to the known
	// type priorities.
	bin_stack = bin_stack.sort(
	    function(a, b){
		return aid.priority(b) - aid.priority(a);
	    });

	return bin_stack;
    }

    // Create a colorful label stack into an individual table.
    var enode_stack_table = new bbop.html.tag('table',
					      {'class':'bbop-mme-stack-table'});
    each(_enode_to_stack(enode),
	 function(item){
	     var trstr = '
'
		 + bme_type_to_text(item['type'], aid) + '
';
	     enode_stack_table.add_to(trstr);
	 });
    w.add_to(enode_stack_table);

    // Box to drag new connections from.
    var konn = new bbop.html.tag('div', {'class': 'konn'});
    w.add_to(konn);

    // Box to drag new connections from.
    var opend = new bbop.html.tag('div', {'class': 'open-dialog'});
    w.add_to(opend);

    jQuery(graph_div).append(w.to_string());
};

bbop_mme_widgets.render_edge_modal = function(aid, modal_edge_title_elt,
					      modal_edge_body_elt,
					     source_id, target_id){
    var each = bbop.core.each;

    // Get a sorted list of known rels.
    var rels = aid.all_entities();
    rels = rels.sort(
	function(a,b){
	    return aid.priority(b) - aid.priority(a);
	});
    var rellist = [];
    each(rels,
	 function(rel){
	     rellist.push([rel, aid.readable(rel)]);
	 });

    // Assemble modal content.
    var mete = modal_edge_title_elt;
    var mebe = modal_edge_body_elt;
    jQuery(mete).empty();
    jQuery(mete).append('Add Edge');
    jQuery(mebe).empty();
    jQuery(mebe).append('
Relation selection
');
    jQuery(mebe).append('Edge source: ' +
			source_id);
    jQuery(mebe).append('
');
    jQuery(mebe).append('Edge target: ' +
			target_id);
    var tcache = [];
    each(rellist,
	 function(tmp_rel, rel_ind){
	     tcache.push('
');
	     tcache.push('<input type="radio" ');
	     tcache.push('name="rel_val" ');
	     tcache.push('value="' + tmp_rel[0] +'"');
	     if( rel_ind == 0 ){
		 tcache.push('checked>');
	     }else{
				       tcache.push('>');
	     }
	     tcache.push(tmp_rel[1] + ' ');
	     tcache.push('(' + tmp_rel[0] + ')');
	     tcache.push('
');

	 });

    // Put up modal shield.
    jQuery(modal_edge_body_elt).append(tcache.join(''));
};<|endoftext|>


<commit_before>(function () {
/**
 *
 * Copyright (C) 2009 SAKAI, Kazuaki <kaz.july.7 at gmail.com>
 * Licensed under the MIT Licenses.
 *
 */
var __version__ = 0.1;

if (typeof BlogpartsObject != 'undefined') {
  if (__version__ <= BlogpartsObject.version) return;
} else {
  var BlogpartsObject = window.BlogpartsObject = function() {};
}

/**
 * Original Source; jQuery(jquery.com)
 */
var ua = navigator.userAgent.toLowerCase();
BlogpartsObject.browser = {
	version: (ua.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
	safari: /webkit/.test( ua ),
	opera: /opera/.test( ua ),
	msie: /msie/.test( ua ) && !/opera/.test( ua ),
	mozilla: /mozilla/.test( ua ) && !/(compatible|webkit)/.test( ua )
};
(function(){
  this.version = __version__;
  this.prefix = 'bluestadium-';
  this.isReady = false;
  this.__ready__ = [];
  this.__widget__ = {};
  this.__elem__ = null;

  /************** Utilities *****************/
  this.escape = function(source,type) {
    if (typeof source != 'string') return source;
    switch (type) {
      case 'js' :
        return source.replace(/\\/g,'\\\\').replace(/"/g,'\\"')
          .replace(/'/g,"\\'").replace(/\//g,'\\/');
      case 'url' :
        return encodeURIComponent(source);
      case 'html' :
      default :
        return source.replace(
            /(?:&)(?!(?:[a-z0-9]{2,7}|#[0-9]{2,5}|#x[0-9a-f]{2,5});)/gi,
            '&'
          ).replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"')
          .replace(/'/g,''').replace(/`/,'&#96');
    }
  }

  this.each = function(args, callback, obj) {
    if (args.length == undefined || args.length == 0) return callback.call(obj,args);
    var result = [];
    for (var i in args) result.push(callback.call(obj,args[i]));
    return result;
  }

  // getElementById
  this.$ = function(elem) {
    return document.getElementById(elem);
  }

  // getElementsByClassName
  this.$c = function(name, node, tag) {
    if (node == null) node = document;
    if (node.getElementsByClassName) {
      return node.getElementsByClassName(name);
    } else {
      if (tag == null) tag = '*';
      var elems = node.getElementsByTagName(tag);
      var pattern = new RegExp('(?:^|\\s)' + name + '(?:\\s|$)');
      var found = [];
      BlogpartsObject.each(elems, function(elem) {
        if(typeof elem == 'object' && pattern.test(elem.className)) found.push(elem);
      });
      return found;
    }
  }

  /************** Core *****************/
  this.init = function() {
    if (!BlogpartsObject.isReady) {
      BlogpartsObject.isReady = true;
      BlogpartsObject.each(BlogpartsObject.__ready__,function(fn) {
        if (fn['fn'])
          fn['fn'].call(document,fn['args']);
        delete fn;
      });
      BlogpartsObject.__ready__ = null;
    }

    return this;
  }

  this.ready = function(callback,callback_args) {
    bindReady();
    if(BlogpartsObject.isReady)
      callback.call(document,callback_args);
    else
      BlogpartsObject.__ready__.push({fn : callback, args: callback_args});

    return this;
  }

  this.register = function(args) {
    if(!this.__widget__[args.name]) this.__widget__[args.name] = [];
    var count  = this.__widget__[args.name].length;
    if(args.singleton && count != 0) return this;
    args.id = BlogpartsObject.prefix + args.name + '-' + count;
    var widget = new BlogpartsObject.widget(args);
    this.__widget__[args.name].push(widget);
    return this;
  }

  this.load = function() {
    for(var label in BlogpartsObject.__widget__) {
      var targets = BlogpartsObject.$c(BlogpartsObject.prefix + label, document.body, 'div');
      if (targets == '') continue;
      for(var i = 0, length = BlogpartsObject.__widget__[label].length; i < length; i++) {
        var widget = BlogpartsObject.__widget__[label][i];
        var cloneTarget = targets[i].cloneNode(false);
        cloneTarget.innerHTML = widget.tag();
        targets[i].parentNode.replaceChild(cloneTarget, targets[i]);
      }
    }
    return this;
  }

}).apply(BlogpartsObject);

BlogpartsObject.event = {
  add : function(target, type, fn,  useCapture) {
    if (typeof target != 'object') return false;
    // this を イベント発生元に切り替える。
    // (IE でこれやらないと、event 時にイベント発生元の情報を上手くとれない)
    var __fn = function(event) { return fn.call(target,event); };
    if (target.addEventListener) {
      target.addEventListener(type,__fn,useCapture);
      return true;
    } else if (target.attachEvent) {
      return target.attachEvent('on' + type, __fn);
    } else {
      target['on' + type] = __fn;
    }
  },

  cancel : function (event) {
    if(event.preventDefault)
      event.preventDefault();
    else
      event.retunValue = false;
  },
  special: {
    ready : {
      setup : bindReady,
      teardown: function(){}
    }
  }
}

BlogpartsObject.widget = function(args) {
  for(var label in args)
    if (args.hasOwnProperty(label)) {
      if (label == 'vars') {
        switch (typeof args[label]) {
          case 'string':
            this[label] = BlogpartsObject.escape(args[label], 'html');
            break;
          case 'object':
            var vars = [];
            for (var i in args[label]) vars.push(i + '=' + args[label][i]);
            this[label] = BlogpartsObject.escape(vars.join('&'), 'html');
            break;
          case 'array':
            this[label] = BlogpartsObject.escape(args[label].join('&'));
            break;
          default:
            throw 'unsupported arguments';
        }
      } else {
        this[label] = BlogpartsObject.escape(args[label], 'html');
      }
    }
  return this;
}

BlogpartsObject.widget.prototype = {
  objectId : function() {
    return this.id + '-object';
  },
  embedId : function() {
    return this.id + '-embed';
  },
  tag : function() {
    return (this.type == 'flash') ? this.flash() : this.image();
  },
  image : function() {
    return [
      '<image src="' +  this.path + '"',
      'stype="width: ' + this.width + ';',
      'height: ' + this.height + ';"',
      '/>'
    ].join(' ');
  },
  flash : function() {
    var trans_obj = (this.transparent) ? '<param name="wmode" value="transparent" />' : '';
    var trans_emb = (this.transparent) ?  'wmode="transparent" ' : '';
    return [
      '<object id="' + this.objectId()  + '" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ',
      'name="' + this.objectId() + '" width="' + this.width +'" height="' + this.height + '" ',
      'codebase="<?php echo $proto ?>://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" >',
      '<param name="allowScriptAccess" value="' + this.scriptAccess + '" />'  + trans_obj,
      '<param name="movie" value="' + this.path + '" />',
      '<param name="play" value="true"/><param name="loop" value="' + this.loop + '"/>',
      '<param name="menu" value="false"/><param name="quality" value="high"/>',
      '<param name="FlashVars" value="' + this.vars + '"/>',
      '<embed id="' + this.embedId() + '" name="' + this.embedId() + '" ',
      'src="' + this.path + '" loop="' + this.loop + '" menu="false" quality="high" ',
      'swLiveConnect="true" width="' + this.width + '" height="' + this.height + '" ',
      'FlashVars="' + this.vars + '" ' + trans_emb,
      'allowScriptAccess="' + this.scriptAccess + '" type="application/x-shockwave-flash" ',
      'pluginspage="<?php echo $proto ?>://www.macromedia.com/go/getflashplayer" />',
      '</object>'
    ].join('');
  }
}

/**
 * Original Source: jQuery
 */
var readyBound = false;
function bindReady() {
  if (readyBound) return;
  readyBound = true;

  if (document.addEventListener && !BlogpartsObject.browser.opera)
    document.addEventListener('DOMContentLoaded', BlogpartsObject.init, false);

  if(BlogpartsObject.browser.msie && window == top){(function() {
    if (BlogpartsObject.isReady) return;
    try {
      document.documentElement.doScroll('left');
    } catch (e){
      setTimeout(arguments.callee, 0);
      return;
    }
    BlogpartsObject.init();
  })();}

  if (BlogpartsObject.browser.opera) {
    document.addEventListener('DOMContentLoaded', function() {
      if (BlogpartsObject.isReady) return;

      for (var i = 0; i < document.styleSheets.length; i++)
        if(document.styleSheets[i].disabled) {
          setTimeout(arguments.callee, 0);
          return;
        }

      BlogpartsObject.init()
    }, false);
  }

  if (BlogpartsObject.browser.safari) {
    var numStyles;
    (function() {
      if (BlogpartsObject.isReady) return;
      if ( document.readyState != "loaded" && document.readyState != "complete" ) {
        setTimeout( arguments.callee, 0);
        return;
      }
      if (numStyles === undefined) {
        numStyles = 0;
        elems = document.getElementsByTagName('link');
        for (var i = 0; i < elems.length; i++)
          if (elems[i].getAttribute('rel').toLowerCase() == 'stylesheet') numStyles++;
        if (document.styleSheets.length != numStyles) {
          setTimeout(arguments.callee, 0);
          return;
        }
        BlogpartsObject.init();
      }

    })();
  }

  // fail back
  BlogpartsObject.event.add(window, 'load', BlogpartsObject.init);
}

})();

/** widget **/
// BlogpartsObject.ready(function(args){
//   BlogpartsObject.register({
//     name: 'widget',
//     type: 'flash',
//     singleton: true,
//     width: '0px',
//     height: '0px',
//     scriptAccess: 'always',
//     transparent: true,
//     loop: false,
//     vars: '',
//     path: '/path/to/swf'
//   }).load();
// }, []);

/* vim: set ft=javascript: */
<commit_msg>まだ存在してはいけない文字列が残ってた<commit_after>(function () {
/**
 *
 * Copyright (C) 2009 SAKAI, Kazuaki <kaz.july.7 at gmail.com>
 * Licensed under the MIT Licenses.
 *
 */
var __version__ = 0.1;

if (typeof BlogpartsObject != 'undefined') {
  if (__version__ <= BlogpartsObject.version) return;
} else {
  var BlogpartsObject = window.BlogpartsObject = function() {};
}

/**
 * Original Source; jQuery(jquery.com)
 */
var ua = navigator.userAgent.toLowerCase();
BlogpartsObject.browser = {
	version: (ua.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
	safari: /webkit/.test( ua ),
	opera: /opera/.test( ua ),
	msie: /msie/.test( ua ) && !/opera/.test( ua ),
	mozilla: /mozilla/.test( ua ) && !/(compatible|webkit)/.test( ua )
};
(function(){
  this.version = __version__;
  this.prefix = 'blogparts-object-';
  this.isReady = false;
  this.__ready__ = [];
  this.__widget__ = {};
  this.__elem__ = null;

  /************** Utilities *****************/
  this.escape = function(source,type) {
    if (typeof source != 'string') return source;
    switch (type) {
      case 'js' :
        return source.replace(/\\/g,'\\\\').replace(/"/g,'\\"')
          .replace(/'/g,"\\'").replace(/\//g,'\\/');
      case 'url' :
        return encodeURIComponent(source);
      case 'html' :
      default :
        return source.replace(
            /(?:&)(?!(?:[a-z0-9]{2,7}|#[0-9]{2,5}|#x[0-9a-f]{2,5});)/gi,
            '&'
          ).replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"')
          .replace(/'/g,''').replace(/`/,'&#96');
    }
  }

  this.each = function(args, callback, obj) {
    if (args.length == undefined || args.length == 0) return callback.call(obj,args);
    var result = [];
    for (var i in args) result.push(callback.call(obj,args[i]));
    return result;
  }

  // getElementById
  this.$ = function(elem) {
    return document.getElementById(elem);
  }

  // getElementsByClassName
  this.$c = function(name, node, tag) {
    if (node == null) node = document;
    if (node.getElementsByClassName) {
      return node.getElementsByClassName(name);
    } else {
      if (tag == null) tag = '*';
      var elems = node.getElementsByTagName(tag);
      var pattern = new RegExp('(?:^|\\s)' + name + '(?:\\s|$)');
      var found = [];
      BlogpartsObject.each(elems, function(elem) {
        if(typeof elem == 'object' && pattern.test(elem.className)) found.push(elem);
      });
      return found;
    }
  }

  /************** Core *****************/
  this.init = function() {
    if (!BlogpartsObject.isReady) {
      BlogpartsObject.isReady = true;
      BlogpartsObject.each(BlogpartsObject.__ready__,function(fn) {
        if (fn['fn'])
          fn['fn'].call(document,fn['args']);
        delete fn;
      });
      BlogpartsObject.__ready__ = null;
    }

    return this;
  }

  this.ready = function(callback,callback_args) {
    bindReady();
    if(BlogpartsObject.isReady)
      callback.call(document,callback_args);
    else
      BlogpartsObject.__ready__.push({fn : callback, args: callback_args});

    return this;
  }

  this.register = function(args) {
    if(!this.__widget__[args.name]) this.__widget__[args.name] = [];
    var count  = this.__widget__[args.name].length;
    if(args.singleton && count != 0) return this;
    args.id = BlogpartsObject.prefix + args.name + '-' + count;
    var widget = new BlogpartsObject.widget(args);
    this.__widget__[args.name].push(widget);
    return this;
  }

  this.load = function() {
    for(var label in BlogpartsObject.__widget__) {
      var targets = BlogpartsObject.$c(BlogpartsObject.prefix + label, document.body, 'div');
      if (targets == '') continue;
      for(var i = 0, length = BlogpartsObject.__widget__[label].length; i < length; i++) {
        var widget = BlogpartsObject.__widget__[label][i];
        var cloneTarget = targets[i].cloneNode(false);
        cloneTarget.innerHTML = widget.tag();
        targets[i].parentNode.replaceChild(cloneTarget, targets[i]);
      }
    }
    return this;
  }

}).apply(BlogpartsObject);

BlogpartsObject.event = {
  add : function(target, type, fn,  useCapture) {
    if (typeof target != 'object') return false;
    // this を イベント発生元に切り替える。
    // (IE でこれやらないと、event 時にイベント発生元の情報を上手くとれない)
    var __fn = function(event) { return fn.call(target,event); };
    if (target.addEventListener) {
      target.addEventListener(type,__fn,useCapture);
      return true;
    } else if (target.attachEvent) {
      return target.attachEvent('on' + type, __fn);
    } else {
      target['on' + type] = __fn;
    }
  },

  cancel : function (event) {
    if(event.preventDefault)
      event.preventDefault();
    else
      event.retunValue = false;
  },
  special: {
    ready : {
      setup : bindReady,
      teardown: function(){}
    }
  }
}

BlogpartsObject.widget = function(args) {
  for(var label in args)
    if (args.hasOwnProperty(label)) {
      if (label == 'vars') {
        switch (typeof args[label]) {
          case 'string':
            this[label] = BlogpartsObject.escape(args[label], 'html');
            break;
          case 'object':
            var vars = [];
            for (var i in args[label]) vars.push(i + '=' + args[label][i]);
            this[label] = BlogpartsObject.escape(vars.join('&'), 'html');
            break;
          case 'array':
            this[label] = BlogpartsObject.escape(args[label].join('&'));
            break;
          default:
            throw 'unsupported arguments';
        }
      } else {
        this[label] = BlogpartsObject.escape(args[label], 'html');
      }
    }
  return this;
}

BlogpartsObject.widget.prototype = {
  objectId : function() {
    return this.id + '-object';
  },
  embedId : function() {
    return this.id + '-embed';
  },
  tag : function() {
    return (this.type == 'flash') ? this.flash() : this.image();
  },
  image : function() {
    return [
      '<image src="' +  this.path + '"',
      'stype="width: ' + this.width + ';',
      'height: ' + this.height + ';"',
      '/>'
    ].join(' ');
  },
  flash : function() {
    var trans_obj = (this.transparent) ? '<param name="wmode" value="transparent" />' : '';
    var trans_emb = (this.transparent) ?  'wmode="transparent" ' : '';
    return [
      '<object id="' + this.objectId()  + '" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ',
      'name="' + this.objectId() + '" width="' + this.width +'" height="' + this.height + '" ',
      'codebase="<?php echo $proto ?>://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" >',
      '<param name="allowScriptAccess" value="' + this.scriptAccess + '" />'  + trans_obj,
      '<param name="movie" value="' + this.path + '" />',
      '<param name="play" value="true"/><param name="loop" value="' + this.loop + '"/>',
      '<param name="menu" value="false"/><param name="quality" value="high"/>',
      '<param name="FlashVars" value="' + this.vars + '"/>',
      '<embed id="' + this.embedId() + '" name="' + this.embedId() + '" ',
      'src="' + this.path + '" loop="' + this.loop + '" menu="false" quality="high" ',
      'swLiveConnect="true" width="' + this.width + '" height="' + this.height + '" ',
      'FlashVars="' + this.vars + '" ' + trans_emb,
      'allowScriptAccess="' + this.scriptAccess + '" type="application/x-shockwave-flash" ',
      'pluginspage="<?php echo $proto ?>://www.macromedia.com/go/getflashplayer" />',
      '</object>'
    ].join('');
  }
}

/**
 * Original Source: jQuery
 */
var readyBound = false;
function bindReady() {
  if (readyBound) return;
  readyBound = true;

  if (document.addEventListener && !BlogpartsObject.browser.opera)
    document.addEventListener('DOMContentLoaded', BlogpartsObject.init, false);

  if(BlogpartsObject.browser.msie && window == top){(function() {
    if (BlogpartsObject.isReady) return;
    try {
      document.documentElement.doScroll('left');
    } catch (e){
      setTimeout(arguments.callee, 0);
      return;
    }
    BlogpartsObject.init();
  })();}

  if (BlogpartsObject.browser.opera) {
    document.addEventListener('DOMContentLoaded', function() {
      if (BlogpartsObject.isReady) return;

      for (var i = 0; i < document.styleSheets.length; i++)
        if(document.styleSheets[i].disabled) {
          setTimeout(arguments.callee, 0);
          return;
        }

      BlogpartsObject.init()
    }, false);
  }

  if (BlogpartsObject.browser.safari) {
    var numStyles;
    (function() {
      if (BlogpartsObject.isReady) return;
      if ( document.readyState != "loaded" && document.readyState != "complete" ) {
        setTimeout( arguments.callee, 0);
        return;
      }
      if (numStyles === undefined) {
        numStyles = 0;
        elems = document.getElementsByTagName('link');
        for (var i = 0; i < elems.length; i++)
          if (elems[i].getAttribute('rel').toLowerCase() == 'stylesheet') numStyles++;
        if (document.styleSheets.length != numStyles) {
          setTimeout(arguments.callee, 0);
          return;
        }
        BlogpartsObject.init();
      }

    })();
  }

  // fail back
  BlogpartsObject.event.add(window, 'load', BlogpartsObject.init);
}

})();

/** widget **/
// BlogpartsObject.ready(function(args){
//   BlogpartsObject.register({
//     name: 'widget',
//     type: 'flash',
//     singleton: true,
//     width: '0px',
//     height: '0px',
//     scriptAccess: 'always',
//     transparent: true,
//     loop: false,
//     vars: '',
//     path: '/path/to/swf'
//   }).load();
// }, []);

/* vim: set ft=javascript: */
<|endoftext|>


<commit_before>//Module is a Singelton
define(function(require) {

  var $ = require('jquery');
  var _ = require('underscore');
  var Backbone = require('backbone');
  var Store = require('localstorage');
  var Doc = require('models/doc');
  var settings = require('models/settings');

  var remoteStorageDocuments = require('remotestorage-documents');

  remoteStorage.claimAccess('documents', 'rw');

  var Docs = Backbone.Collection.extend({

    model: Doc,

    localStorage: remoteStorageDocuments.getBackboneStore('notes'),

    initialize: function(models) {

      this.fetch({
        success: _.bind(function() {
          if (this.isEmpty()) {
            this.addNew();
          }
        }, this)
      });

      this
        .on('reset', this.updateColor)
        .on('change:content', this.sort)
        .on('change:lastEdited', this.saveWhenIdle)
        .on('change:title', this.updateUrl);
    },

    addNew: function() {
      this.create({
        id: _.uniqueId(),
        lastEdited: new Date().getTime()
      });
    },

    // Sort by 'lastEdited'
    comparator: function(first, second) {
      return first.get('lastEdited') > second.get('lastEdited') ? -1 : 1 ;
    },

    updateColor: _.throttle(function() {
      this.each(function(doc) {
        doc.calculateColor();
      });
    }, 3000),

    saveTimeout: undefined,
    saveWhenIdle: function(doc) {
      clearTimeout(this.saveTimeout);
      this.saveTimeout = setTimeout(_.bind(doc.save, doc), 300);
    },

    updateUrl: function(doc) {
      var url = encodeURI(doc.get('title').toLowerCase().replace(/\s| /g, '-'));
      if (url.length < 1) {
        doc.set('url', '');
        return;
      }
      var len = this.filter(function(doc) {
	return new RegExp('^' + url + '(-[0-9]|$)').test(doc.get('url'));
      }).length;
      url = len < 1 ? url : url + '-' + len;
      doc.set('url', url);
    },

    deleteEmpty: function() {
      var previousDoc = this.get(settings.previous('openDocId'));
      if (previousDoc && previousDoc.isEmpty()) {
        this.remove(previousDoc);
      }
    }

  });

  var docs = new Docs();

  var hasConnected = false;

  remoteStorage.onWidget('state', function(state) {
    if ((! hasConnected) && state == 'connected') {
      hasConnected = true;

      docs.reset(docs.localStorage.findAll());
    } else if ( state == 'disconnected') {
      docs.reset().addNew();
    }
  });

  return docs;

});
<commit_msg>fixed #51<commit_after>//Module is a Singelton
define(function(require) {

  var $ = require('jquery');
  var _ = require('underscore');
  var Backbone = require('backbone');
  var Store = require('localstorage');
  var Doc = require('models/doc');
  var settings = require('models/settings');

  var remoteStorageDocuments = require('remotestorage-documents');

  remoteStorage.claimAccess('documents', 'rw');

  var Docs = Backbone.Collection.extend({

    model: Doc,

    localStorage: remoteStorageDocuments.getBackboneStore('notes'),

    initialize: function(models) {

      this.fetch({
        success: _.bind(function() {
          if (this.isEmpty()) {
            this.addNew();
          }
        }, this)
      });

      this
        .on('reset', this.updateColor)
        .on('change:content', this.sort)
        .on('change:lastEdited', this.saveWhenIdle)
        .on('change:title', this.updateUrl);
    },

    addNew: function() {
      this.create({
        id: _.uniqueId(),
        lastEdited: new Date().getTime()
      });
    },

    // Sort by 'lastEdited'
    comparator: function(first, second) {
      return first.get('lastEdited') > second.get('lastEdited') ? -1 : 1 ;
    },

    updateColor: _.throttle(function() {
      this.each(function(doc) {
        doc.calculateColor();
      });
    }, 3000),

    saveTimeout: undefined,
    saveWhenIdle: function(doc) {
      clearTimeout(this.saveTimeout);
      this.saveTimeout = setTimeout(_.bind(doc.save, doc), 300);
    },

    updateUrl: function(doc) {
      var url = encodeURI(doc.get('title').toLowerCase().replace(/\s| /g, '-'));
      if (url.length < 1) {
        doc.set('url', '');
        return;
      }
      var len = this.filter(function(doc) {
	return new RegExp('^' + url + '(-[0-9]|$)').test(doc.get('url'));
      }).length;
      url = len < 1 ? url : url + '-' + len;
      doc.set('url', url);
    },

    deleteEmpty: function() {
      var previousDoc = this.get(settings.previous('openDocId'));
      if (previousDoc && previousDoc.isEmpty()) {
        log('here');
        previousDoc.destroy();
      }
    }

  });

  var docs = new Docs();

  var hasConnected = false;

  remoteStorage.onWidget('state', function(state) {
    if ((! hasConnected) && state == 'connected') {
      hasConnected = true;

      docs.reset(docs.localStorage.findAll());
    } else if ( state == 'disconnected') {
      docs.reset().addNew();
    }
  });

  return docs;

});
<|endoftext|>


<commit_before>import React               from 'react';
import { Route, NavLink }  from 'react-router-dom';
import PropTypes           from 'prop-types';

import Client              from '../_common/base/client_base';
import { redirectToLogin } from '../_common/base/login';
import { localize }        from '../_common/localize';

import Portfolio           from './pages/portfolio/portfolio.jsx';
import TradeApp            from './pages/trading/trade_app.jsx';
import Settings            from './pages/settings/settings.jsx';
import Statement           from './pages/statement/statement.jsx';

// Settings Routes
import AccountPassword        from './pages/settings/sections/account_password.jsx';
import ApiToken               from './pages/settings/sections/api_token.jsx';
import AuthorizedApplications from './pages/settings/sections/authorized_applications.jsx';
import CashierPassword        from './pages/settings/sections/cashier_password.jsx';
import FinancialAssessment    from './pages/settings/sections/financial_assessment.jsx';
import Limits                 from './pages/settings/sections/limits.jsx';
import LoginHistory           from './pages/settings/sections/login_history.jsx';
import PersonalDetails        from './pages/settings/sections/personal_details.jsx';
import SelfExclusion          from './pages/settings/sections/self_exclusion.jsx';

const routes = [
    { path: '/',          component: TradeApp, exact: true },
    { path: '/portfolio', component: Portfolio, is_authenticated: true },
    { path: '/statement', component: Statement, is_authenticated: true },
    {
        path     : '/settings',
        component: Settings,
        is_authenticated: true,
        routes   : [
            { path: 'personal',         component: PersonalDetails },
            { path: 'financial',        component: FinancialAssessment },
            { path: 'account_password', component: AccountPassword },
            { path: 'cashier_password', component: CashierPassword },
            { path: 'exclusion',        component: SelfExclusion },
            { path: 'limits',           component: Limits },
            { path: 'history',          component: LoginHistory },
            { path: 'token',            component: ApiToken },
            { path: 'apps',             component: AuthorizedApplications },
        ],
    },
];

const RouteWithSubRoutes = route => (
    <Route
        exact={route.exact}
        path={route.path}
        render={props => (
            (route.is_authenticated && !Client.isLoggedIn()) ? // TODO: update styling of the message below
                {localize('Please login to view this page.')} :
                <route.component {...props} routes={route.routes} />
        )}
    />
);

export const BinaryRoutes = () => routes.map((route, idx) => (
    <RouteWithSubRoutes key={idx} {...route} />
));

const normalizePath = (path) => /^\//.test(path) ? path : `/${path || ''}`; // Default to '/'

const getRouteInfo = (path) => routes.find(r => r.path === normalizePath(path));

export const isRouteVisible = (path, route = getRouteInfo(path)) =>
    !(route && route.is_authenticated && !Client.isLoggedIn());

export const BinaryLink = ({ to, children, ...props }) => {
    const path  = normalizePath(to);
    const route = getRouteInfo(path);

    if (!route) {
        throw new Error(`Route not found: ${to}`);
    }

    return (
        to ?
            <NavLink to={path} activeClassName='active' exact={route.exact} {...props}>
                {children}
            </NavLink>
        :

                {children}

    );
};

BinaryLink.propTypes = {
    children: PropTypes.object,
    to      : PropTypes.string,
};
<commit_msg>fix eslint<commit_after>import React               from 'react';
import { Route, NavLink }  from 'react-router-dom';
import PropTypes           from 'prop-types';

import Client              from '../_common/base/client_base';
import { redirectToLogin } from '../_common/base/login';
import { localize }        from '../_common/localize';

import Portfolio           from './pages/portfolio/portfolio.jsx';
import TradeApp            from './pages/trading/trade_app.jsx';
import Settings            from './pages/settings/settings.jsx';
import Statement           from './pages/statement/statement.jsx';

// Settings Routes
import AccountPassword        from './pages/settings/sections/account_password.jsx';
import ApiToken               from './pages/settings/sections/api_token.jsx';
import AuthorizedApplications from './pages/settings/sections/authorized_applications.jsx';
import CashierPassword        from './pages/settings/sections/cashier_password.jsx';
import FinancialAssessment    from './pages/settings/sections/financial_assessment.jsx';
import Limits                 from './pages/settings/sections/limits.jsx';
import LoginHistory           from './pages/settings/sections/login_history.jsx';
import PersonalDetails        from './pages/settings/sections/personal_details.jsx';
import SelfExclusion          from './pages/settings/sections/self_exclusion.jsx';

const routes = [
    { path: '/',          component: TradeApp, exact: true },
    { path: '/portfolio', component: Portfolio, is_authenticated: true },
    { path: '/statement', component: Statement, is_authenticated: true },
    {
        path            : '/settings',
        component       : Settings,
        is_authenticated: true,
        routes          : [
            { path: 'personal',         component: PersonalDetails },
            { path: 'financial',        component: FinancialAssessment },
            { path: 'account_password', component: AccountPassword },
            { path: 'cashier_password', component: CashierPassword },
            { path: 'exclusion',        component: SelfExclusion },
            { path: 'limits',           component: Limits },
            { path: 'history',          component: LoginHistory },
            { path: 'token',            component: ApiToken },
            { path: 'apps',             component: AuthorizedApplications },
        ],
    },
];

const RouteWithSubRoutes = route => (
    <Route
        exact={route.exact}
        path={route.path}
        render={props => (
            (route.is_authenticated && !Client.isLoggedIn()) ? // TODO: update styling of the message below
                {localize('Please login to view this page.')} :
                <route.component {...props} routes={route.routes} />
        )}
    />
);

export const BinaryRoutes = () => routes.map((route, idx) => (
    <RouteWithSubRoutes key={idx} {...route} />
));

const normalizePath = (path) => /^\//.test(path) ? path : `/${path || ''}`; // Default to '/'

const getRouteInfo = (path) => routes.find(r => r.path === normalizePath(path));

export const isRouteVisible = (path, route = getRouteInfo(path)) =>
    !(route && route.is_authenticated && !Client.isLoggedIn());

export const BinaryLink = ({ to, children, ...props }) => {
    const path  = normalizePath(to);
    const route = getRouteInfo(path);

    if (!route) {
        throw new Error(`Route not found: ${to}`);
    }

    return (
        to ?
            <NavLink to={path} activeClassName='active' exact={route.exact} {...props}>
                {children}
            </NavLink>
        :

                {children}

    );
};

BinaryLink.propTypes = {
    children: PropTypes.object,
    to      : PropTypes.string,
};
<|endoftext|>