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

/**
 * User-interface component for picking a number value from a range.
 * This is essentially a value with integrated up/down spinners.
 * But PhET has been calling it a 'picker', so that's what this type is named.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var ButtonListener = require( 'SCENERY/input/ButtonListener' );
  var Color = require( 'SCENERY/util/Color' );
  var DerivedProperty = require( 'AXON/DerivedProperty' );
  var Dimension2 = require( 'DOT/Dimension2' );
  var FireOnHoldInputListener = require( 'SCENERY_PHET/buttons/FireOnHoldInputListener' );
  var inherit = require( 'PHET_CORE/inherit' );
  var LinearGradient = require( 'SCENERY/util/LinearGradient' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var Property = require( 'AXON/Property' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Shape = require( 'KITE/Shape' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Util = require( 'DOT/Util' );
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );

  /**
   * @param {Property.<number>} valueProperty
   * @param {Property.<Range>} rangeProperty
   * @param {Object} [options]
   * @constructor
   */
  function NumberPicker( valueProperty, rangeProperty, options ) {

    options = _.extend( {
      cursor: 'pointer',
      color: new Color( 0, 0, 255 ), // {Color|string} color of arrows, and top/bottom gradient on pointer over
      backgroundColor: 'white', // {Color|string} color of the background when pointer is not over it
      cornerRadius: 6,
      xMargin: 3,
      yMargin: 3,
      decimalPlaces: 0,
      font: new PhetFont( 24 ),
      upFunction: function() { return valueProperty.get() + 1; },
      downFunction: function() { return valueProperty.get() - 1; },
      timerDelay: 400, // start to fire continuously after pressing for this long (milliseconds)
      timerInterval: 100, // fire continuously at this frequency (milliseconds),
      noValueString: '-', // string to display if valueProperty.get is null or undefined
      align: 'center', // horizontal alignment of the value, 'center'|'right'|'left'
      touchAreaExpandX: 10,
      touchAreaExpandY: 10,
      mouseAreaExpandX: 0,
      mouseAreaExpandY: 5,
      backgroundStroke: 'gray',
      backgroundLineWidth: 0.5,
      arrowHeight: 6,
      arrowYSpacing: 3
    }, options );
    // {Color|string} color of arrows and top/bottom gradient when pressed
    options.pressedColor = options.pressedColor || Color.toColor( options.color ).darkerColor();

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

    //------------------------------------------------------------
    // Properties

    this.valueProperty = valueProperty; // @private must be unlinked in dispose

    var upStateProperty = new Property( 'up' ); // up|down|over|out
    var downStateProperty = new Property( 'up' ); // up|down|over|out

    // @private must be detached in dispose
    this.upEnabledProperty = new DerivedProperty( [ valueProperty, rangeProperty ], function( value, range ) {
      return ( value !== null && value !== undefined && value < range.max );
    } );

    // @private must be detached in dispose
    this.downEnabledProperty = new DerivedProperty( [ valueProperty, rangeProperty ], function( value, range ) {
      return ( value !== null && value !== undefined && value > range.min );
    } );

    //------------------------------------------------------------
    // Nodes

    // displays the value
    var valueNode = new Text( '', { font: options.font, pickable: false } );

    // compute max width of text based on value range
    valueNode.text = Util.toFixed( rangeProperty.get().min, options.decimalPlaces );
    var maxWidth = valueNode.width;
    valueNode.text = Util.toFixed( rangeProperty.get().max, options.decimalPlaces );
    maxWidth = Math.max( maxWidth, valueNode.width );

    // compute shape of the background behind the numeric value
    var backgroundWidth = maxWidth + ( 2 * options.xMargin );
    var backgroundHeight = valueNode.height + ( 2 * options.yMargin );
    var backgroundOverlap = 1;
    var backgroundCornerRadius = options.cornerRadius;

    // top half of the background, for 'up'. Shape computed starting at upper-left, going clockwise.
    var upBackground = new Path( new Shape()
      .arc( backgroundCornerRadius, backgroundCornerRadius, backgroundCornerRadius, Math.PI, Math.PI * 3 / 2, false )
      .arc( backgroundWidth - backgroundCornerRadius, backgroundCornerRadius, backgroundCornerRadius, -Math.PI / 2, 0, false )
      .lineTo( backgroundWidth, ( backgroundHeight / 2 ) + backgroundOverlap )
      .lineTo( 0, ( backgroundHeight / 2 ) + backgroundOverlap )
      .close(), { pickable: false } );

    // bottom half of the background, for 'down'. Shape computed starting at bottom-right, going clockwise.
    var downBackground = new Path( new Shape()
      .arc( backgroundWidth - backgroundCornerRadius, backgroundHeight - backgroundCornerRadius, backgroundCornerRadius, 0, Math.PI / 2, false )
      .arc( backgroundCornerRadius, backgroundHeight - backgroundCornerRadius, backgroundCornerRadius, Math.PI / 2, Math.PI, false )
      .lineTo( 0, backgroundHeight / 2 )
      .lineTo( backgroundWidth, backgroundHeight / 2 )
      .close(), { pickable: false } );

    // separate rectangle for stroke around value background
    var strokedBackground = new Rectangle( 0, 0, backgroundWidth, backgroundHeight, backgroundCornerRadius, backgroundCornerRadius, {
      pickable: false,
      stroke: options.backgroundStroke,
      lineWidth: options.backgroundLineWidth
    } );

    // compute size of arrows
    var arrowButtonSize = new Dimension2( 0.5 * backgroundWidth, options.arrowHeight );

    // 'up' arrow
    var arrowOptions = { stroke: 'black', lineWidth: 0.25, pickable: false };
    var upArrowShape = new Shape()
      .moveTo( arrowButtonSize.width / 2, 0 )
      .lineTo( arrowButtonSize.width, arrowButtonSize.height )
      .lineTo( 0, arrowButtonSize.height )
      .close();
    this.upArrow = new Path( upArrowShape, arrowOptions ); // @private
    this.upArrow.centerX = upBackground.centerX;
    this.upArrow.bottom = upBackground.top - options.arrowYSpacing;

    // 'down' arrow
    var downArrowShape = new Shape()
      .moveTo( arrowButtonSize.width / 2, arrowButtonSize.height )
      .lineTo( 0, 0 )
      .lineTo( arrowButtonSize.width, 0 )
      .close();
    this.downArrow = new Path( downArrowShape, arrowOptions ); // @private
    this.downArrow.centerX = downBackground.centerX;
    this.downArrow.top = downBackground.bottom + options.arrowYSpacing;

    // parents for 'up' and 'down' components
    var upParent = new Node( { children: [ upBackground, this.upArrow ] } );
    upParent.addChild( new Rectangle( upParent.localBounds ) ); // invisible overlay
    var downParent = new Node( { children: [ downBackground, this.downArrow ] } );
    downParent.addChild( new Rectangle( downParent.localBounds ) ); // invisible overlay

    // rendering order
    this.addChild( upParent );
    this.addChild( downParent );
    this.addChild( strokedBackground );
    this.addChild( valueNode );

    //------------------------------------------------------------
    // Pointer areas

    // touch area
    upParent.touchArea = Shape.rectangle(
      upParent.left - ( options.touchAreaExpandX / 2 ), upParent.top - options.touchAreaExpandY,
      upParent.width + options.touchAreaExpandX, upParent.height + options.touchAreaExpandY );
    downParent.touchArea = Shape.rectangle(
      downParent.left - ( options.touchAreaExpandX / 2 ), downParent.top,
      downParent.width + options.touchAreaExpandX, downParent.height + options.touchAreaExpandY );

    // mouse area
    upParent.mouseArea = Shape.rectangle(
      upParent.left - ( options.mouseAreaExpandX / 2 ), upParent.top - options.mouseAreaExpandY,
      upParent.width + options.mouseAreaExpandX, upParent.height + options.mouseAreaExpandY );
    downParent.mouseArea = Shape.rectangle(
      downParent.left - ( options.mouseAreaExpandX / 2 ), downParent.top,
      downParent.width + options.mouseAreaExpandX, downParent.height + options.mouseAreaExpandY );

    //------------------------------------------------------------
    // Colors

    // arrow colors
    var arrowColors = {
      up: options.color,
      over: options.color,
      down: options.pressedColor,
      out: options.color,
      disabled: 'rgb(176,176,176)'
    };

    // background colors
    var highlightGradient = createVerticalGradient( options.color, options.backgroundColor, options.color, backgroundHeight );
    var pressedGradient = createVerticalGradient( options.pressedColor, options.backgroundColor, options.pressedColor, backgroundHeight );
    var backgroundColors = {
      up: options.backgroundColor,
      over: highlightGradient,
      down: pressedGradient,
      out: pressedGradient,
      disabled: options.backgroundColor
    };

    //------------------------------------------------------------
    // Observers and InputListeners

    // up
    upParent.addInputListener( new ButtonStateListener( upStateProperty ) );
    var upListener = new FireOnHoldInputListener( {
      listener: function() {
        valueProperty.set( Math.min( options.upFunction(), rangeProperty.get().max ) );
      },
      timerDelay: options.timerDelay,
      timerInterval: options.timerInterval
    } );
    upParent.addInputListener( upListener );

    // down
    downParent.addInputListener( new ButtonStateListener( downStateProperty ) );
    var downListener = new FireOnHoldInputListener( {
      listener: function() {
        valueProperty.set( Math.max( options.downFunction(), rangeProperty.get().min ) );
      },
      timerDelay: options.timerDelay,
      timerInterval: options.timerInterval
    } );
    downParent.addInputListener( downListener );

    // enable/disable listeners: unlink unnecessary, properties are owned by this instance
    this.upEnabledProperty.link( function( enabled ) { upListener.enabled = enabled; } );
    this.downEnabledProperty.link( function( enabled ) { downListener.enabled = enabled; } );

    // @private Update text to match the value
    this.valueObserver = function( value ) {
      if ( value === null || value === undefined ) {
        valueNode.text = options.noValueString;
        valueNode.x = ( backgroundWidth - valueNode.width ) / 2; // horizontally centered
      }
      else {
        valueNode.text = Util.toFixed( value, options.decimalPlaces );
        if ( options.align === 'center' ) {
          valueNode.centerX = upBackground.centerX;
        }
        else if ( options.align === 'right' ) {
          valueNode.right = upBackground.right - options.xMargin;
        }
        else if ( options.align === 'left' ) {
          valueNode.left = upBackground.left + options.xMargin;
        }
        else {
          throw new Error( 'unsupported value for options.align: ' + options.align );
        }
      }
      valueNode.centerY = backgroundHeight / 2;
    };
    this.valueProperty.link( this.valueObserver ); // must be unlinked in dispose

    // @private update colors for 'up' components
    Property.multilink( [ upStateProperty, this.upEnabledProperty ], function( state, enabled ) {
      updateColors( state, enabled, upBackground, thisNode.upArrow, backgroundColors, arrowColors );
    } );

    // @private update colors for 'down' components
    Property.multilink( [ downStateProperty, this.downEnabledProperty ], function( state, enabled ) {
      updateColors( state, enabled, downBackground, thisNode.downArrow, backgroundColors, arrowColors );
    } );

    this.mutate( options );
  }

  sceneryPhet.register( 'NumberPicker', NumberPicker );

  /**
   * Converts ButtonListener events to state changes.
   *
   * @param {Property.<string>} stateProperty up|down|over|out
   * @param {Object} [options]
   * @constructor
   */
  function ButtonStateListener( stateProperty ) {
    ButtonListener.call( this, {
      up: function() { stateProperty.set( 'up' ); },
      over: function() { stateProperty.set( 'over' ); },
      down: function() { stateProperty.set( 'down' ); },
      out: function() { stateProperty.set( 'out' ); }
    } );
  }

  sceneryPhet.register( 'NumberPicker.ButtonStateListener', ButtonStateListener );

  inherit( ButtonListener, ButtonStateListener );

  // creates a vertical gradient
  var createVerticalGradient = function( topColor, centerColor, bottomColor, height ) {
    return new LinearGradient( 0, 0, 0, height )
      .addColorStop( 0, topColor )
      .addColorStop( 0.5, centerColor )
      .addColorStop( 1, bottomColor );
  };

  // Update arrow and background colors
  var updateColors = function( state, enabled, background, arrow, backgroundColors, arrowColors ) {
    if ( enabled ) {
      arrow.stroke = 'black';
      if ( state === 'up' ) {
        background.fill = backgroundColors.up;
        arrow.fill = arrowColors.up;
      }
      else if ( state === 'over' ) {
        background.fill = backgroundColors.over;
        arrow.fill = arrowColors.over;
      }
      else if ( state === 'down' ) {
        background.fill = backgroundColors.down;
        arrow.fill = arrowColors.down;
      }
      else if ( state === 'out' ) {
        background.fill = backgroundColors.out;
        arrow.fill = arrowColors.out;
      }
      else {
        throw new Error( 'unsupported state: ' + state );
      }
    }
    else {
      background.fill = backgroundColors.disabled;
      arrow.fill = arrowColors.disabled;
      arrow.stroke = arrowColors.disabled; // stroke so that arrow size will look the same when it's enabled/disabled
    }
  };

  return inherit( Node, NumberPicker, {

    // @public Ensures that this node is eligible for GC.
    dispose: function() {

      this.upEnabledProperty.unlinkAll(); // Property is owned by this instance
      this.upEnabledProperty.dispose();

      this.downEnabledProperty.unlinkAll(); // Property is owned by this instance
      this.downEnabledProperty.dispose();

      this.valueProperty.unlink( this.valueObserver );
    },

    // @public
    setArrowsVisible: function( visible ) {
      this.upArrow.visible = this.downArrow.visible = visible;
    }
  } );
} );
<commit_msg>NumberPicker: disabled arrow buttons when they are made invisible, #212<commit_after>// Copyright 2014-2015, University of Colorado Boulder

/**
 * User-interface component for picking a number value from a range.
 * This is essentially a value with integrated up/down spinners.
 * But PhET has been calling it a 'picker', so that's what this type is named.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var ButtonListener = require( 'SCENERY/input/ButtonListener' );
  var Color = require( 'SCENERY/util/Color' );
  var DerivedProperty = require( 'AXON/DerivedProperty' );
  var Dimension2 = require( 'DOT/Dimension2' );
  var FireOnHoldInputListener = require( 'SCENERY_PHET/buttons/FireOnHoldInputListener' );
  var inherit = require( 'PHET_CORE/inherit' );
  var LinearGradient = require( 'SCENERY/util/LinearGradient' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var Property = require( 'AXON/Property' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Shape = require( 'KITE/Shape' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Util = require( 'DOT/Util' );
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );

  /**
   * @param {Property.<number>} valueProperty
   * @param {Property.<Range>} rangeProperty
   * @param {Object} [options]
   * @constructor
   */
  function NumberPicker( valueProperty, rangeProperty, options ) {

    options = _.extend( {
      cursor: 'pointer',
      color: new Color( 0, 0, 255 ), // {Color|string} color of arrows, and top/bottom gradient on pointer over
      backgroundColor: 'white', // {Color|string} color of the background when pointer is not over it
      cornerRadius: 6,
      xMargin: 3,
      yMargin: 3,
      decimalPlaces: 0,
      font: new PhetFont( 24 ),
      upFunction: function() { return valueProperty.get() + 1; },
      downFunction: function() { return valueProperty.get() - 1; },
      timerDelay: 400, // start to fire continuously after pressing for this long (milliseconds)
      timerInterval: 100, // fire continuously at this frequency (milliseconds),
      noValueString: '-', // string to display if valueProperty.get is null or undefined
      align: 'center', // horizontal alignment of the value, 'center'|'right'|'left'
      touchAreaExpandX: 10,
      touchAreaExpandY: 10,
      mouseAreaExpandX: 0,
      mouseAreaExpandY: 5,
      backgroundStroke: 'gray',
      backgroundLineWidth: 0.5,
      arrowHeight: 6,
      arrowYSpacing: 3
    }, options );
    // {Color|string} color of arrows and top/bottom gradient when pressed
    options.pressedColor = options.pressedColor || Color.toColor( options.color ).darkerColor();

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

    //------------------------------------------------------------
    // Properties

    this.valueProperty = valueProperty; // @private must be unlinked in dispose

    var upStateProperty = new Property( 'up' ); // up|down|over|out
    var downStateProperty = new Property( 'up' ); // up|down|over|out

    // @private must be detached in dispose
    this.upEnabledProperty = new DerivedProperty( [ valueProperty, rangeProperty ], function( value, range ) {
      return ( value !== null && value !== undefined && value < range.max );
    } );

    // @private must be detached in dispose
    this.downEnabledProperty = new DerivedProperty( [ valueProperty, rangeProperty ], function( value, range ) {
      return ( value !== null && value !== undefined && value > range.min );
    } );

    //------------------------------------------------------------
    // Nodes

    // displays the value
    var valueNode = new Text( '', { font: options.font, pickable: false } );

    // compute max width of text based on value range
    valueNode.text = Util.toFixed( rangeProperty.get().min, options.decimalPlaces );
    var maxWidth = valueNode.width;
    valueNode.text = Util.toFixed( rangeProperty.get().max, options.decimalPlaces );
    maxWidth = Math.max( maxWidth, valueNode.width );

    // compute shape of the background behind the numeric value
    var backgroundWidth = maxWidth + ( 2 * options.xMargin );
    var backgroundHeight = valueNode.height + ( 2 * options.yMargin );
    var backgroundOverlap = 1;
    var backgroundCornerRadius = options.cornerRadius;

    // top half of the background, for 'up'. Shape computed starting at upper-left, going clockwise.
    var upBackground = new Path( new Shape()
      .arc( backgroundCornerRadius, backgroundCornerRadius, backgroundCornerRadius, Math.PI, Math.PI * 3 / 2, false )
      .arc( backgroundWidth - backgroundCornerRadius, backgroundCornerRadius, backgroundCornerRadius, -Math.PI / 2, 0, false )
      .lineTo( backgroundWidth, ( backgroundHeight / 2 ) + backgroundOverlap )
      .lineTo( 0, ( backgroundHeight / 2 ) + backgroundOverlap )
      .close(), { pickable: false } );

    // bottom half of the background, for 'down'. Shape computed starting at bottom-right, going clockwise.
    var downBackground = new Path( new Shape()
      .arc( backgroundWidth - backgroundCornerRadius, backgroundHeight - backgroundCornerRadius, backgroundCornerRadius, 0, Math.PI / 2, false )
      .arc( backgroundCornerRadius, backgroundHeight - backgroundCornerRadius, backgroundCornerRadius, Math.PI / 2, Math.PI, false )
      .lineTo( 0, backgroundHeight / 2 )
      .lineTo( backgroundWidth, backgroundHeight / 2 )
      .close(), { pickable: false } );

    // separate rectangle for stroke around value background
    var strokedBackground = new Rectangle( 0, 0, backgroundWidth, backgroundHeight, backgroundCornerRadius, backgroundCornerRadius, {
      pickable: false,
      stroke: options.backgroundStroke,
      lineWidth: options.backgroundLineWidth
    } );

    // compute size of arrows
    var arrowButtonSize = new Dimension2( 0.5 * backgroundWidth, options.arrowHeight );

    // 'up' arrow
    var arrowOptions = { stroke: 'black', lineWidth: 0.25, pickable: false };
    var upArrowShape = new Shape()
      .moveTo( arrowButtonSize.width / 2, 0 )
      .lineTo( arrowButtonSize.width, arrowButtonSize.height )
      .lineTo( 0, arrowButtonSize.height )
      .close();
    this.upArrow = new Path( upArrowShape, arrowOptions ); // @private
    this.upArrow.centerX = upBackground.centerX;
    this.upArrow.bottom = upBackground.top - options.arrowYSpacing;

    // 'down' arrow
    var downArrowShape = new Shape()
      .moveTo( arrowButtonSize.width / 2, arrowButtonSize.height )
      .lineTo( 0, 0 )
      .lineTo( arrowButtonSize.width, 0 )
      .close();
    this.downArrow = new Path( downArrowShape, arrowOptions ); // @private
    this.downArrow.centerX = downBackground.centerX;
    this.downArrow.top = downBackground.bottom + options.arrowYSpacing;

    // parents for 'up' and 'down' components
    var upParent = new Node( { children: [ upBackground, this.upArrow ] } );
    upParent.addChild( new Rectangle( upParent.localBounds ) ); // invisible overlay
    var downParent = new Node( { children: [ downBackground, this.downArrow ] } );
    downParent.addChild( new Rectangle( downParent.localBounds ) ); // invisible overlay

    // rendering order
    this.addChild( upParent );
    this.addChild( downParent );
    this.addChild( strokedBackground );
    this.addChild( valueNode );

    //------------------------------------------------------------
    // Pointer areas

    // touch area
    upParent.touchArea = Shape.rectangle(
      upParent.left - ( options.touchAreaExpandX / 2 ), upParent.top - options.touchAreaExpandY,
      upParent.width + options.touchAreaExpandX, upParent.height + options.touchAreaExpandY );
    downParent.touchArea = Shape.rectangle(
      downParent.left - ( options.touchAreaExpandX / 2 ), downParent.top,
      downParent.width + options.touchAreaExpandX, downParent.height + options.touchAreaExpandY );

    // mouse area
    upParent.mouseArea = Shape.rectangle(
      upParent.left - ( options.mouseAreaExpandX / 2 ), upParent.top - options.mouseAreaExpandY,
      upParent.width + options.mouseAreaExpandX, upParent.height + options.mouseAreaExpandY );
    downParent.mouseArea = Shape.rectangle(
      downParent.left - ( options.mouseAreaExpandX / 2 ), downParent.top,
      downParent.width + options.mouseAreaExpandX, downParent.height + options.mouseAreaExpandY );

    //------------------------------------------------------------
    // Colors

    // arrow colors
    var arrowColors = {
      up: options.color,
      over: options.color,
      down: options.pressedColor,
      out: options.color,
      disabled: 'rgb(176,176,176)'
    };

    // background colors
    var highlightGradient = createVerticalGradient( options.color, options.backgroundColor, options.color, backgroundHeight );
    var pressedGradient = createVerticalGradient( options.pressedColor, options.backgroundColor, options.pressedColor, backgroundHeight );
    var backgroundColors = {
      up: options.backgroundColor,
      over: highlightGradient,
      down: pressedGradient,
      out: pressedGradient,
      disabled: options.backgroundColor
    };

    //------------------------------------------------------------
    // Observers and InputListeners

    // up
    upParent.addInputListener( new ButtonStateListener( upStateProperty ) );
    this.upListener = new FireOnHoldInputListener( {
      listener: function() {
        valueProperty.set( Math.min( options.upFunction(), rangeProperty.get().max ) );
      },
      timerDelay: options.timerDelay,
      timerInterval: options.timerInterval
    } );
    upParent.addInputListener( this.upListener );

    // down
    downParent.addInputListener( new ButtonStateListener( downStateProperty ) );
    // @private
    this.downListener = new FireOnHoldInputListener( {
      listener: function() {
        valueProperty.set( Math.max( options.downFunction(), rangeProperty.get().min ) );
      },
      timerDelay: options.timerDelay,
      timerInterval: options.timerInterval
    } );
    downParent.addInputListener( this.downListener );

    // enable/disable listeners: unlink unnecessary, properties are owned by this instance
    this.upEnabledProperty.link( function( enabled ) { thisNode.upListener.enabled = enabled; } );
    this.downEnabledProperty.link( function( enabled ) { thisNode.downListener.enabled = enabled; } );

    // @private Update text to match the value
    this.valueObserver = function( value ) {
      if ( value === null || value === undefined ) {
        valueNode.text = options.noValueString;
        valueNode.x = ( backgroundWidth - valueNode.width ) / 2; // horizontally centered
      }
      else {
        valueNode.text = Util.toFixed( value, options.decimalPlaces );
        if ( options.align === 'center' ) {
          valueNode.centerX = upBackground.centerX;
        }
        else if ( options.align === 'right' ) {
          valueNode.right = upBackground.right - options.xMargin;
        }
        else if ( options.align === 'left' ) {
          valueNode.left = upBackground.left + options.xMargin;
        }
        else {
          throw new Error( 'unsupported value for options.align: ' + options.align );
        }
      }
      valueNode.centerY = backgroundHeight / 2;
    };
    this.valueProperty.link( this.valueObserver ); // must be unlinked in dispose

    // @private update colors for 'up' components
    Property.multilink( [ upStateProperty, this.upEnabledProperty ], function( state, enabled ) {
      updateColors( state, enabled, upBackground, thisNode.upArrow, backgroundColors, arrowColors );
    } );

    // @private update colors for 'down' components
    Property.multilink( [ downStateProperty, this.downEnabledProperty ], function( state, enabled ) {
      updateColors( state, enabled, downBackground, thisNode.downArrow, backgroundColors, arrowColors );
    } );

    this.mutate( options );
  }

  sceneryPhet.register( 'NumberPicker', NumberPicker );

  /**
   * Converts ButtonListener events to state changes.
   *
   * @param {Property.<string>} stateProperty up|down|over|out
   * @param {Object} [options]
   * @constructor
   */
  function ButtonStateListener( stateProperty ) {
    ButtonListener.call( this, {
      up: function() { stateProperty.set( 'up' ); },
      over: function() { stateProperty.set( 'over' ); },
      down: function() { stateProperty.set( 'down' ); },
      out: function() { stateProperty.set( 'out' ); }
    } );
  }

  sceneryPhet.register( 'NumberPicker.ButtonStateListener', ButtonStateListener );

  inherit( ButtonListener, ButtonStateListener );

  // creates a vertical gradient
  var createVerticalGradient = function( topColor, centerColor, bottomColor, height ) {
    return new LinearGradient( 0, 0, 0, height )
      .addColorStop( 0, topColor )
      .addColorStop( 0.5, centerColor )
      .addColorStop( 1, bottomColor );
  };

  // Update arrow and background colors
  var updateColors = function( state, enabled, background, arrow, backgroundColors, arrowColors ) {
    if ( enabled ) {
      arrow.stroke = 'black';
      if ( state === 'up' ) {
        background.fill = backgroundColors.up;
        arrow.fill = arrowColors.up;
      }
      else if ( state === 'over' ) {
        background.fill = backgroundColors.over;
        arrow.fill = arrowColors.over;
      }
      else if ( state === 'down' ) {
        background.fill = backgroundColors.down;
        arrow.fill = arrowColors.down;
      }
      else if ( state === 'out' ) {
        background.fill = backgroundColors.out;
        arrow.fill = arrowColors.out;
      }
      else {
        throw new Error( 'unsupported state: ' + state );
      }
    }
    else {
      background.fill = backgroundColors.disabled;
      arrow.fill = arrowColors.disabled;
      arrow.stroke = arrowColors.disabled; // stroke so that arrow size will look the same when it's enabled/disabled
    }
  };

  return inherit( Node, NumberPicker, {

    // @public Ensures that this node is eligible for GC.
    dispose: function() {

      this.upEnabledProperty.unlinkAll(); // Property is owned by this instance
      this.upEnabledProperty.dispose();

      this.downEnabledProperty.unlinkAll(); // Property is owned by this instance
      this.downEnabledProperty.dispose();

      this.valueProperty.unlink( this.valueObserver );
    },

    // @public
    setArrowsVisible: function( visible ) {
      this.upListener.setEnabled( visible );
      this.downListener.setEnabled( visible );
      this.upArrow.visible = this.downArrow.visible = visible;
    }
  } );
} );
<|endoftext|>


<commit_before>'use strict';

const { flatten } = require('lodash');
const createDebug = require('debug');
const defaultRoles = require('../config/defaultRoles');
const routes = require('../routes/acl');

const debug = createDebug('uwave:acl');

async function getSubRoles(role) {
  if (role.roles.length === 0) {
    return [role];
  }
  if (!(role.roles[0] instanceof role.constructor)) {
    role.populate('roles');
    await role.execPopulate();
  }

  const roles = await Promise.all(role.roles.map(getSubRoles));
  roles.unshift(role);
  return flatten(roles);
}

async function getAllUserRoles(user) {
  if (user.roles.length === 0) {
    return [];
  }

  user.populate('roles');
  await user.execPopulate('roles');
  const roles = await Promise.all(user.roles.map(getSubRoles));
  return flatten(roles);
}

const getRoleName = (role) => (
  typeof role === 'string' ? role : role.id
);

class Acl {
  constructor(uw) {
    this.uw = uw;
    this.superRole = '*';
  }

  get AclRole() {
    return this.uw.model('AclRole');
  }

  async maybeAddDefaultRoles() {
    const existingRoles = await this.AclRole.estimatedDocumentCount();
    debug('existing roles', existingRoles);
    if (existingRoles === 0) {
      debug('no roles found, adding defaults');
      for (const [roleName, permissions] of Object.entries(defaultRoles)) {
        // eslint-disable-next-line no-await-in-loop
        await this.createRole(roleName, permissions);
      }
    }
  }

  async getAclRoles(names, options = {}) {
    const existingRoles = await this.AclRole.find({ _id: { $in: names } });
    const newNames = names.filter((name) => (
      !existingRoles.some((role) => role.id === name)
    ));
    if (options.create && newNames.length > 0) {
      const newRoles = await this.AclRole.create(newNames.map((name) => ({ _id: name })));
      existingRoles.push(...newRoles);
    }
    return existingRoles;
  }

  getAclUser(user) {
    return this.uw.users.getUser(user);
  }

  async getAllRoles() {
    const roles = await this.AclRole.find().lean();
    return roles.reduce((map, role) => Object.assign(map, {
      [role._id]: role.roles,
    }), {});
  }

  async createRole(name, permissions) {
    const roles = await this.getAclRoles(permissions, { create: true });
    await this.AclRole.findByIdAndUpdate(
      name,
      { roles },
      { upsert: true },
    );

    const subRoles = await Promise.all(roles.map(getSubRoles));
    return {
      name,
      permissions: flatten(subRoles).map((role) => role._id),
    };
  }

  async deleteRole(name) {
    await this.AclRole.deleteOne({ _id: name });
  }

  async allow(user, roleNames) {
    const aclRoles = await this.getAclRoles(roleNames);
    const aclUser = await this.getAclUser(user);

    aclUser.roles.push(...aclRoles);

    await aclUser.save();

    this.uw.publish('acl:allow', {
      userID: aclUser.id,
      roles: aclRoles.map((role) => role.id),
    });
  }

  async disallow(user, roleNames) {
    const aclRoles = await this.getAclRoles(roleNames);
    const aclUser = await this.getAclUser(user);
    const shouldRemove = (roleName) => aclRoles.some((remove) => remove.id === roleName);
    aclUser.roles = aclUser.roles.filter((role) => !shouldRemove(getRoleName(role)));
    await aclUser.save();

    this.uw.publish('acl:disallow', {
      userID: aclUser.id,
      roles: aclRoles.map((role) => role.id),
    });
  }

  async getAllPermissions(user) {
    const aclUser = await this.getAclUser(user);
    const roles = await getAllUserRoles(aclUser);
    return roles.map((role) => role.id);
  }

  async isAllowed(user, permission) {
    const role = await this.AclRole.findById(permission);
    if (!role) {
      return false;
    }

    const aclUser = await this.getAclUser(user);
    const userRoles = await getAllUserRoles(aclUser);
    const roleIds = userRoles.map((userRole) => userRole.id);

    debug('role ids', roleIds, 'check', aclUser, role.id, 'super', this.superRole);

    return roleIds.includes(role.id) || roleIds.includes(this.superRole);
  }
}

async function acl(uw, opts = {}) {
  uw.acl = new Acl(uw);
  uw.httpApi.use('/roles', routes());

  if (opts.defaultRoles !== false) {
    uw.after(async () => {
      await uw.acl.maybeAddDefaultRoles();
    });
  }
}

module.exports = acl;
module.exports.Acl = Acl;
<commit_msg>Remove option to disable default roles (#437)<commit_after>'use strict';

const { flatten } = require('lodash');
const createDebug = require('debug');
const defaultRoles = require('../config/defaultRoles');
const routes = require('../routes/acl');

const debug = createDebug('uwave:acl');

async function getSubRoles(role) {
  if (role.roles.length === 0) {
    return [role];
  }
  if (!(role.roles[0] instanceof role.constructor)) {
    role.populate('roles');
    await role.execPopulate();
  }

  const roles = await Promise.all(role.roles.map(getSubRoles));
  roles.unshift(role);
  return flatten(roles);
}

async function getAllUserRoles(user) {
  if (user.roles.length === 0) {
    return [];
  }

  user.populate('roles');
  await user.execPopulate('roles');
  const roles = await Promise.all(user.roles.map(getSubRoles));
  return flatten(roles);
}

const getRoleName = (role) => (
  typeof role === 'string' ? role : role.id
);

class Acl {
  constructor(uw) {
    this.uw = uw;
    this.superRole = '*';
  }

  get AclRole() {
    return this.uw.model('AclRole');
  }

  async maybeAddDefaultRoles() {
    const existingRoles = await this.AclRole.estimatedDocumentCount();
    debug('existing roles', existingRoles);
    if (existingRoles === 0) {
      debug('no roles found, adding defaults');
      for (const [roleName, permissions] of Object.entries(defaultRoles)) {
        // eslint-disable-next-line no-await-in-loop
        await this.createRole(roleName, permissions);
      }
    }
  }

  async getAclRoles(names, options = {}) {
    const existingRoles = await this.AclRole.find({ _id: { $in: names } });
    const newNames = names.filter((name) => (
      !existingRoles.some((role) => role.id === name)
    ));
    if (options.create && newNames.length > 0) {
      const newRoles = await this.AclRole.create(newNames.map((name) => ({ _id: name })));
      existingRoles.push(...newRoles);
    }
    return existingRoles;
  }

  getAclUser(user) {
    return this.uw.users.getUser(user);
  }

  async getAllRoles() {
    const roles = await this.AclRole.find().lean();
    return roles.reduce((map, role) => Object.assign(map, {
      [role._id]: role.roles,
    }), {});
  }

  async createRole(name, permissions) {
    const roles = await this.getAclRoles(permissions, { create: true });
    await this.AclRole.findByIdAndUpdate(
      name,
      { roles },
      { upsert: true },
    );

    const subRoles = await Promise.all(roles.map(getSubRoles));
    return {
      name,
      permissions: flatten(subRoles).map((role) => role._id),
    };
  }

  async deleteRole(name) {
    await this.AclRole.deleteOne({ _id: name });
  }

  async allow(user, roleNames) {
    const aclRoles = await this.getAclRoles(roleNames);
    const aclUser = await this.getAclUser(user);

    aclUser.roles.push(...aclRoles);

    await aclUser.save();

    this.uw.publish('acl:allow', {
      userID: aclUser.id,
      roles: aclRoles.map((role) => role.id),
    });
  }

  async disallow(user, roleNames) {
    const aclRoles = await this.getAclRoles(roleNames);
    const aclUser = await this.getAclUser(user);
    const shouldRemove = (roleName) => aclRoles.some((remove) => remove.id === roleName);
    aclUser.roles = aclUser.roles.filter((role) => !shouldRemove(getRoleName(role)));
    await aclUser.save();

    this.uw.publish('acl:disallow', {
      userID: aclUser.id,
      roles: aclRoles.map((role) => role.id),
    });
  }

  async getAllPermissions(user) {
    const aclUser = await this.getAclUser(user);
    const roles = await getAllUserRoles(aclUser);
    return roles.map((role) => role.id);
  }

  async isAllowed(user, permission) {
    const role = await this.AclRole.findById(permission);
    if (!role) {
      return false;
    }

    const aclUser = await this.getAclUser(user);
    const userRoles = await getAllUserRoles(aclUser);
    const roleIds = userRoles.map((userRole) => userRole.id);

    debug('role ids', roleIds, 'check', aclUser, role.id, 'super', this.superRole);

    return roleIds.includes(role.id) || roleIds.includes(this.superRole);
  }
}

async function acl(uw) {
  uw.acl = new Acl(uw);
  uw.httpApi.use('/roles', routes());

  uw.after(async () => {
    await uw.acl.maybeAddDefaultRoles();
  });
}

module.exports = acl;
module.exports.Acl = Acl;
<|endoftext|>


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

/**
 * Constants used in sun, scenery-phet, and potentially in other PhET UI components.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( require => {
  'use strict';

  // modules
  const sun = require( 'SUN/sun' );

  const SunConstants = {

    // Named placeholder in string patterns where a value will be substituted.
    // Typically appears in translated strings (e.g. "{{value}} nm") and used with StringUtils.fillIn.
    VALUE_NAMED_PLACEHOLDER: '{{value}}',

    // Numbered placeholder in string patterns where a value will be substituted.
    // Typically appears in translated strings (e.g. "{0} nm") and used with StringUtils.format.
    // @deprecated - Named placeholders are the new standard, and numbered placeholders should not be used in new code.
    VALUE_NUMBERED_PLACEHOLDER: '{0}',

    // Opacity that is typically applied to a UI component in its disabled state, to make it look grayed out.
    DISABLED_OPACITY: 0.3
  };

  return sun.register( 'SunConstants', SunConstants );
} );<commit_msg>Use block comments so IDEA marks usages as deprecated, see https://github.com/phetsims/scenery-phet/issues/446<commit_after>// Copyright 2019, University of Colorado Boulder

/**
 * Constants used in sun, scenery-phet, and potentially in other PhET UI components.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( require => {
  'use strict';

  // modules
  const sun = require( 'SUN/sun' );

  const SunConstants = {

    /**
     * Named placeholder in string patterns where a value will be substituted.
     * Typically appears in translated strings (e.g. "{{value}} nm") and used with StringUtils.fillIn.
     */
    VALUE_NAMED_PLACEHOLDER: '{{value}}',

    /**
     * Numbered placeholder in string patterns where a value will be substituted.
     * Typically appears in translated strings (e.g. "{0} nm") and used with StringUtils.format.
     * @deprecated - Named placeholders are the new standard, and numbered placeholders should not be used in new code.
     */
    VALUE_NUMBERED_PLACEHOLDER: '{0}',

    // Opacity that is typically applied to a UI component in its disabled state, to make it look grayed out.
    DISABLED_OPACITY: 0.3
  };

  return sun.register( 'SunConstants', SunConstants );
} );<|endoftext|>


<commit_before>function(doc) {
    if (doc.type != "Vrac") {
        return;
    }

    var original = "OUI";
    if (doc.attente_original) {
        original = "NON"
    }

    var prix_variable = "NON";
    if (doc.prix_variable) {
        prix_variable = "OUI";
    }

    var interne = "NON";
    if (doc.interne) {
        interne = "OUI";
    }

    var archive = doc.numero_contrat;
    if (doc.numero_archive) {
	   archive = doc.numero_archive;
    }
    teledeclare = 0;
    if (doc.teledeclare && doc.teledeclare===true) {
	   teledeclare = 1;
    }

    if (doc.mandataire_identifiant) {
    emit([doc.mandataire_identifiant, doc.campagne, doc.date_signature, doc.valide.statut], [doc.campagne, doc.valide.statut, doc._id, doc.numero_contrat, archive, doc.acheteur_identifiant, doc.acheteur.nom, doc.vendeur_identifiant, doc.vendeur.nom, doc.mandataire_identifiant,doc.mandataire.nom, doc.representant_identifiant,doc.representant.nom, doc.type_transaction, doc.produit, doc.produit_libelle, doc.volume_propose, doc.volume_enleve, doc.prix_initial_unitaire_hl, doc.prix_unitaire_hl, prix_variable, interne, original, doc.type_contrat, doc.date_signature, doc.date_campagne, doc.valide.date_saisie, doc.millesime, doc.categorie_vin, doc.domaine, doc.part_variable, doc.cvo_repartition, doc.cvo_nature, doc.cepage, doc.cepage_libelle]);
    }
      emit([doc.acheteur_identifiant, doc.campagne, doc.date_signature, doc.valide.statut], [doc.campagne, doc.valide.statut, doc._id, doc.numero_contrat, archive, doc.acheteur_identifiant, doc.acheteur.nom, doc.vendeur_identifiant, doc.vendeur.nom, doc.mandataire_identifiant,doc.mandataire.nom, doc.representant_identifiant,doc.representant.nom, doc.type_transaction, doc.produit, doc.produit_libelle, doc.volume_propose, doc.volume_enleve, doc.prix_initial_unitaire_hl, doc.prix_unitaire_hl, prix_variable, interne, original, doc.type_contrat, doc.date_signature, doc.date_campagne, doc.valide.date_saisie, doc.millesime, doc.categorie_vin, doc.domaine, doc.part_variable, doc.cvo_repartition, doc.cvo_nature, doc.cepage, doc.cepage_libelle]);

      emit([doc.vendeur_identifiant, doc.campagne, doc.date_signature, doc.valide.statut], [doc.campagne, doc.valide.statut, doc._id, doc.numero_contrat, archive, doc.acheteur_identifiant, doc.acheteur.nom, doc.vendeur_identifiant, doc.vendeur.nom, doc.mandataire_identifiant,doc.mandataire.nom, doc.representant_identifiant,doc.representant.nom, doc.type_transaction, doc.produit, doc.produit_libelle, doc.volume_propose, doc.volume_enleve, doc.prix_initial_unitaire_hl, doc.prix_unitaire_hl, prix_variable, interne, original, doc.type_contrat, doc.date_signature, doc.date_campagne, doc.valide.date_saisie, doc.millesime, doc.categorie_vin, doc.domaine, doc.part_variable, doc.cvo_repartition, doc.cvo_nature, doc.cepage, doc.cepage_libelle]);
}
<commit_msg>Remonté des contrats par représentant<commit_after>function(doc) {
    if (doc.type != "Vrac") {
        return;
    }

    var original = "OUI";
    if (doc.attente_original) {
        original = "NON"
    }

    var prix_variable = "NON";
    if (doc.prix_variable) {
        prix_variable = "OUI";
    }

    var interne = "NON";
    if (doc.interne) {
        interne = "OUI";
    }

    var archive = doc.numero_contrat;
    if (doc.numero_archive) {
	   archive = doc.numero_archive;
    }
    teledeclare = 0;
    if (doc.teledeclare && doc.teledeclare===true) {
	   teledeclare = 1;
    }

    if (doc.mandataire_identifiant) {
    emit([doc.mandataire_identifiant, doc.campagne, doc.date_signature, doc.valide.statut], [doc.campagne, doc.valide.statut, doc._id, doc.numero_contrat, archive, doc.acheteur_identifiant, doc.acheteur.nom, doc.vendeur_identifiant, doc.vendeur.nom, doc.mandataire_identifiant,doc.mandataire.nom, doc.representant_identifiant,doc.representant.nom, doc.type_transaction, doc.produit, doc.produit_libelle, doc.volume_propose, doc.volume_enleve, doc.prix_initial_unitaire_hl, doc.prix_unitaire_hl, prix_variable, interne, original, doc.type_contrat, doc.date_signature, doc.date_campagne, doc.valide.date_saisie, doc.millesime, doc.categorie_vin, doc.domaine, doc.part_variable, doc.cvo_repartition, doc.cvo_nature, doc.cepage, doc.cepage_libelle]);
    }
      emit([doc.acheteur_identifiant, doc.campagne, doc.date_signature, doc.valide.statut], [doc.campagne, doc.valide.statut, doc._id, doc.numero_contrat, archive, doc.acheteur_identifiant, doc.acheteur.nom, doc.vendeur_identifiant, doc.vendeur.nom, doc.mandataire_identifiant,doc.mandataire.nom, doc.representant_identifiant,doc.representant.nom, doc.type_transaction, doc.produit, doc.produit_libelle, doc.volume_propose, doc.volume_enleve, doc.prix_initial_unitaire_hl, doc.prix_unitaire_hl, prix_variable, interne, original, doc.type_contrat, doc.date_signature, doc.date_campagne, doc.valide.date_saisie, doc.millesime, doc.categorie_vin, doc.domaine, doc.part_variable, doc.cvo_repartition, doc.cvo_nature, doc.cepage, doc.cepage_libelle]);

      emit([doc.vendeur_identifiant, doc.campagne, doc.date_signature, doc.valide.statut], [doc.campagne, doc.valide.statut, doc._id, doc.numero_contrat, archive, doc.acheteur_identifiant, doc.acheteur.nom, doc.vendeur_identifiant, doc.vendeur.nom, doc.mandataire_identifiant,doc.mandataire.nom, doc.representant_identifiant,doc.representant.nom, doc.type_transaction, doc.produit, doc.produit_libelle, doc.volume_propose, doc.volume_enleve, doc.prix_initial_unitaire_hl, doc.prix_unitaire_hl, prix_variable, interne, original, doc.type_contrat, doc.date_signature, doc.date_campagne, doc.valide.date_saisie, doc.millesime, doc.categorie_vin, doc.domaine, doc.part_variable, doc.cvo_repartition, doc.cvo_nature, doc.cepage, doc.cepage_libelle]);

    if (doc.representant_identifiant && doc.representant_identifiant != doc.vendeur_identifiant) {
         emit([doc.representant_identifiant, doc.campagne, doc.date_signature, doc.valide.statut], [doc.campagne, doc.valide.statut, doc._id, doc.numero_contrat, archive, doc.acheteur_identifiant, doc.acheteur.nom, doc.vendeur_identifiant, doc.vendeur.nom, doc.mandataire_identifiant,doc.mandataire.nom, doc.representant_identifiant,doc.representant.nom, doc.type_transaction, doc.produit, doc.produit_libelle, doc.volume_propose, doc.volume_enleve, doc.prix_initial_unitaire_hl, doc.prix_unitaire_hl, prix_variable, interne, original, doc.type_contrat, doc.date_signature, doc.date_campagne, doc.valide.date_saisie, doc.millesime, doc.categorie_vin, doc.domaine, doc.part_variable, doc.cvo_repartition, doc.cvo_nature, doc.cepage, doc.cepage_libelle]);
    }
}
<|endoftext|>


<commit_before>/**
 * Switcher.js allows native-like page switching with ajax.
 *
 * Server identifies Switcher.js requests by "X-Requested-With: SwitchPage" HTTP header.
 * When this header is detected, you can return only updated elements. Usually there is no point
 * to track which elements was updated, but you can return very simplified page with container elements only,
 * with all layout missing. To identify which elements to replace, this script uses id attribute. All container
 * elements you want to update on page switch should contain .switcher class.
 *
 * If script fails to switch link, it uses http redirect to change page.
 *
 * This script adds following events:
 * construct        fires after page change
 * destruct        fires before page change
 * switch        fires after destruct when script understands that page switch will be successful
 */

var switcher = {};

switcher.noPush = false;
switcher.url = false;
switcher.referrer = false;

switcher.ajaxConfig = {
    async: true, cache: false, headers: {'X-Requested-With': 'SwitchPage'}, type: 'GET', beforeSend: function () {
        loading.show();
    }, success: function (data, status, xhr) {
        try {
            var newdata = $(data);
        } catch (ignore) {
            switcher.fallback();
            return;
        }
        var a = newdata.filter('.switcher').add(newdata.find('.switcher'));
        if (!a.length) {
            switcher.fallback();
            return;
        }
        $(document).triggerHandler('destruct');
        if (!switcher.noPush) {
            history.pushState({url: switcher.url}, '', switcher.url);
        }
        $(document).triggerHandler('switch');

        a.each(function (k, v) {
            try {
                $('#' + $(v).attr('id')).replaceWith(v).remove();
            } catch (ignore) {
            }
        });
        $(window).scrollTop(0);

        var title = newdata.filter('title').text();
        if (title.length) {
            document.title = title;
        } else {
            title = newdata.find('title').text();
            if (title.length) {
                document.title = title;
            }
        }
        $(document).triggerHandler('construct');
        loading.hide();
    }, error: function (xhr) {
        switcher.fallback();
    }
};

/**
 * Page switch fall back
 */
switcher.fallback = function () {
    $(document).triggerHandler('destruct');
    loading.hide();
    document.location = switcher.url;
};

/**
 * Switch page
 * @param url
 * @param noPush
 * @param data
 */
switcher.page = function (url, noPush, data) {
    // cut protocol://host part if it matches current host
    var host = window.location.protocol + "//" + window.location.host + "/";
    if (host == url.substring(0, host.length)) {
        switcher.page(url.substring(host.length - 1));
        return;
    }
    if (typeof debug != 'undefined') {
        debug.addReq('Switching page: ' + url);
    }
    switcher.noPush = !!noPush;
    switcher.referrer = switcher.url;
    switcher.url = url;
    if (typeof data == 'undefined') {
        if ($('.switcher').length) {
            $.ajax(url, switcher.ajaxConfig);
        } else {
            $(document).triggerHandler('destruct');
            loading.hide();
            window.location = switcher.url;
        }
    } else {
        var conf = switcher.ajaxConfig;
        conf.type = 'POST';
        conf.data = data;
        $.ajax(url, conf);
    }
};

switcher.bind = function () {
    $(document).on('click dblclick touchend', 'a', function (event) {

        // skip .ajaxer and .noAjaxer links
        if ($(this).hasClass('ajaxer') || $(this).hasClass('noAjaxer')) {
            return;
        }

        var href = $(this).attr('href');

        // skip empty links, anchors and javascript
        if (href == '' || href == '#') {
            event.preventDefault();
            return;
        }
        if (href.substr(0, 11) == 'javascript:' || href.substr(0, 1) == '#') {
            return;
        }

        event.preventDefault();
        switcher.page(href);
    });
    $(window).on('popstate', switcher.onpopstate);
};

/**
 * Support "Back" and "Forward" browser buttons
 */
switcher.onpopstate = function () {
    if (switcher.url && switcher.url != decodeURI(document.location.pathname)) {
        switcher.page(document.location.href, true);
    }
};

switcher.init = function () {
    // if there are no switcher elements, don't init
    if (!$('.switcher:not(#debug)').length) {
        return;
    }
    // remember current URL on first page load
    if (!switcher.url) {
        switcher.url = decodeURI(document.location.pathname);
    }
    // binds
    switcher.bind();
};

$(document).ready(switcher.init);
<commit_msg>Fix browsers "back" action in switcher<commit_after>/**
 * Switcher.js allows native-like page switching with ajax.
 *
 * Server identifies Switcher.js requests by "X-Requested-With: SwitchPage" HTTP header.
 * When this header is detected, you can return only updated elements. Usually there is no point
 * to track which elements was updated, but you can return very simplified page with container elements only,
 * with all layout missing. To identify which elements to replace, this script uses id attribute. All container
 * elements you want to update on page switch should contain .switcher class.
 *
 * If script fails to switch link, it uses http redirect to change page.
 *
 * This script adds following events:
 * construct        fires after page change
 * destruct        fires before page change
 * switch        fires after destruct when script understands that page switch will be successful
 */

var switcher = {};

switcher.noPush = false;
switcher.url = false;
switcher.referrer = false;

switcher.ajaxConfig = {
    async: true, cache: false, headers: {'X-Requested-With': 'SwitchPage'}, type: 'GET', beforeSend: function () {
        loading.show();
    }, success: function (data, status, xhr) {
        try {
            var newdata = $(data);
        } catch (ignore) {
            switcher.fallback();
            return;
        }
        var a = newdata.filter('.switcher').add(newdata.find('.switcher'));
        if (!a.length) {
            switcher.fallback();
            return;
        }
        $(document).triggerHandler('destruct');
        if (!switcher.noPush) {
            history.pushState({url: switcher.url}, '', switcher.url);
        }
        $(document).triggerHandler('switch');

        a.each(function (k, v) {
            try {
                $('#' + $(v).attr('id')).replaceWith(v).remove();
            } catch (ignore) {
            }
        });
        $(window).scrollTop(0);

        var title = newdata.filter('title').text();
        if (title.length) {
            document.title = title;
        } else {
            title = newdata.find('title').text();
            if (title.length) {
                document.title = title;
            }
        }
        $(document).triggerHandler('construct');
        loading.hide();
    }, error: function (xhr) {
        switcher.fallback();
    }
};

/**
 * Page switch fall back
 */
switcher.fallback = function () {
    $(document).triggerHandler('destruct');
    loading.hide();
    document.location = switcher.url;
};

/**
 * Switch page
 * @param url
 * @param noPush
 * @param data
 */
switcher.page = function (url, noPush, data) {
    // cut protocol://host part if it matches current host
    var host = window.location.protocol + "//" + window.location.host + "/";
    if (host == url.substring(0, host.length)) {
        switcher.page(url.substring(host.length - 1), noPush, data);
        return;
    }
    if (typeof debug != 'undefined') {
        debug.addReq('Switching page: ' + url);
    }
    switcher.noPush = !!noPush;
    switcher.referrer = switcher.url;
    switcher.url = url;
    if (typeof data == 'undefined') {
        if ($('.switcher').length) {
            $.ajax(url, switcher.ajaxConfig);
        } else {
            $(document).triggerHandler('destruct');
            loading.hide();
            window.location = switcher.url;
        }
    } else {
        var conf = switcher.ajaxConfig;
        conf.type = 'POST';
        conf.data = data;
        $.ajax(url, conf);
    }
};

switcher.bind = function () {
    $(document).on('click dblclick touchend', 'a', function (event) {

        // skip .ajaxer and .noAjaxer links
        if ($(this).hasClass('ajaxer') || $(this).hasClass('noAjaxer')) {
            return;
        }

        var href = $(this).attr('href');

        // skip empty links, anchors and javascript
        if (href == '' || href == '#') {
            event.preventDefault();
            return;
        }
        if (href.substr(0, 11) == 'javascript:' || href.substr(0, 1) == '#') {
            return;
        }

        event.preventDefault();
        switcher.page(href);
    });
    $(window).on('popstate', switcher.onpopstate);
};

/**
 * Support "Back" and "Forward" browser buttons
 */
switcher.onpopstate = function () {
    if (switcher.url && switcher.url != decodeURI(document.location.pathname)) {
        switcher.page(document.location.href, true);
    }
};

switcher.init = function () {
    // if there are no switcher elements, don't init
    if (!$('.switcher:not(#debug)').length) {
        return;
    }
    // remember current URL on first page load
    if (!switcher.url) {
        switcher.url = decodeURI(document.location.pathname);
    }
    // binds
    switcher.bind();
};

$(document).ready(switcher.init);
<|endoftext|>


<commit_before>

window.onload=function()
{
	window.bgnum=4;//背景图片数量
	var myFirebaseRef = new Firebase("https://five-african.firebaseio.com/opentimes");
	myFirebaseRef.on("value", function(snapshot) {
		$(".good").text("已有"+snapshot.val()+"人点赞");
	});
	window.currentrandom=1;
	var audio = $("#audio")["0"];
	var disk = $('.disk');
	var cursor = $(".cursor");
	var paused=true;
	window.loopone = false;
	window.shuffle = false;
	$.getJSON("members.json",parseMembers);
	$("#progressbar").width(0);

	//进度条音量条
	$(".progress").mousedown(starDrag = function(ev) {
                ev.stopPropagation();
                var origX = ev.clientX;
                var origW = $(this).prev().width();
                var target = $(this).prev();
                var type = $(this).attr("id");
                $(document).mousemove(doDrag = function(ev){
                    ev.preventDefault();
                    var moveX = ev.clientX - origX;
                    //console.log(moveX/target.parent().width()*100);
                    var value = (origW+moveX)/target.parent().width();
                    value=value>1?1:(value<0?0:value);
                    target.width(value*100+"%");
                    //console.log(value*100);
                    switch(type)
                    {
                    	case "volume":audio.changeVolumeTo(value);break;
                    	case "progress":audio.setCurrentTime(value*audio.duration);break;
                    }
                });
                $(document).mouseup(stopDrag = function(){
                    $(document).unbind("mousemove",doDrag);
                    $(document).unbind("mouseup",stopDrag);
                });
            });
	$(".progress").bind("touchstart",starTDrag = function(ev) {
                ev.stopPropagation();
                var origX = ev.originalEvent.targetTouches["0"].clientX;
                console.log(ev.originalEvent.targetTouches["0"].clientX);
                var origW = $(this).prev().width();
                var target = $(this).prev();
                var type = $(this).attr("id");
                $(document).bind("touchmove",doTDrag = function(ev){
                    ev.preventDefault();
                    var moveX = ev.originalEvent.targetTouches["0"].clientX - origX;
                    console.log(moveX/target.parent().width()*100);
                    var value = (origW+moveX)/target.parent().width();
                    value=value>1?1:(value<0?0:value);
                    target.width(value*100+"%");
                    console.log(value*100);
                    switch(type)
                    {
                    	case "volume":audio.changeVolumeTo(value);break;
                    	case "progress":audio.setCurrentTime(value*audio.duration);break;
                    }
                });
                $(document).bind("touchend",stopTDrag = function(){
                    $(document).unbind("touchmove",doTDrag);
                    $(document).unbind("touchend",stopTDrag);
                });
            });

	//循环按钮
	$('#loop').click(function(){
		if(window.loopone){//window.loopone->window.shuffle
			$(this).addClass('window.shuffle').removeClass('window.loopone');
			audio.loop = false;
			window.loopone=false;
			window.shuffle=true;
		}
		else if(window.shuffle)//window.shuffle->loop
		{
			$(this).addClass('loop').removeClass('window.shuffle');
			audio.loop = false;
			window.loopone = false;
			window.shuffle=false;
		}
		else //loop->window.loopone
		{
			$(this).addClass('window.loopone').removeClass('loop');
			audio.loop = true;
			window.loopone = true;
			window.shuffle=false;
		}

	});
	$("#list").click(listshow = function(e){
		e.stopPropagation();
		if(!listshow.shown){
			$(".listcontainer").show().css("animation","listshow 0.7s linear 0s forwards").css("-webkit-animation","listshow 0.7s linear 0s forwards").css("-o-animation","listshow 0.7s linear 0s forwards").css("-moz-animation","listshow 0.7s linear 0s forwards").delay(700);listshow.shown=true;
		}else
		{
			$(".listcontainer").css("animation","listhide 0.7s linear 0s forwards").css("-webkit-animation","listhide 0.7s linear 0s forwards").css("-o-animation","listhide 0.7s linear 0s forwards").css("-moz-animation","listhide 0.7s linear 0s forwards").delay(700).hide(0);listshow.shown=false;
		}
	});
	$(".diskborder").click(function(e){
		e.stopPropagation();
		$(".cursoroutter").fadeToggle();
		$(".outtervolumebar").fadeToggle();
		$(".diskborder").fadeToggle();
		$("#membersshow").fadeToggle();
		$(".funcs>:first-child").fadeToggle();

	});
	$("#membersshow").click(function(e){
		e.stopPropagation();
		$(".cursoroutter").fadeToggle();
		$(".outtervolumebar").fadeToggle();
		$(".diskborder").fadeToggle();
		$("#membersshow").fadeToggle();
		$(".funcs>:first-child").fadeToggle();
	});
	$(".love").click(function(e)
	{
		$(this)[0].flag==undefined&&($(this)[0].flag=false);
		e.stopPropagation();
		if($(this)[0].flag)
		{
			$(this).addClass("love").removeClass("loved");
			$(this)[0].flag=false;
		}
		else
		{
			$(this).addClass("loved").removeClass("love");
			myFirebaseRef.transaction(function(opentimes) {

				$(".good").fadeToggle().text("已有"+(opentimes++)+"人点赞").delay(500).fadeToggle();
				return opentimes;
			});
			$(this)[0].flag=true;
		}
	})

	//读取媒体信息
	$(audio).bind("loadedmetadata",function(){
                var totalTime = formatTime(this.duration);
                $($("div").find("span")["1"]).text(totalTime);
                $("#progressbar").width(0);
            });
	$(audio).bind("canplay",function()
	{
		//播放暂停
		$("#play").click(function(e)
		{
			e.stopPropagation();
			if(paused)
			{
				audio.play();
			}
			else
			{
				audio.pause();
			}
		});
	});
	$(audio).trigger("canplay");
	$(audio).trigger("loadedmetadata");

	//播放时间更新
	$(audio).bind("timeupdate",function(){
                var duration = this.duration;
                var curTime = this.currentTime;
                var percentage = curTime/duration * 100;
                $("#progressbar").css("width",percentage + "%");
                var passedTime = formatTime(curTime);
                $(".progressbars>:first-child").text(passedTime);
            });

	//音频播放
	$(audio).bind("play",function(){
			$("#play").addClass('pause').removeClass('play');
			disk.css("animation-play-state","running");
			disk.css("-webkit-animation-play-state","running");
			disk.css("-o-animation-play-state","running");
			disk.css("-moz-animation-play-state","running");
			cursor.css("transform","rotate(-20deg)");
			cursor.css("-webkit-transform","rotate(-20deg)");
			cursor.css("-o-transform","rotate(-20deg)");
			cursor.css("-moztransform","rotate(-20deg)");
			cursor.css("animation","cursorclockwise 0.5s linear 0s forwards");
			cursor.css("-webkit-animation","cursorclockwise 0.5s linear 0s forwards");
			cursor.css("-o-animation","cursorclockwise 0.5s linear 0s forwards");
			cursor.css("-moz-animation","cursorclockwise 0.5s linear 0s forwards");
			paused=false;
            });

	//音频暂停
	$(audio).bind("pause",function(){
            $("#play").addClass('play').removeClass('pause');
			disk.css("animation-play-state","paused");
			disk.css("-wevkit-animation-play-state","paused");
			disk.css("-o-animation-play-state","paused");
			disk.css("-mozanimation-play-state","paused");
			cursor.css("transform","rotate(0deg)");
			cursor.css("-webkit-transform","rotate(0deg)");
			cursor.css("-o-transform","rotate(0deg)");
			cursor.css("-moz-transform","rotate(0deg)");
			cursor.css("animation","cursorcounter 0.5s linear 0s forwards");
			cursor.css("-webkit-animation","cursorcounter 0.5s linear 0s forwards");
			cursor.css("-o-animation","cursorcounter 0.5s linear 0s forwards");
			cursor.css("-moz-animation","cursorcounter 0.5s linear 0s forwards");
			paused=true;
            });

	//音频播放结束
	$(audio).bind("ended",function(){
		disk.css("animation-play-state","paused");
			disk.css("-wevkit-animation-play-state","paused");
			disk.css("-o-animation-play-state","paused");
			disk.css("-mozanimation-play-state","paused");
		paused=true;
		$("#play").addClass('play').removeClass('pause');
		if (!window.loopone) {
                $(".next").triggerHandler("click");
            }
        });
	//下一曲
	$(".next").click(function(){
		if(window.shuffle){
			window.currentSrcIndex = Math.ceil(Math.random()*window.memberlist.length)-1;
			window.currentSrcIndex < 0 && (window.currentSrcIndex = 0);
			window.currentSrcIndex = window.currentSrcIndex % window.memberlist.length;
		}
		else
        	++window.currentSrcIndex > window.memberlist.length - 1 && (window.currentSrcIndex = 0);

        audio.setCurrentSrc(window.currentSrcIndex);
		audio.play();
		audio.loop = window.loopone;
    });
    //上一曲
    $(".prev").click(function(){
    	if(window.shuffle){
			window.currentSrcIndex = Math.ceil(Math.random()*window.memberlist.length)-1;
			window.currentSrcIndex < 0 && (window.currentSrcIndex = 0);
			window.currentSrcIndex = window.currentSrcIndex % window.memberlist.length;
		}
		else
        	--window.currentSrcIndex < 0 && (window.currentSrcIndex = 0);
        var currentSrc = window.memberlist[window.currentSrcIndex].soundsrc;
        audio.setCurrentSrc(window.currentSrcIndex);
		audio.play();
		audio.loop = window.loopone;
    });

}
HTMLAudioElement.prototype.changeVolumeTo = function(volume){
    this.volume = volume;
}
HTMLAudioElement.prototype.setCurrentTime = function(currentTime){
	// $($("div").find("span")["0"]).text(formatTime(currentTime));
    this.currentTime = currentTime;
}
HTMLAudioElement.prototype.setCurrentSrc = function(currentSrcIndex){

	var val = Math.ceil(Math.random()*window.bgnum);
	val==0&&(val=1);
	currentrandom==val&&(val=(val+1)%window.bgnum);
	currentrandom=val;
	// $("#containerbody").fadeOut("fast",function(){$(this).css("background-image","url(\"img/bg"+val+".jpg\")");$(this).fadeIn()});
	// $(".mask_bg").fadeOut("fast",function(){$(this).css("background-image","url(\"img/bg"+val+".jpg\")");$(this).fadeIn()});
	$("#containerbody").css("background-image","url(\"img/bg"+val+".jpg\")");
	$(".mask_bg").css("background-image","url(\"img/bg"+val+".jpg\")");
	window.currentSrcIndex = currentSrcIndex;
	var currentSrc = window.memberlist[window.currentSrcIndex].soundsrc;
    $("#currenttitle").text(window.memberlist[window.currentSrcIndex].soundtitle);
	$("#currentartist").text(window.memberlist[window.currentSrcIndex].soundartist);
	$(".disk").css("background","img/"+window.memberlist[window.currentSrcIndex].soundtitle+".jpg");
	$(".diskmask").css("background","img/"+window.memberlist[window.currentSrcIndex].soundtitle+"jpg");
	$(".listcontainer li").css("background-color","");
	$("[data-id="+window.currentSrcIndex+"]").css("background-color","rgba(255,0,0,0.3)");
	$("#membersshow #"+window.memberlist[currentSrcIndex].membername+" .containerwrap").load(window.memberlist[currentSrcIndex].memberintro+' .main-border');
	$("#membersshow .active").removeClass("active");
	$("#membersshow #"+window.memberlist[currentSrcIndex].membername).addClass("active");
	$(".download").click(function()
	{
		 window.open(currentSrc,'_blank');
	});
    this.src = currentSrc;
}
function formatTime(time) {
    var minutes = parseInt(time/60);
    var seconds = parseInt(time%60);
    seconds<10 && (seconds = "0" + seconds);
    minutes<10 && (minutes = "0" + minutes);
    return isNaN(time)?"∞:∞":minutes + ":" + seconds;
};

function parseMembers(data)
{
	window.memberlist;
	window.currentSrcIndex=0;
	window.memberlist = new Array();
	$.each(data,function(i,content)
	{
		window.memberlist[i]=content;
		$(".listcontainer ul").append('

'+content["membername"]+'
'+content["soundtitle"]+''+content["soundartist"]+'
'+content["soundtime"]+'

');
		$("#membersshow").append('

');
	});
	$("#membersshow #"+window.memberlist[0].membername+" .containerwrap").load(window.memberlist[0].memberintro+' .main-border');
	audio.setCurrentSrc(0);
	$(".musiclist").click(function()
	{
		audio.setCurrentSrc($(this).attr("data-id"));
		audio.play();
		audio.loop = window.loopone;
	})
}<commit_msg>fix cover display<commit_after>

window.onload=function()
{
	window.bgnum=4;//背景图片数量
	var myFirebaseRef = new Firebase("https://five-african.firebaseio.com/opentimes");
	myFirebaseRef.on("value", function(snapshot) {
		$(".good").text("已有"+snapshot.val()+"人点赞");
	});
	window.currentrandom=1;
	var audio = $("#audio")["0"];
	var disk = $('.disk');
	var cursor = $(".cursor");
	var paused=true;
	window.loopone = false;
	window.shuffle = false;
	$.getJSON("members.json",parseMembers);
	$("#progressbar").width(0);

	//进度条音量条
	$(".progress").mousedown(starDrag = function(ev) {
                ev.stopPropagation();
                var origX = ev.clientX;
                var origW = $(this).prev().width();
                var target = $(this).prev();
                var type = $(this).attr("id");
                $(document).mousemove(doDrag = function(ev){
                    ev.preventDefault();
                    var moveX = ev.clientX - origX;
                    //console.log(moveX/target.parent().width()*100);
                    var value = (origW+moveX)/target.parent().width();
                    value=value>1?1:(value<0?0:value);
                    target.width(value*100+"%");
                    //console.log(value*100);
                    switch(type)
                    {
                    	case "volume":audio.changeVolumeTo(value);break;
                    	case "progress":audio.setCurrentTime(value*audio.duration);break;
                    }
                });
                $(document).mouseup(stopDrag = function(){
                    $(document).unbind("mousemove",doDrag);
                    $(document).unbind("mouseup",stopDrag);
                });
            });
	$(".progress").bind("touchstart",starTDrag = function(ev) {
                ev.stopPropagation();
                var origX = ev.originalEvent.targetTouches["0"].clientX;
                console.log(ev.originalEvent.targetTouches["0"].clientX);
                var origW = $(this).prev().width();
                var target = $(this).prev();
                var type = $(this).attr("id");
                $(document).bind("touchmove",doTDrag = function(ev){
                    ev.preventDefault();
                    var moveX = ev.originalEvent.targetTouches["0"].clientX - origX;
                    console.log(moveX/target.parent().width()*100);
                    var value = (origW+moveX)/target.parent().width();
                    value=value>1?1:(value<0?0:value);
                    target.width(value*100+"%");
                    console.log(value*100);
                    switch(type)
                    {
                    	case "volume":audio.changeVolumeTo(value);break;
                    	case "progress":audio.setCurrentTime(value*audio.duration);break;
                    }
                });
                $(document).bind("touchend",stopTDrag = function(){
                    $(document).unbind("touchmove",doTDrag);
                    $(document).unbind("touchend",stopTDrag);
                });
            });

	//循环按钮
	$('#loop').click(function(){
		if(window.loopone){//window.loopone->window.shuffle
			$(this).addClass('window.shuffle').removeClass('window.loopone');
			audio.loop = false;
			window.loopone=false;
			window.shuffle=true;
		}
		else if(window.shuffle)//window.shuffle->loop
		{
			$(this).addClass('loop').removeClass('window.shuffle');
			audio.loop = false;
			window.loopone = false;
			window.shuffle=false;
		}
		else //loop->window.loopone
		{
			$(this).addClass('window.loopone').removeClass('loop');
			audio.loop = true;
			window.loopone = true;
			window.shuffle=false;
		}

	});
	$("#list").click(listshow = function(e){
		e.stopPropagation();
		if(!listshow.shown){
			$(".listcontainer").show().css("animation","listshow 0.7s linear 0s forwards").css("-webkit-animation","listshow 0.7s linear 0s forwards").css("-o-animation","listshow 0.7s linear 0s forwards").css("-moz-animation","listshow 0.7s linear 0s forwards").delay(700);listshow.shown=true;
		}else
		{
			$(".listcontainer").css("animation","listhide 0.7s linear 0s forwards").css("-webkit-animation","listhide 0.7s linear 0s forwards").css("-o-animation","listhide 0.7s linear 0s forwards").css("-moz-animation","listhide 0.7s linear 0s forwards").delay(700).hide(0);listshow.shown=false;
		}
	});
	$(".diskborder").click(function(e){
		e.stopPropagation();
		$(".cursoroutter").fadeToggle();
		$(".outtervolumebar").fadeToggle();
		$(".diskborder").fadeToggle();
		$("#membersshow").fadeToggle();
		$(".funcs>:first-child").fadeToggle();

	});
	$("#membersshow").click(function(e){
		e.stopPropagation();
		$(".cursoroutter").fadeToggle();
		$(".outtervolumebar").fadeToggle();
		$(".diskborder").fadeToggle();
		$("#membersshow").fadeToggle();
		$(".funcs>:first-child").fadeToggle();
	});
	$(".love").click(function(e)
	{
		$(this)[0].flag==undefined&&($(this)[0].flag=false);
		e.stopPropagation();
		if($(this)[0].flag)
		{
			$(this).addClass("love").removeClass("loved");
			$(this)[0].flag=false;
		}
		else
		{
			$(this).addClass("loved").removeClass("love");
			myFirebaseRef.transaction(function(opentimes) {

				$(".good").fadeToggle().text("已有"+(opentimes++)+"人点赞").delay(500).fadeToggle();
				return opentimes;
			});
			$(this)[0].flag=true;
		}
	})

	//读取媒体信息
	$(audio).bind("loadedmetadata",function(){
                var totalTime = formatTime(this.duration);
                $($("div").find("span")["1"]).text(totalTime);
                $("#progressbar").width(0);
            });
	$(audio).bind("canplay",function()
	{
		//播放暂停
		$("#play").click(function(e)
		{
			e.stopPropagation();
			if(paused)
			{
				audio.play();
			}
			else
			{
				audio.pause();
			}
		});
	});
	$(audio).trigger("canplay");
	$(audio).trigger("loadedmetadata");

	//播放时间更新
	$(audio).bind("timeupdate",function(){
                var duration = this.duration;
                var curTime = this.currentTime;
                var percentage = curTime/duration * 100;
                $("#progressbar").css("width",percentage + "%");
                var passedTime = formatTime(curTime);
                $(".progressbars>:first-child").text(passedTime);
            });

	//音频播放
	$(audio).bind("play",function(){
			$("#play").addClass('pause').removeClass('play');
			disk.css("animation-play-state","running");
			disk.css("-webkit-animation-play-state","running");
			disk.css("-o-animation-play-state","running");
			disk.css("-moz-animation-play-state","running");
			cursor.css("transform","rotate(-20deg)");
			cursor.css("-webkit-transform","rotate(-20deg)");
			cursor.css("-o-transform","rotate(-20deg)");
			cursor.css("-moztransform","rotate(-20deg)");
			cursor.css("animation","cursorclockwise 0.5s linear 0s forwards");
			cursor.css("-webkit-animation","cursorclockwise 0.5s linear 0s forwards");
			cursor.css("-o-animation","cursorclockwise 0.5s linear 0s forwards");
			cursor.css("-moz-animation","cursorclockwise 0.5s linear 0s forwards");
			paused=false;
            });

	//音频暂停
	$(audio).bind("pause",function(){
            $("#play").addClass('play').removeClass('pause');
			disk.css("animation-play-state","paused");
			disk.css("-wevkit-animation-play-state","paused");
			disk.css("-o-animation-play-state","paused");
			disk.css("-mozanimation-play-state","paused");
			cursor.css("transform","rotate(0deg)");
			cursor.css("-webkit-transform","rotate(0deg)");
			cursor.css("-o-transform","rotate(0deg)");
			cursor.css("-moz-transform","rotate(0deg)");
			cursor.css("animation","cursorcounter 0.5s linear 0s forwards");
			cursor.css("-webkit-animation","cursorcounter 0.5s linear 0s forwards");
			cursor.css("-o-animation","cursorcounter 0.5s linear 0s forwards");
			cursor.css("-moz-animation","cursorcounter 0.5s linear 0s forwards");
			paused=true;
            });

	//音频播放结束
	$(audio).bind("ended",function(){
		disk.css("animation-play-state","paused");
			disk.css("-wevkit-animation-play-state","paused");
			disk.css("-o-animation-play-state","paused");
			disk.css("-mozanimation-play-state","paused");
		paused=true;
		$("#play").addClass('play').removeClass('pause');
		if (!window.loopone) {
                $(".next").triggerHandler("click");
            }
        });
	//下一曲
	$(".next").click(function(){
		if(window.shuffle){
			window.currentSrcIndex = Math.ceil(Math.random()*window.memberlist.length)-1;
			window.currentSrcIndex < 0 && (window.currentSrcIndex = 0);
			window.currentSrcIndex = window.currentSrcIndex % window.memberlist.length;
		}
		else
        	++window.currentSrcIndex > window.memberlist.length - 1 && (window.currentSrcIndex = 0);

        audio.setCurrentSrc(window.currentSrcIndex);
		audio.play();
		audio.loop = window.loopone;
    });
    //上一曲
    $(".prev").click(function(){
    	if(window.shuffle){
			window.currentSrcIndex = Math.ceil(Math.random()*window.memberlist.length)-1;
			window.currentSrcIndex < 0 && (window.currentSrcIndex = 0);
			window.currentSrcIndex = window.currentSrcIndex % window.memberlist.length;
		}
		else
        	--window.currentSrcIndex < 0 && (window.currentSrcIndex = 0);
        var currentSrc = window.memberlist[window.currentSrcIndex].soundsrc;
        audio.setCurrentSrc(window.currentSrcIndex);
		audio.play();
		audio.loop = window.loopone;
    });

}
HTMLAudioElement.prototype.changeVolumeTo = function(volume){
    this.volume = volume;
}
HTMLAudioElement.prototype.setCurrentTime = function(currentTime){
	// $($("div").find("span")["0"]).text(formatTime(currentTime));
    this.currentTime = currentTime;
}
HTMLAudioElement.prototype.setCurrentSrc = function(currentSrcIndex){

	var val = Math.ceil(Math.random()*window.bgnum);
	val==0&&(val=1);
	currentrandom==val&&(val=(val+1)%window.bgnum);
	currentrandom=val;
	// $("#containerbody").fadeOut("fast",function(){$(this).css("background-image","url(\"img/bg"+val+".jpg\")");$(this).fadeIn()});
	// $(".mask_bg").fadeOut("fast",function(){$(this).css("background-image","url(\"img/bg"+val+".jpg\")");$(this).fadeIn()});
	$("#containerbody").css("background-image","url(\"img/bg"+val+".jpg\")");
	$(".mask_bg").css("background-image","url(\"img/bg"+val+".jpg\")");
	window.currentSrcIndex = currentSrcIndex;
	var currentSrc = window.memberlist[window.currentSrcIndex].soundsrc;
    $("#currenttitle").text(window.memberlist[window.currentSrcIndex].soundtitle);
	$("#currentartist").text(window.memberlist[window.currentSrcIndex].soundartist);
	$(".disk").css("background-image","url(\"img/"+window.memberlist[window.currentSrcIndex].soundtitle+".jpg\")");
	$(".diskmask").css("background-image","url(\"img/"+window.memberlist[window.currentSrcIndex].soundtitle+".jpg\")");
	$(".listcontainer li").css("background-color","");
	$("[data-id="+window.currentSrcIndex+"]").css("background-color","rgba(255,0,0,0.3)");
	$("#membersshow #"+window.memberlist[currentSrcIndex].membername+" .containerwrap").load(window.memberlist[currentSrcIndex].memberintro+' .main-border');
	$("#membersshow .active").removeClass("active");
	$("#membersshow #"+window.memberlist[currentSrcIndex].membername).addClass("active");
	$(".download").click(function()
	{
		 window.open(currentSrc,'_blank');
	});
    this.src = currentSrc;
}
function formatTime(time) {
    var minutes = parseInt(time/60);
    var seconds = parseInt(time%60);
    seconds<10 && (seconds = "0" + seconds);
    minutes<10 && (minutes = "0" + minutes);
    return isNaN(time)?"∞:∞":minutes + ":" + seconds;
};

function parseMembers(data)
{
	window.memberlist;
	window.currentSrcIndex=0;
	window.memberlist = new Array();
	$.each(data,function(i,content)
	{
		window.memberlist[i]=content;
		$(".listcontainer ul").append('

'+content["membername"]+'
'+content["soundtitle"]+''+content["soundartist"]+'
'+content["soundtime"]+'

');
		$("#membersshow").append('

');
	});
	$("#membersshow #"+window.memberlist[0].membername+" .containerwrap").load(window.memberlist[0].memberintro+' .main-border');
	audio.setCurrentSrc(0);
	$(".musiclist").click(function()
	{
		audio.setCurrentSrc($(this).attr("data-id"));
		audio.play();
		audio.loop = window.loopone;
	})
}<|endoftext|>