<commit_before>/**
 * @author centsent
 */
import angular from 'angular';
import servicesModule from './_index';
import {
  mediaState
}
from './../AppSettings';

const _mediaElement = new WeakMap();
const _hydeoElement = new WeakMap();

const fullScreenEvents = [
  // Spec: https://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html
  'fullscreenchange',
  // WebKit
  'webkitfullscreenchange',
  // Mozilla
  'mozfullscreenchange',
  // Microsoft
  'MSFullscreenChange',
  // Old Webkit
  'webkitfullscreenchange'
];
const fullScreenElements = [
  // Spec: https://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html
  'fullscreenElement',
  // Webkit
  'webkitFullscreenElement',
  // Old Webkit
  'webkitCurrentFullScreenElement',
  // Mozilla
  'mozFullScreenElement',
  // Microsoft
  'msFullscreenElement'
];

/**
 * Provide APIs and event bindings for audio/video.
 */
class HyMediaService {

  /**
   * Store the hydeo element.
   */
  setHydeoElement(element) {
    _hydeoElement.set(this, element);
  }

  /**
   * Store the audio/video element.
   */
  setMediaElement(element) {
    _mediaElement.set(this, element);
    // Fires when a user enter/exit fullscreen mode.
    this.onFullScreenChange();
  }

  /**
   * Fires when the audio/video was started or no longer paused.
   *
   * @param handler {Function} A function to execute each time the `play` event
   * is triggered.
   *
   */
  onPlay(handler) {
    this.bindEvent('play', event => {
      this.currentState = mediaState.PLAY;

      if (angular.isFunction(handler)) {
        handler(this.currentState, event);
      }
    });
  }

  /**
   * Fires when the audio/video was paused.
   *
   * @param handler {Function} A function to execute each time the `pause` event
   * is triggered.
   *
   */
  onPause(handler) {
    this.bindEvent('pause', event => {
      this.currentState = mediaState.PAUSE;

      if (angular.isFunction(handler)) {
        handler(this.currentState, event);
      }
    });
  }

  /**
   * Start buffering.
   *
   * @param handler {Function} A function to execute when audio/video starting buffering.
   *
   */
  onWaiting(handler) {
    this.bindEvent('waiting', event => {
      this.isBuffering = true;

      if (angular.isFunction(handler)) {
        handler(this.isBuffering, event);
      }
    });
  }

  /**
   * Fires when the audio/video resumed playing after been paused or stopped
   * for buffering.
   *
   * @param handler {Function} A function to execute when the audio/video is resumed playing.
   *
   */
  onPlaying(handler) {
    this.bindEvent('playing', event => {
      this.isBuffering = false;

      if (angular.isFunction(handler)) {
        handler(this.isBuffering, event);
      }
    });
  }

  /**
   * Fires when the audio/video is ready to play.
   *
   * @param handler {Function} A function to execute once the audio/video is ready to play.
   *
   */
  onCanPlay(handler) {
    this.bindEvent('canplay', event => {
      this.isBuffering = false;
      this.totaltime = event.target.duration * 1000;

      if (angular.isFunction(handler)) {
        handler(this.totalTime, this.isBuffering, event);
      }
    });
  }

  /**
   * Fires when the current playback position was changed.
   *
   * @param handler {Function} A function to execute each time the `timeupdate`
   * event is triggered.
   *
   */
  onTimeUpdate(handler) {
    this.bindEvent('timeupdate', event => {
      const target = event.target;
      this.currentTime = target.currentTime * 1000;

      if (target.duration !== Infinity) {
        this.totalTime = target.duration * 1000;
        this.timeLeft = this.totalTime - this.currentTime;
        this.isLive = false;
      } else {
        this.isLive = true;
      }

      if (angular.isFunction(handler)) {
        handler(this.currentTime, this.timeLeft, event);
      }
    });
  }

  /**
   * Fires when the browser is downloading the audio/video.
   *
   * @param handler {Function} A function to execute each time the `progress`
   * event is triggered.
   *
   */
  onProgress(handler) {
    if (angular.isFunction(handler)) {
      this.bindEvent('progress', handler);
    }
  }

  /**
   * Fires when a user enter/exit fullscreen mode.
   *
   * @param handler {Function} A function to execute each time enter/exit full screen
   * mode.
   */
  onFullScreenChange(handler) {
    const hydeoElement = _hydeoElement.get(this);

    hydeoElement.bind(fullScreenEvents.join(' '), event => {
      if (angular.isFunction(handler)) {
        handler(this.isFullScreen, event);
      }
    });
  }

  /**
   * Fires when the volume has been changed.
   *
   * @param handler {Function} A function to execute each time change the audio/video volume.
   *
   */
  onVolumeChange(handler) {
    const mediaElement = _mediaElement.get(this);
    this.bindEvent('volumechange', event => {
      this.currentVolume = mediaElement.prop('volume');

      if (angular.isFunction(handler)) {
        handler(this.currentVolume, this.isMuted, event);
      }
    });
  }

  /**
   * Check whether the audio/video is paused or not.
   *
   * @returns {boolean} Returns `true` for the audio/video is paused, else `false`.
   */
  get isPause() {
    return this.currentState === mediaState.PAUSE;
  }

  /**
   * Check whether the audio/video is on playing or not.
   *
   * @returns {boolean} Returns `true` in case the audio/video is playing, else `false`.
   */
  get isPlay() {
    return this.currentState === mediaState.PLAY;
  }

  /**
   * Check whether the audio/video is stopped or not.
   *
   * @return {boolean} Returns `true` in case the audio/video is stopped, else `false`.
   */
  get isStop() {
    return this.currentState === mediaState.STOP;
  }

  /**
   * Determine a user enter/exit the full screen mode.
   */
  get isFullScreen() {
    return fullScreenElements.find(item => document[item]) !== undefined;
  }

  /**
   * Return `true` if the audio/video is muted, else `false`.
   *
   * @returns {boolean} Returns whether the audio/video is muted or not.
   */
  get isMuted() {
    const mediaElement = _mediaElement.get(this);
    return mediaElement.prop('muted');
  }

  /**
   * Attach a handler to an event for the video/audio elements.
   *
   * @param eventType {String} A string containing a DOM event types.
   * @param handler {Function} A function to execute each time the event is triggered.
   */
  bindEvent(eventType, handler) {
    const mediaElement = _mediaElement.get(this);
    if (eventType && angular.isFunction(handler)) {
      mediaElement.bind(eventType, handler);
    }
  }

  /**
   * Moving/skipping to a new position in the audio/video.
   *
   * @param time {number} A time point in second.
   */
  seek(time) {
    const mediaElement = _mediaElement.get(this);
    mediaElement[0].currentTime = time;
    this.currentTime = time * 1000;
  }

  /**
   * Play the audio/video if it's paused, else pause it.
   *
   * @param onPlay {Function} A function to execute when the audio/video is starting to play.
   * @param onPause {Function} A function to execute when the audio/video is starting to pause.
   *
   */
  togglePlay(onPlay, onPause) {
    if (this.isPlay) {
      this.pause();
      this.onPause(onPause);
    } else {
      this.play();
      this.onPlay(onPlay);
    }
  }

  /**
   * Enter full screen mode.
   */
  requestFullScreen() {
    const element = _hydeoElement.get(this)[0];
    if (element.webkitRequestFullscreen) {
      // Spec: https://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html
      element.webkitRequestFullscreen();
    } else if (element.requestFullscreen) {
      // Webkit
      element.requestFullscreen();
    } else if (element.mozRequestFullScreen) {
      // Mozilla
      element.mozRequestFullScreen();
    } else if (element.msRequestFullscreen) {
      // Microsoft
      element.msRequestFullscreen();
    } else if (element.webkitRequestFullScreen) {
      // Old WebKit (Safari 5.1)
      element.webkitRequestFullScreen();
    }
  }

  /**
   * Exit full screen mode.
   */
  exitFullScreen() {
    if (document.exitFullscreen) {
      // Spec: https://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html
      document.exitFullscreen();
    } else if (document.webkitExitFullscreen) {
      // Webkit
      document.webkitExitFullscreen();
    } else if (document.mozCancelFullScreen) {
      // Mozilla
      document.mozCancelFullScreen();
    } else if (document.msExitFullscreen) {
      // Microsoft
      document.msExitFullscreen();
    } else if (document.webkitCancelFullScreen) {
      // Old WebKit (Safari 5.1)
      document.webkitCancelFullScree();
    }
  }

  /**
   * Enter the full screen mode if not being displayed full-screen, else exit.
   */
  toggleFullScreen() {
    if (this.isFullScreen) {
      this.exitFullScreen();
    } else {
      this.requestFullScreen();
    }
  }

  /**
   * Pauses the currently playing audio/video.
   */
  pause() {
    const mediaElement = _mediaElement.get(this);

    if (!this.isPause && mediaElement) {
      mediaElement[0].pause();
      this.currentState = mediaState.PAUSE;
    }
  }

  /**
   * Starts playing the audio/video.
   */
  play() {
    const mediaElement = _mediaElement.get(this);

    if (!this.isPlay && mediaElement) {
      mediaElement[0].play();
      this.currentState = mediaState.PLAY;
    }
  }

  /**
   * Pauses the currently playing audio/video and reset current time to 0.
   */
  stop() {
    const mediaElement = _mediaElement.get(this);

    if (!this.isStop && mediaElement) {
      const elem = mediaElement[0];
      elem.pause();
      elem.currentTime = 0;
      this.currentState = mediaState.STOP;
    }
  }

  /**
   * Sets the audio/video is muted.
   */
  mute() {
    const mediaElement = _mediaElement.get(this);
    mediaElement.prop('muted', true);
  }

  /**
   * Sets the audio/video is not muted.
   */
  unmute() {
    const mediaElement = _mediaElement.get(this);
    mediaElement.prop('muted', false);
  }

  /**
   * Switch the audio/video to muted whether is not muted or vice verse.
   */
  toggleMuted() {
    if (this.isMuted) {
      this.unmute();
    } else {
      this.mute();
    }
  }

}

servicesModule.factory('$hyMedia', () => new HyMediaService());
<commit_msg>feat: add an api to change audio/video volume level<commit_after>/**
 * @author centsent
 */
import angular from 'angular';
import servicesModule from './_index';
import {
  mediaState
}
from './../AppSettings';

const _mediaElement = new WeakMap();
const _hydeoElement = new WeakMap();

const fullScreenEvents = [
  // Spec: https://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html
  'fullscreenchange',
  // WebKit
  'webkitfullscreenchange',
  // Mozilla
  'mozfullscreenchange',
  // Microsoft
  'MSFullscreenChange',
  // Old Webkit
  'webkitfullscreenchange'
];
const fullScreenElements = [
  // Spec: https://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html
  'fullscreenElement',
  // Webkit
  'webkitFullscreenElement',
  // Old Webkit
  'webkitCurrentFullScreenElement',
  // Mozilla
  'mozFullScreenElement',
  // Microsoft
  'msFullscreenElement'
];

/**
 * Provide APIs and event bindings for audio/video.
 */
class HyMediaService {

  /**
   * Store the hydeo element.
   */
  setHydeoElement(element) {
    _hydeoElement.set(this, element);
  }

  /**
   * Store the audio/video element.
   */
  setMediaElement(element) {
    _mediaElement.set(this, element);
    // Fires when a user enter/exit fullscreen mode.
    this.onFullScreenChange();
  }

  /**
   * Fires when the audio/video was started or no longer paused.
   *
   * @param handler {Function} A function to execute each time the `play` event
   * is triggered.
   *
   */
  onPlay(handler) {
    this.bindEvent('play', event => {
      this.currentState = mediaState.PLAY;

      if (angular.isFunction(handler)) {
        handler(this.currentState, event);
      }
    });
  }

  /**
   * Fires when the audio/video was paused.
   *
   * @param handler {Function} A function to execute each time the `pause` event
   * is triggered.
   *
   */
  onPause(handler) {
    this.bindEvent('pause', event => {
      this.currentState = mediaState.PAUSE;

      if (angular.isFunction(handler)) {
        handler(this.currentState, event);
      }
    });
  }

  /**
   * Start buffering.
   *
   * @param handler {Function} A function to execute when audio/video starting buffering.
   *
   */
  onWaiting(handler) {
    this.bindEvent('waiting', event => {
      this.isBuffering = true;

      if (angular.isFunction(handler)) {
        handler(this.isBuffering, event);
      }
    });
  }

  /**
   * Fires when the audio/video resumed playing after been paused or stopped
   * for buffering.
   *
   * @param handler {Function} A function to execute when the audio/video is resumed playing.
   *
   */
  onPlaying(handler) {
    this.bindEvent('playing', event => {
      this.isBuffering = false;

      if (angular.isFunction(handler)) {
        handler(this.isBuffering, event);
      }
    });
  }

  /**
   * Fires when the audio/video is ready to play.
   *
   * @param handler {Function} A function to execute once the audio/video is ready to play.
   *
   */
  onCanPlay(handler) {
    this.bindEvent('canplay', event => {
      this.isBuffering = false;
      this.totaltime = event.target.duration * 1000;

      if (angular.isFunction(handler)) {
        handler(this.totalTime, this.isBuffering, event);
      }
    });
  }

  /**
   * Fires when the current playback position was changed.
   *
   * @param handler {Function} A function to execute each time the `timeupdate`
   * event is triggered.
   *
   */
  onTimeUpdate(handler) {
    this.bindEvent('timeupdate', event => {
      const target = event.target;
      this.currentTime = target.currentTime * 1000;

      if (target.duration !== Infinity) {
        this.totalTime = target.duration * 1000;
        this.timeLeft = this.totalTime - this.currentTime;
        this.isLive = false;
      } else {
        this.isLive = true;
      }

      if (angular.isFunction(handler)) {
        handler(this.currentTime, this.timeLeft, event);
      }
    });
  }

  /**
   * Fires when the browser is downloading the audio/video.
   *
   * @param handler {Function} A function to execute each time the `progress`
   * event is triggered.
   *
   */
  onProgress(handler) {
    if (angular.isFunction(handler)) {
      this.bindEvent('progress', handler);
    }
  }

  /**
   * Fires when a user enter/exit fullscreen mode.
   *
   * @param handler {Function} A function to execute each time enter/exit full screen
   * mode.
   */
  onFullScreenChange(handler) {
    const hydeoElement = _hydeoElement.get(this);

    hydeoElement.bind(fullScreenEvents.join(' '), event => {
      if (angular.isFunction(handler)) {
        handler(this.isFullScreen, event);
      }
    });
  }

  /**
   * Fires when the volume has been changed.
   *
   * @param handler {Function} A function to execute each time change the audio/video volume.
   *
   */
  onVolumeChange(handler) {
    const mediaElement = _mediaElement.get(this);
    this.bindEvent('volumechange', event => {
      const volume = mediaElement.prop('volume');
      // TODO extract 100 to a configurable constant
      this.currentVolume = volume * 100;

      if (angular.isFunction(handler)) {
        handler(this.currentVolume, this.isMuted, event);
      }
    });
  }

  /**
   * Check whether the audio/video is paused or not.
   *
   * @returns {boolean} Returns `true` for the audio/video is paused, else `false`.
   */
  get isPause() {
    return this.currentState === mediaState.PAUSE;
  }

  /**
   * Check whether the audio/video is on playing or not.
   *
   * @returns {boolean} Returns `true` in case the audio/video is playing, else `false`.
   */
  get isPlay() {
    return this.currentState === mediaState.PLAY;
  }

  /**
   * Check whether the audio/video is stopped or not.
   *
   * @return {boolean} Returns `true` in case the audio/video is stopped, else `false`.
   */
  get isStop() {
    return this.currentState === mediaState.STOP;
  }

  /**
   * Determine a user enter/exit the full screen mode.
   */
  get isFullScreen() {
    return fullScreenElements.find(item => document[item]) !== undefined;
  }

  /**
   * Return `true` if the audio/video is muted, else `false`.
   *
   * @returns {boolean} Returns whether the audio/video is muted or not.
   */
  get isMuted() {
    const mediaElement = _mediaElement.get(this);
    return mediaElement.prop('muted');
  }

  /**
   * Attach a handler to an event for the video/audio elements.
   *
   * @param eventType {String} A string containing a DOM event types.
   * @param handler {Function} A function to execute each time the event is triggered.
   */
  bindEvent(eventType, handler) {
    const mediaElement = _mediaElement.get(this);
    if (eventType && angular.isFunction(handler)) {
      mediaElement.bind(eventType, handler);
    }
  }

  /**
   * Moving/skipping to a new position in the audio/video.
   *
   * @param time {number} A time point in second.
   */
  seek(time) {
    const mediaElement = _mediaElement.get(this);
    mediaElement[0].currentTime = time;
    this.currentTime = time * 1000;
  }

  /**
   * Play the audio/video if it's paused, else pause it.
   *
   * @param onPlay {Function} A function to execute when the audio/video is starting to play.
   * @param onPause {Function} A function to execute when the audio/video is starting to pause.
   *
   */
  togglePlay(onPlay, onPause) {
    if (this.isPlay) {
      this.pause();
      this.onPause(onPause);
    } else {
      this.play();
      this.onPlay(onPlay);
    }
  }

  /**
   * Enter full screen mode.
   */
  requestFullScreen() {
    const element = _hydeoElement.get(this)[0];
    if (element.webkitRequestFullscreen) {
      // Spec: https://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html
      element.webkitRequestFullscreen();
    } else if (element.requestFullscreen) {
      // Webkit
      element.requestFullscreen();
    } else if (element.mozRequestFullScreen) {
      // Mozilla
      element.mozRequestFullScreen();
    } else if (element.msRequestFullscreen) {
      // Microsoft
      element.msRequestFullscreen();
    } else if (element.webkitRequestFullScreen) {
      // Old WebKit (Safari 5.1)
      element.webkitRequestFullScreen();
    }
  }

  /**
   * Exit full screen mode.
   */
  exitFullScreen() {
    if (document.exitFullscreen) {
      // Spec: https://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html
      document.exitFullscreen();
    } else if (document.webkitExitFullscreen) {
      // Webkit
      document.webkitExitFullscreen();
    } else if (document.mozCancelFullScreen) {
      // Mozilla
      document.mozCancelFullScreen();
    } else if (document.msExitFullscreen) {
      // Microsoft
      document.msExitFullscreen();
    } else if (document.webkitCancelFullScreen) {
      // Old WebKit (Safari 5.1)
      document.webkitCancelFullScree();
    }
  }

  /**
   * Change the audio/video volume level.
   *
   * @param level {number} A number range from 0 to 100.
   *
   */
  volume(level) {
    if (!level || level < 0 || level > 100) {
      return;
    }

    const mediaElement = _mediaElement.get(this);
    // TODO extract 100 to a configurable constant.
    const volume = level / 100;
    mediaElement.prop('volume', volume);
  }

  /**
   * Enter the full screen mode if not being displayed full-screen, else exit.
   */
  toggleFullScreen() {
    if (this.isFullScreen) {
      this.exitFullScreen();
    } else {
      this.requestFullScreen();
    }
  }

  /**
   * Pauses the currently playing audio/video.
   */
  pause() {
    const mediaElement = _mediaElement.get(this);

    if (!this.isPause && mediaElement) {
      mediaElement[0].pause();
      this.currentState = mediaState.PAUSE;
    }
  }

  /**
   * Starts playing the audio/video.
   */
  play() {
    const mediaElement = _mediaElement.get(this);

    if (!this.isPlay && mediaElement) {
      mediaElement[0].play();
      this.currentState = mediaState.PLAY;
    }
  }

  /**
   * Pauses the currently playing audio/video and reset current time to 0.
   */
  stop() {
    const mediaElement = _mediaElement.get(this);

    if (!this.isStop && mediaElement) {
      const elem = mediaElement[0];
      elem.pause();
      elem.currentTime = 0;
      this.currentState = mediaState.STOP;
    }
  }

  /**
   * Sets the audio/video is muted.
   */
  mute() {
    const mediaElement = _mediaElement.get(this);
    mediaElement.prop('muted', true);
  }

  /**
   * Sets the audio/video is not muted.
   */
  unmute() {
    const mediaElement = _mediaElement.get(this);
    mediaElement.prop('muted', false);
  }

  /**
   * Switch the audio/video to muted whether is not muted or vice verse.
   */
  toggleMuted() {
    if (this.isMuted) {
      this.unmute();
    } else {
      this.mute();
    }
  }

}

servicesModule.factory('$hyMedia', () => new HyMediaService());
<|endoftext|>


<commit_before>'use strict'

/**
 * Ball Object representing any ball type object
 */
class Ball {
  constructor(args){
    this.x     = args.x;     //X position (center)
    this.y     = args.y;     //Y position (center)
    this.rad   = args.rad;   //Radius
    this.vec   = args.vec;   //Motion Vector
    this.color = args.color; //
  }

  /**
   * Called every frame
   */
  timeStep(){
    this.x = this.x+this.vec.x;
    this.y = this.y+this.vec.y;
  }

  /**
   * Called to render to a 2D context
   */
  drawable(ctx){
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.rad, 0, 2 * Math.PI, false);
    ctx.fillStyle = this.color;
    ctx.fill();
  }
}

/**
 * A rectangular object used to reflect other objects
 */
class Paddle {
  constructor(args){
    this.length    = 150;             //The "height" of the paddle
    this.width     = 10;              //The width of the paddle
    this.x         = args.x;          //The paddles top left x position
    this.y         = args.y;          //The paddles top left y position
    this.totalH    = args.totalH;     //The total height of the canvas this is in //TODO Make this dynamic
    this.direction = 0;               //The direction that this paddle is moving (-1 up 1 down 0 not)
    this.speed     = args.speed;      //The speed factor to move this paddle at.
    this.color     = "rgba(0,0,0,1)"; //The color of this paddle
  }

  /**
   * Called every frame
   */
  timeStep(){
    //Compute weather this is hitting the top or bottom (dont move further in that direction if so)
    if(this.y > 0 && this.direction < 0)
      this.y += this.direction*this.speed;
    else if(this.y < this.totalH-this.length && this.direction > 0)
      this.y += this.direction*this.speed;
  }

  /**
   * Called to render to a 2D context
   */
  drawable(ctx){
    ctx.fillStyle = this.color;
    ctx.fillRect(this.x,this.y,this.width,this.length);
  }
}

/**
 * A Wrapper around an HTML5 Canvas
 */
class Canvas {
  constructor(args){
    this.canvasElem = args.canvasElem;                   //The Canvas Element
    this.ctx = args.canvasElem.getContext('2d');         //The 2D context
    this.rect = this.canvasElem.getBoundingClientRect(); //The Rect
    this.width = this.rect.width;                        //Real pixel width
    this.height = this.rect.height;                      //Real pixel height
    this.canvasElem.width = this.width;                  //Set width
    this.canvasElem.height = this.height;                //Set height
    this.backgroundColor = args.backgroundColor;         //Set background Color
    this.forgroundColor = args.forgroundColor;           //Set forground Color
  }

  /**
   * Redraw blank canvas with background objects in it
   */
  clear(){
    //Clear
    this.ctx.beginPath();
    this.ctx.fillStyle = this.backgroundColor;
    this.ctx.fillRect(0,0,this.width,this.height);
    //Draw Box
    this.ctx.strokeStyle = this.forgroundColor;
    this.ctx.strokeWidth = 2;
    this.ctx.rect(0,0,this.width,this.height);
    this.ctx.stroke();
    //Draw Center Line
    this.ctx.beginPath();
    this.ctx.moveTo(this.width/2,0);
    this.ctx.lineTo(this.width/2,this.height);
    this.ctx.stroke();
  }
}

/**
 * Controller Super Class for all your controlling neeeds
 */
class Controller{
  constructor(args){
    this.sampleFreq = args.sampleFreq; //Frequency to Sample Data (erase direction)
    this.timestamp = Date.now();       //The current time
    this.direction = 0;                //The direction this controller is going right now (see paddle directions)

    /**
     * The interval function is called every frame with the current time.
     */
    this.intervalFunc = (now)=>{
      if((now - this.timestamp) > this.sampleFreq){
        this.direction = 0;
      }
    };
  }
}

/**
 * For the old school kids who don't use Leap Motion?
 * (also great for testing...)
 */
class KeyboardController extends Controller{
  constructor(args){
    super(args);
    this.oldSkoolMode = false;
    //Ancient method of listening for keystrokes
    document.onkeypress = (e)=>{
      e = e || window.event;
      var charCode = (typeof e.which == "number") ? e.which : e.keyCode;
      if (charCode) {
        //Check for Up key
        if(charCode == 119){ //'w'
          this.direction = -1;
          this.timestamp = Date.now();
        }
        //Check for Down key
        if(charCode == 115){ //'s'
          this.direction = 1;
          this.timestamp = Date.now();
        }
        //Check for oldSkool mode
        if(charCode == 32){
          this.oldSkoolMode = !this.oldSkoolMode;
          if(this.oldSkoolMode){
            document.getElementById("title").innerHTML = "OldSkoolPong";
          }else{
            document.getElementById("title").innerHTML = "LeapPong";
          }
        }
      }
    };
  }
}

/**
 * For us new school kids who use Leap Motion!
 * (the whole point of making this project ;)
 */
class LeapController extends Controller{
  constructor(args){
    super(args);

    const controllerOptions = {enableGestures: true};
    let setDirection = (dir) => {
                         this.direction = dir
                         this.timestamp = Date.now();
                        };

    //The Leap Motion controller loop function
    Leap.loop(controllerOptions, function(frame) {

      //If We see hands we use them
      if (frame.hands.length > 0) {
        //We use all hands to update input
        //Fun fact this makes things interesting when friends
        //are being fun at parties and interfering with your
        //attempts to play the game.
        //Ofc its trivial to use only one hand but that would
        //be less fun and "social"
        for (let i = 0; i < frame.hands.length; i++) {
          let hand = frame.hands[i];
          //We use one finger (your index) to compute direction
          if(hand.indexFinger.extended){
            let nameMap = ["thumb", "index", "middle", "ring", "pinky"];
            let pointer = null;
            hand.pointables.forEach(function(pointable){
              if(nameMap[pointable.type] == "index") pointer = pointable;
            });
            if(pointer){
              //If we have found your index finger we now use its direction to set our direction
              let direction = pointer.direction;
              if(direction[1] > 0.3){
                setDirection(-1);
              }
              if(direction[1] < -0.3){
                setDirection(1);
              }
            }
          }
        }
      }
    });
  }

}

/**
 * Dirty Cheating Computers
 * Oh yes this is a very stupid and rather annoying to beat
 * AI Controller.
 */
class AIController extends Controller{
  constructor(args){
    super(args);
    this.ball   = args.ball;
    this.paddle = args.paddle;

    //Use our own interval synced with the sample rate
    this.interval = setInterval(()=>{
      //Figure out where our paddle is relative to ball in y axis with a threshold
      let top = this.paddle.y + this.paddle.length/2 - 25;
      let bottom = this.paddle.y + this.paddle.length/2 + 25;

      //If top is too high move down
      if(top < this.ball.y){
        this.direction = 1;
        this.timestamp = Date.now();
      }
      //If bottom is too low move up
      if(bottom > this.ball.y){
        this.direction = -1;
        this.timestamp = Date.now();
      }

      //If within range dont move
      if(top > this.ball.y && bottom < this.ball.y){
        this.direction = 0;
        this.timestamp = Date.now();
      }
    },this.sampleFreq);
  }
}

/**
 * At last here is the Game Object!!! This is where
 * all the high level game state stuff happens and
 * where the master game loop resides.
 */
class Pong {
  constructor(canvas){
    this.framerate       = 20;   //The frame rate we want to run at
    this.inPlay          = true; //Fun Fact this is never false you can never ever stop >:3 //TODO Add pause button
    this.oldSkoolMode    = false; //Enable OldSkool controls

    //All the fun Score objects (hint the left will win if its a cheating computer unless you are a god)
    this.leftScore       = document.getElementById("leftScore");
    this.rightScore      = document.getElementById("rightScore");
    this.leftScr         = 0;
    this.rightScr        = 0;

    //The Canvas Object
    this.canvas          = new Canvas({canvasElem:canvas,
                                       backgroundColor: "rgba(255,255,255,1)",
                                       forgroundColor: "rgba(0,0,0,1)"
                           });

    //Our round object that we want to play with
    this.ball            = new Ball({x:this.canvas.width/2,
                                      y:this.canvas.height/2,
                                      rad:10,
                                      vec:{x:5,y:0},
                                      color:"rgba(0,0,0,1)"
                           });

    //The Cheating Computer's Paddle
    this.leftPaddle      = new Paddle({x:10,
                                       y:(canvas.height/2)-50,
                                       speed: 4,
                                       totalH: this.canvas.height,
                                       color:"rgba(0,0,0,1)"
                           });

    //The Human's Paddle
    this.rightPaddle     = new Paddle({x:canvas.width-20,
                                       y:(canvas.height/2)-50,
                                       speed: 6,
                                       totalH: this.canvas.height,
                                       color:"rgba(0,0,0,1)"
                           });

    //The Cheating Computer (I have handicaped him with a speed factor half that of yours)
    this.leftController  = new AIController({sampleFreq: this.framerate,
                                             ball: this.ball,
                                             paddle: this.leftPaddle
                           });

    //The Human using a Leap Motion //TODO Add option for old school players aka Keyboard
    this.rightController = new LeapController({sampleFreq: this.framerate});

    //For those who want to be old school
    this.oldSkool = new KeyboardController({sampleFreq: 250});

    //The Master Game Loop (simple right)
    this.interval = setInterval(()=>{
      if(this.inPlay)
        this.gameStep();
    },this.framerate);
  }

  /**
   * Compute a state change through a single step of the game.
   * (oh wait... not so simple...)
   */
  gameStep(){

    let now = Date.now(); //The current timestamp (please note that time travel will make this work oddly)

    //Update Controller input
    this.leftController.intervalFunc(now);
    this.oldSkool.intervalFunc(now);
    this.rightController.intervalFunc(now);

    //Get oldSkool mode from Keyboard Controller
    this.oldSkoolMode = this.oldSkool.oldSkoolMode;

    //Apply controller input to the Paddles
    this.leftPaddle.direction = this.leftController.direction;
    if(this.oldSkoolMode)
      this.rightPaddle.direction = this.oldSkool.direction;
    else
      this.rightPaddle.direction = this.rightController.direction;

    //Update the positions of the Round thing and the Paddles
    this.ball.timeStep();
    this.leftPaddle.timeStep();
    this.rightPaddle.timeStep();

    //Handle Wall Collisions
    if(this.ball.x < 0 + this.ball.rad){
      //left player loss
      this.rightScr += 1;
      this.ball.x = this.canvas.width/2;
      this.ball.y = this.canvas.height/2;
      this.ball.vec = {x:5,y:0}
    }

    if(this.ball.x > this.canvas.width - this.ball.rad){
      //right player loss
      this.leftScr += 1;
      this.ball.x = this.canvas.width/2;
      this.ball.y = this.canvas.height/2;
      this.ball.vec = {x:-5,y:0}
    }

    if(this.ball.y < 0 + this.ball.rad){
      //bounce mirror
      this.ball.vec.y = -this.ball.vec.y;
    }

    if(this.ball.y > this.canvas.height - this.ball.rad){
      //bounce mirror
      this.ball.vec.y = -this.ball.vec.y;
    }

    //Handle Paddle Collisions
    if((this.ball.x < this.leftPaddle.x + this.leftPaddle.width + this.ball.rad) &&
       (this.ball.y > this.leftPaddle.y && this.ball.y < this.leftPaddle.y + this.leftPaddle.length)){
      //bounce
      this.ball.vec.x = -this.ball.vec.x
      let yreflectmod = this.ball.y - (this.leftPaddle.y + this.leftPaddle.length/2);
      if(yreflectmod > 2){ //Dumb angle calulation
        yreflectmod = 2;
      }
      if(yreflectmod < -2){
        yreflectmod = -2;
      }
      this.ball.vec.y = this.ball.vec.y + yreflectmod;
    }

    if((this.ball.x > this.rightPaddle.x - this.ball.rad) &&
       (this.ball.y > this.rightPaddle.y && this.ball.y < this.rightPaddle.y + this.rightPaddle.length)){
      //bounce
      this.ball.vec.x = -this.ball.vec.x
      let yreflectmod = this.ball.y - (this.rightPaddle.y + this.rightPaddle.length/2);
      if(yreflectmod > 4){ //Dumb angle calculation
        yreflectmod = 4;
      }
      if(yreflectmod < -4){
        yreflectmod = -4;
      }
      this.ball.vec.y = this.ball.vec.y - yreflectmod;
    }

    //Redraw the Beautiful Canvas (ITS ART I SWEAR)
    this.canvas.clear();
    this.ball.drawable(this.canvas.ctx);
    this.leftPaddle.drawable(this.canvas.ctx);
    this.rightPaddle.drawable(this.canvas.ctx);

    this.leftScore.innerHTML = this.leftScr;
    this.rightScore.innerHTML = this.rightScr;

  }
}

/**
 * Helper function for Leap Motion Vectors
 */
function vectorToString(vector, digits) {
  if (typeof digits === "undefined") {
    digits = 1;
  }
  return "(" + vector[0].toFixed(digits) + ", "
             + vector[1].toFixed(digits) + ", "
             + vector[2].toFixed(digits) + ")";
}

/**
 * Self Calling Function to create THE GAME (did you just loose "the game"" again?)
 */
(()=>{
   new Pong(document.getElementById("canvas"));
})();<commit_msg>Update leappong.js<commit_after>'use strict'

/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 SparkX120 (James Wake)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 **/

/**
 * Ball Object representing any ball type object
 */
class Ball {
  constructor(args){
    this.x     = args.x;     //X position (center)
    this.y     = args.y;     //Y position (center)
    this.rad   = args.rad;   //Radius
    this.vec   = args.vec;   //Motion Vector
    this.color = args.color; //
  }

  /**
   * Called every frame
   */
  timeStep(){
    this.x = this.x+this.vec.x;
    this.y = this.y+this.vec.y;
  }

  /**
   * Called to render to a 2D context
   */
  drawable(ctx){
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.rad, 0, 2 * Math.PI, false);
    ctx.fillStyle = this.color;
    ctx.fill();
  }
}

/**
 * A rectangular object used to reflect other objects
 */
class Paddle {
  constructor(args){
    this.length    = 150;             //The "height" of the paddle
    this.width     = 10;              //The width of the paddle
    this.x         = args.x;          //The paddles top left x position
    this.y         = args.y;          //The paddles top left y position
    this.totalH    = args.totalH;     //The total height of the canvas this is in //TODO Make this dynamic
    this.direction = 0;               //The direction that this paddle is moving (-1 up 1 down 0 not)
    this.speed     = args.speed;      //The speed factor to move this paddle at.
    this.color     = "rgba(0,0,0,1)"; //The color of this paddle
  }

  /**
   * Called every frame
   */
  timeStep(){
    //Compute weather this is hitting the top or bottom (dont move further in that direction if so)
    if(this.y > 0 && this.direction < 0)
      this.y += this.direction*this.speed;
    else if(this.y < this.totalH-this.length && this.direction > 0)
      this.y += this.direction*this.speed;
  }

  /**
   * Called to render to a 2D context
   */
  drawable(ctx){
    ctx.fillStyle = this.color;
    ctx.fillRect(this.x,this.y,this.width,this.length);
  }
}

/**
 * A Wrapper around an HTML5 Canvas
 */
class Canvas {
  constructor(args){
    this.canvasElem = args.canvasElem;                   //The Canvas Element
    this.ctx = args.canvasElem.getContext('2d');         //The 2D context
    this.rect = this.canvasElem.getBoundingClientRect(); //The Rect
    this.width = this.rect.width;                        //Real pixel width
    this.height = this.rect.height;                      //Real pixel height
    this.canvasElem.width = this.width;                  //Set width
    this.canvasElem.height = this.height;                //Set height
    this.backgroundColor = args.backgroundColor;         //Set background Color
    this.forgroundColor = args.forgroundColor;           //Set forground Color
  }

  /**
   * Redraw blank canvas with background objects in it
   */
  clear(){
    //Clear
    this.ctx.beginPath();
    this.ctx.fillStyle = this.backgroundColor;
    this.ctx.fillRect(0,0,this.width,this.height);
    //Draw Box
    this.ctx.strokeStyle = this.forgroundColor;
    this.ctx.strokeWidth = 2;
    this.ctx.rect(0,0,this.width,this.height);
    this.ctx.stroke();
    //Draw Center Line
    this.ctx.beginPath();
    this.ctx.moveTo(this.width/2,0);
    this.ctx.lineTo(this.width/2,this.height);
    this.ctx.stroke();
  }
}

/**
 * Controller Super Class for all your controlling neeeds
 */
class Controller{
  constructor(args){
    this.sampleFreq = args.sampleFreq; //Frequency to Sample Data (erase direction)
    this.timestamp = Date.now();       //The current time
    this.direction = 0;                //The direction this controller is going right now (see paddle directions)

    /**
     * The interval function is called every frame with the current time.
     */
    this.intervalFunc = (now)=>{
      if((now - this.timestamp) > this.sampleFreq){
        this.direction = 0;
      }
    };
  }
}

/**
 * For the old school kids who don't use Leap Motion?
 * (also great for testing...)
 */
class KeyboardController extends Controller{
  constructor(args){
    super(args);
    this.oldSkoolMode = false;
    //Ancient method of listening for keystrokes
    document.onkeypress = (e)=>{
      e = e || window.event;
      var charCode = (typeof e.which == "number") ? e.which : e.keyCode;
      if (charCode) {
        //Check for Up key
        if(charCode == 119){ //'w'
          this.direction = -1;
          this.timestamp = Date.now();
        }
        //Check for Down key
        if(charCode == 115){ //'s'
          this.direction = 1;
          this.timestamp = Date.now();
        }
        //Check for oldSkool mode
        if(charCode == 32){
          this.oldSkoolMode = !this.oldSkoolMode;
          if(this.oldSkoolMode){
            document.getElementById("title").innerHTML = "OldSkoolPong";
          }else{
            document.getElementById("title").innerHTML = "LeapPong";
          }
        }
      }
    };
  }
}

/**
 * For us new school kids who use Leap Motion!
 * (the whole point of making this project ;)
 */
class LeapController extends Controller{
  constructor(args){
    super(args);

    const controllerOptions = {enableGestures: true};
    let setDirection = (dir) => {
                         this.direction = dir
                         this.timestamp = Date.now();
                        };

    //The Leap Motion controller loop function
    Leap.loop(controllerOptions, function(frame) {

      //If We see hands we use them
      if (frame.hands.length > 0) {
        //We use all hands to update input
        //Fun fact this makes things interesting when friends
        //are being fun at parties and interfering with your
        //attempts to play the game.
        //Ofc its trivial to use only one hand but that would
        //be less fun and "social"
        for (let i = 0; i < frame.hands.length; i++) {
          let hand = frame.hands[i];
          //We use one finger (your index) to compute direction
          if(hand.indexFinger.extended){
            let nameMap = ["thumb", "index", "middle", "ring", "pinky"];
            let pointer = null;
            hand.pointables.forEach(function(pointable){
              if(nameMap[pointable.type] == "index") pointer = pointable;
            });
            if(pointer){
              //If we have found your index finger we now use its direction to set our direction
              let direction = pointer.direction;
              if(direction[1] > 0.3){
                setDirection(-1);
              }
              if(direction[1] < -0.3){
                setDirection(1);
              }
            }
          }
        }
      }
    });
  }

}

/**
 * Dirty Cheating Computers
 * Oh yes this is a very stupid and rather annoying to beat
 * AI Controller.
 */
class AIController extends Controller{
  constructor(args){
    super(args);
    this.ball   = args.ball;
    this.paddle = args.paddle;

    //Use our own interval synced with the sample rate
    this.interval = setInterval(()=>{
      //Figure out where our paddle is relative to ball in y axis with a threshold
      let top = this.paddle.y + this.paddle.length/2 - 25;
      let bottom = this.paddle.y + this.paddle.length/2 + 25;

      //If top is too high move down
      if(top < this.ball.y){
        this.direction = 1;
        this.timestamp = Date.now();
      }
      //If bottom is too low move up
      if(bottom > this.ball.y){
        this.direction = -1;
        this.timestamp = Date.now();
      }

      //If within range dont move
      if(top > this.ball.y && bottom < this.ball.y){
        this.direction = 0;
        this.timestamp = Date.now();
      }
    },this.sampleFreq);
  }
}

/**
 * At last here is the Game Object!!! This is where
 * all the high level game state stuff happens and
 * where the master game loop resides.
 */
class Pong {
  constructor(canvas){
    this.framerate       = 20;   //The frame rate we want to run at
    this.inPlay          = true; //Fun Fact this is never false you can never ever stop >:3 //TODO Add pause button
    this.oldSkoolMode    = false; //Enable OldSkool controls

    //All the fun Score objects (hint the left will win if its a cheating computer unless you are a god)
    this.leftScore       = document.getElementById("leftScore");
    this.rightScore      = document.getElementById("rightScore");
    this.leftScr         = 0;
    this.rightScr        = 0;

    //The Canvas Object
    this.canvas          = new Canvas({canvasElem:canvas,
                                       backgroundColor: "rgba(255,255,255,1)",
                                       forgroundColor: "rgba(0,0,0,1)"
                           });

    //Our round object that we want to play with
    this.ball            = new Ball({x:this.canvas.width/2,
                                      y:this.canvas.height/2,
                                      rad:10,
                                      vec:{x:5,y:0},
                                      color:"rgba(0,0,0,1)"
                           });

    //The Cheating Computer's Paddle
    this.leftPaddle      = new Paddle({x:10,
                                       y:(canvas.height/2)-50,
                                       speed: 4,
                                       totalH: this.canvas.height,
                                       color:"rgba(0,0,0,1)"
                           });

    //The Human's Paddle
    this.rightPaddle     = new Paddle({x:canvas.width-20,
                                       y:(canvas.height/2)-50,
                                       speed: 6,
                                       totalH: this.canvas.height,
                                       color:"rgba(0,0,0,1)"
                           });

    //The Cheating Computer (I have handicaped him with a speed factor half that of yours)
    this.leftController  = new AIController({sampleFreq: this.framerate,
                                             ball: this.ball,
                                             paddle: this.leftPaddle
                           });

    //The Human using a Leap Motion //TODO Add option for old school players aka Keyboard
    this.rightController = new LeapController({sampleFreq: this.framerate});

    //For those who want to be old school
    this.oldSkool = new KeyboardController({sampleFreq: 250});

    //The Master Game Loop (simple right)
    this.interval = setInterval(()=>{
      if(this.inPlay)
        this.gameStep();
    },this.framerate);
  }

  /**
   * Compute a state change through a single step of the game.
   * (oh wait... not so simple...)
   */
  gameStep(){

    let now = Date.now(); //The current timestamp (please note that time travel will make this work oddly)

    //Update Controller input
    this.leftController.intervalFunc(now);
    this.oldSkool.intervalFunc(now);
    this.rightController.intervalFunc(now);

    //Get oldSkool mode from Keyboard Controller
    this.oldSkoolMode = this.oldSkool.oldSkoolMode;

    //Apply controller input to the Paddles
    this.leftPaddle.direction = this.leftController.direction;
    if(this.oldSkoolMode)
      this.rightPaddle.direction = this.oldSkool.direction;
    else
      this.rightPaddle.direction = this.rightController.direction;

    //Update the positions of the Round thing and the Paddles
    this.ball.timeStep();
    this.leftPaddle.timeStep();
    this.rightPaddle.timeStep();

    //Handle Wall Collisions
    if(this.ball.x < 0 + this.ball.rad){
      //left player loss
      this.rightScr += 1;
      this.ball.x = this.canvas.width/2;
      this.ball.y = this.canvas.height/2;
      this.ball.vec = {x:5,y:0}
    }

    if(this.ball.x > this.canvas.width - this.ball.rad){
      //right player loss
      this.leftScr += 1;
      this.ball.x = this.canvas.width/2;
      this.ball.y = this.canvas.height/2;
      this.ball.vec = {x:-5,y:0}
    }

    if(this.ball.y < 0 + this.ball.rad){
      //bounce mirror
      this.ball.vec.y = -this.ball.vec.y;
    }

    if(this.ball.y > this.canvas.height - this.ball.rad){
      //bounce mirror
      this.ball.vec.y = -this.ball.vec.y;
    }

    //Handle Paddle Collisions
    if((this.ball.x < this.leftPaddle.x + this.leftPaddle.width + this.ball.rad) &&
       (this.ball.y > this.leftPaddle.y && this.ball.y < this.leftPaddle.y + this.leftPaddle.length)){
      //bounce
      this.ball.vec.x = -this.ball.vec.x
      let yreflectmod = this.ball.y - (this.leftPaddle.y + this.leftPaddle.length/2);
      if(yreflectmod > 2){ //Dumb angle calulation
        yreflectmod = 2;
      }
      if(yreflectmod < -2){
        yreflectmod = -2;
      }
      this.ball.vec.y = this.ball.vec.y + yreflectmod;
    }

    if((this.ball.x > this.rightPaddle.x - this.ball.rad) &&
       (this.ball.y > this.rightPaddle.y && this.ball.y < this.rightPaddle.y + this.rightPaddle.length)){
      //bounce
      this.ball.vec.x = -this.ball.vec.x
      let yreflectmod = this.ball.y - (this.rightPaddle.y + this.rightPaddle.length/2);
      if(yreflectmod > 4){ //Dumb angle calculation
        yreflectmod = 4;
      }
      if(yreflectmod < -4){
        yreflectmod = -4;
      }
      this.ball.vec.y = this.ball.vec.y - yreflectmod;
    }

    //Redraw the Beautiful Canvas (ITS ART I SWEAR)
    this.canvas.clear();
    this.ball.drawable(this.canvas.ctx);
    this.leftPaddle.drawable(this.canvas.ctx);
    this.rightPaddle.drawable(this.canvas.ctx);

    this.leftScore.innerHTML = this.leftScr;
    this.rightScore.innerHTML = this.rightScr;

  }
}

/**
 * Helper function for Leap Motion Vectors
 */
function vectorToString(vector, digits) {
  if (typeof digits === "undefined") {
    digits = 1;
  }
  return "(" + vector[0].toFixed(digits) + ", "
             + vector[1].toFixed(digits) + ", "
             + vector[2].toFixed(digits) + ")";
}

/**
 * Self Calling Function to create THE GAME (did you just loose "the game"" again?)
 */
(()=>{
   new Pong(document.getElementById("canvas"));
})();
<|endoftext|>


<commit_before>module.exports = (responseFromWatson) => {

    //  All the intents
    const { intents } = responseFromWatson;
    // //  All the entities
    const { entites } = responseFromWatson;

    //  Take the highest matching intent
    const intent = intents.sort((first, second) => {
        if(first && second) return -(first.confidence - second.confidence);
        return 0;
    })[0];

    //  Return the object of intent and entities
    return { intent, entites };

};
<commit_msg>watson<commit_after>module.exports = (responseFromWatson) => {

    console.log(responseFromWatson);

    //  All the intents
    const { intents } = responseFromWatson;
    // //  All the entities
    const { entites } = responseFromWatson;

    //  Take the highest matching intent
    const intent = intents.sort((first, second) => {
        if(first && second) return -(first.confidence - second.confidence);
        return 0;
    })[0];

    //  Return the object of intent and entities
    return { intent, entites };

};
<|endoftext|>


<commit_before>var Adapter = require("../lib/ev3/adapter");
var runIfSp = require("./util").runIfSp;

suite("Adapter", function(){
  test("should get sensor data", runIfSp(function(done){
    var a = new Adapter(process.env.SERIAL_PORT);
    a.once("ready", function(){
      a.readSensor(1 /*port 1*/, 0, 10/*touch sensor*/, function(err, data){
        console.log("raw readsensor data:" + data);
        done();
      })
    })
  }))
});<commit_msg>tweaks to ev3 adapter tests<commit_after>var Adapter = require("../lib/ev3/adapter");
var runIfSp = require("./util").runIfSp;

suite("Adapter", function(){
  test("should get sensor data", runIfSp(function(done){
    var a = new Adapter(process.env.SERIAL_PORT);
    a.once("ready", function(){
      a.readSensor(1 /*port 1*/, 0, 10/*touch sensor*/, function(err, data){
        console.log("raw readsensor data:" + data);
        a.close(done);
      })
    })
  }));

  test("should be able to set motors", runIfSp(function(done){
    var a = new Adapter(process.env.SERIAL_PORT);
    a.once("ready", function(){
      a.setMotors(100, 100, 100, 100, function(err){
        if(err) throw err;
        a.close(done);
      })
    })
  }))
});<|endoftext|>


<commit_before>module.exports = Cmds.addCommand({
    cmds: [';info '],

    requires: {
        guild: true,
        loud: false,
    },

    desc: 'Get info about a user',

    args: '([@user] | [id] | [name])',

    example: 'vae',

    func: (cmd, args, msgObj, speaker, channel, guild) => {
        const target = Util.getEitherByMixed(args, guild);
        if (target == null) return Util.commandFailed(channel, speaker, 'User not found');

        const isMuted = Mutes.checkMuted(target.id, guild);
        const historyStr = Util.historyToString(Util.getHistory(target.id, guild));

        const createdAt = target.createdAt || target.user.createdAt;
        // const timeStr = `${Util.getYearStr(createdAt)}-${Util.getMonthStr(createdAt)}-${Util.getDayStr(createdAt)}`;
        const highestRole = Util.getHighestRole(target);
        const powerRating = `${Util.toFixedCut(Util.getPermRating(guild, target), 3)}%`;

        const guildMembers = Array.from(guild.members.values());
        guildMembers.sort((a, b) => a.joinedTimestamp - b.joinedTimestamp);
        const joinOrder = guildMembers.map(member => member.id);
        const joinPos = joinOrder.indexOf(target.id) + 1;
        const joinPosStr = `${Util.getSuffix(joinPos)} / ${guild.members.size}`;

        const joinedAtStr = (target.joinedAt != null ? Util.getDateString(target.joinedAt) : 'N/A');

        const sendEmbedFields = [];

        sendEmbedFields.push({ name: 'ID', value: target.id });
        sendEmbedFields.push({ name: 'Username', value: target.toString() });
        sendEmbedFields.push({ name: 'Nickname', value: (target.nickname != null ? Util.safe(target.nickname) : 'N/A') });
        sendEmbedFields.push({ name: 'Discriminator', value: target.discriminator });
        sendEmbedFields.push({ name: 'Staff', value: Util.capitalize(Util.checkStaff(guild, target)) });
        sendEmbedFields.push({ name: 'Rank', value: highestRole ? `${highestRole.name} (${highestRole.position})` : 'None' });
        sendEmbedFields.push({ name: 'Power', value: powerRating });
        sendEmbedFields.push({ name: 'Status', value: Util.capitalize(target.presence.status) });
        sendEmbedFields.push({ name: 'Bot', value: Util.capitalize(target.bot || target.user.bot) });
        sendEmbedFields.push({ name: 'Game', value: (target.presence.game != null) ? Util.capitalize(target.presence.game.name) : 'N/A' });
        sendEmbedFields.push({ name: 'Muted', value: Util.capitalize(isMuted) });
        sendEmbedFields.push({ name: 'Mute History', value: historyStr });
        sendEmbedFields.push({ name: 'Mic Muted', value: Util.capitalize(target.selfMute) });
        sendEmbedFields.push({ name: 'Deafened', value: Util.capitalize(target.selfDeaf) });
        sendEmbedFields.push({ name: 'Server Mic Muted', value: Util.capitalize(target.serverMute) });
        sendEmbedFields.push({ name: 'Server Deafened', value: Util.capitalize(target.serverDeaf) });
        sendEmbedFields.push({ name: 'Registered', value: (createdAt != null ? Util.getDateString(createdAt) : 'N/A') });
        sendEmbedFields.push({ name: `Joined ${guild.name}`, value: `${joinedAtStr} : ${joinPosStr}` });
        sendEmbedFields.push({ name: 'Avatar', value: Util.getAvatar(target, true) });

        // var newDesc = Util.getAvatar(target, true) // + "\n" + */("+").repeat(numSep);

        Util.sendEmbed(channel, 'User Info', null, Util.makeEmbedFooter(speaker), Util.getAvatar(target), 0x00E676, sendEmbedFields);

        return undefined;
    },
});
<commit_msg>Changed formatting<commit_after>module.exports = Cmds.addCommand({
    cmds: [';info '],

    requires: {
        guild: true,
        loud: false,
    },

    desc: 'Get info about a user',

    args: '([@user] | [id] | [name])',

    example: 'vae',

    func: (cmd, args, msgObj, speaker, channel, guild) => {
        const target = Util.getEitherByMixed(args, guild);
        if (target == null) return Util.commandFailed(channel, speaker, 'User not found');

        const isMuted = Mutes.checkMuted(target.id, guild);
        const historyStr = Util.historyToString(Util.getHistory(target.id, guild));

        const createdAt = target.createdAt || target.user.createdAt;
        // const timeStr = `${Util.getYearStr(createdAt)}-${Util.getMonthStr(createdAt)}-${Util.getDayStr(createdAt)}`;
        const highestRole = Util.getHighestRole(target);
        const powerRating = `${Util.toFixedCut(Util.getPermRating(guild, target), 3)}%`;

        const guildMembers = Array.from(guild.members.values());
        guildMembers.sort((a, b) => a.joinedTimestamp - b.joinedTimestamp);
        const joinOrder = guildMembers.map(member => member.id);
        const joinPos = joinOrder.indexOf(target.id) + 1;
        const joinPosStr = `${Util.getSuffix(joinPos)} (${guild.members.size} Total)`;

        const joinedAtStr = (target.joinedAt != null ? Util.getDateString(target.joinedAt) : 'N/A');

        const sendEmbedFields = [];

        sendEmbedFields.push({ name: 'ID', value: target.id });
        sendEmbedFields.push({ name: 'Username', value: target.toString() });
        sendEmbedFields.push({ name: 'Nickname', value: (target.nickname != null ? Util.safe(target.nickname) : 'N/A') });
        sendEmbedFields.push({ name: 'Discriminator', value: target.discriminator });
        sendEmbedFields.push({ name: 'Staff', value: Util.capitalize(Util.checkStaff(guild, target)) });
        sendEmbedFields.push({ name: 'Rank', value: highestRole ? `${highestRole.name} (${highestRole.position})` : 'None' });
        sendEmbedFields.push({ name: 'Power', value: powerRating });
        sendEmbedFields.push({ name: 'Status', value: Util.capitalize(target.presence.status) });
        sendEmbedFields.push({ name: 'Bot', value: Util.capitalize(target.bot || target.user.bot) });
        sendEmbedFields.push({ name: 'Game', value: (target.presence.game != null) ? Util.capitalize(target.presence.game.name) : 'N/A' });
        sendEmbedFields.push({ name: 'Muted', value: Util.capitalize(isMuted) });
        sendEmbedFields.push({ name: 'Mute History', value: historyStr });
        sendEmbedFields.push({ name: 'Mic Muted', value: Util.capitalize(target.selfMute) });
        sendEmbedFields.push({ name: 'Deafened', value: Util.capitalize(target.selfDeaf) });
        sendEmbedFields.push({ name: 'Server Mic Muted', value: Util.capitalize(target.serverMute) });
        sendEmbedFields.push({ name: 'Server Deafened', value: Util.capitalize(target.serverDeaf) });
        sendEmbedFields.push({ name: 'Registered', value: (createdAt != null ? Util.getDateString(createdAt) : 'N/A') });
        sendEmbedFields.push({ name: `Joined ${guild.name}`, value: `${joinedAtStr} | ${joinPosStr}` });
        sendEmbedFields.push({ name: 'Avatar', value: Util.getAvatar(target, true) });

        // var newDesc = Util.getAvatar(target, true) // + "\n" + */("+").repeat(numSep);

        Util.sendEmbed(channel, 'User Info', null, Util.makeEmbedFooter(speaker), Util.getAvatar(target), 0x00E676, sendEmbedFields);

        return undefined;
    },
});
<|endoftext|>


<commit_before>import React from 'react';
import messages from 'src/locale'
import style from './style.css'

import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
import Subheader from 'material-ui/Subheader';
import Toggle from 'material-ui/Toggle';

export default ({ active, discontinued, on_sale, stock_status,
  setActive, setDiscontinued, setOnSale, setStock }) => {
  return

    <Subheader>{messages.filter}</Subheader>

      <Toggle
        label={messages.products.onlyActive}
        onToggle={(e, value) => { setActive(value) }}
        toggled={active}
        className={style.toggle}
      />
      <Toggle
        label={messages.products.onlyDiscontinued}
        onToggle={(e, value) => { setDiscontinued(value) }}
        toggled={discontinued}
        className={style.toggle}
      />
      <Toggle
        label={messages.products.onlyOnSale}
        onToggle={(e, value) => { setOnSale(value) }}
        toggled={on_sale}
        className={style.toggle}
      />
      <SelectField
        value={stock_status}
        onChange={(event, index, value) => { setStock(value) }}
        floatingLabelText={messages.products.stockStatus}
        fullWidth={true}
      >
        <MenuItem value={"all"} primaryText={messages.all} />
        <MenuItem value={"available"} primaryText={messages.products.inStock} />
        <MenuItem value={"out_of_stock"} primaryText={messages.products.outOfStock} />
        <MenuItem value={"backorder"} primaryText={messages.products.backorder} />
        <MenuItem value={"preorder"} primaryText={messages.products.preorder} />
        <MenuItem value={"discontinued"} primaryText={messages.products.discontinued} />
      </SelectField>

}
<commit_msg>Restyle product filter<commit_after>import React from 'react';
import messages from 'src/locale'
import style from './style.css'

import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
import Toggle from 'material-ui/Toggle';

export default ({ active, discontinued, on_sale, stock_status,
  setActive, setDiscontinued, setOnSale, setStock }) => {
  return (

{messages.filter}

      <Toggle
        label={messages.products.onlyActive}
        onToggle={(e, value) => { setActive(value) }}
        toggled={active}
        className={style.toggle}
      />
      <Toggle
        label={messages.products.onlyDiscontinued}
        onToggle={(e, value) => { setDiscontinued(value) }}
        toggled={discontinued}
        className={style.toggle}
      />
      <Toggle
        label={messages.products.onlyOnSale}
        onToggle={(e, value) => { setOnSale(value) }}
        toggled={on_sale}
        className={style.toggle}
      />
      <SelectField
        value={stock_status}
        onChange={(event, index, value) => { setStock(value) }}
        floatingLabelText={messages.products.stockStatus}
        fullWidth={true}
      >
        <MenuItem value={"all"} primaryText={messages.all} />
        <MenuItem value={"available"} primaryText={messages.products.inStock} />
        <MenuItem value={"out_of_stock"} primaryText={messages.products.outOfStock} />
        <MenuItem value={"backorder"} primaryText={messages.products.backorder} />
        <MenuItem value={"preorder"} primaryText={messages.products.preorder} />
        <MenuItem value={"discontinued"} primaryText={messages.products.discontinued} />
      </SelectField>

  )
}
<|endoftext|>


<commit_before>chrome.runtime.sendMessage({}, function(response) {
  var tc = {
    settings: {
      speed: 1.0,           // default 1x
      resetSpeed: 1.0,      // default 1x
      speedStep: 0.1,       // default 0.1x
      fastSpeed: 1.8,       // default 1.8x
      rewindTime: 10,       // default 10s
      advanceTime: 10,      // default 10s
      resetKeyCode:  82,    // default: R
      slowerKeyCode: 83,    // default: S
      fasterKeyCode: 68,    // default: D
      rewindKeyCode: 90,    // default: Z
      advanceKeyCode: 88,   // default: X
      displayKeyCode: 86,   // default: V
      fastKeyCode: 71,      // default: G
      rememberSpeed: false, // default: false
      startHidden: false,   // default: false
      blacklist: `
        www.instagram.com
        twitter.com
        vine.co
        imgur.com
      `.replace(/^\s+|\s+$/gm,'')
    }
  };

  chrome.storage.sync.get(tc.settings, function(storage) {
    tc.settings.speed = Number(storage.speed);
    tc.settings.resetSpeed = Number(storage.resetSpeed);
    tc.settings.speedStep = Number(storage.speedStep);
    tc.settings.fastSpeed = Number(storage.fastSpeed);
    tc.settings.rewindTime = Number(storage.rewindTime);
    tc.settings.advanceTime = Number(storage.advanceTime);
    tc.settings.resetKeyCode = Number(storage.resetKeyCode);
    tc.settings.rewindKeyCode = Number(storage.rewindKeyCode);
    tc.settings.slowerKeyCode = Number(storage.slowerKeyCode);
    tc.settings.fasterKeyCode = Number(storage.fasterKeyCode);
    tc.settings.fastKeyCode = Number(storage.fastKeyCode);
    tc.settings.displayKeyCode = Number(storage.displayKeyCode);
    tc.settings.advanceKeyCode = Number(storage.advanceKeyCode);
    tc.settings.rememberSpeed = Boolean(storage.rememberSpeed);
    tc.settings.startHidden = Boolean(storage.startHidden);
    tc.settings.blacklist = String(storage.blacklist);

    initializeWhenReady(document);
  });

  var forEach = Array.prototype.forEach;

  function defineVideoController() {
    tc.videoController = function(target, parent) {
      if (target.dataset['vscid']) {
        return;
      }

      this.video = target;
      this.parent = target.parentElement || parent;
      this.document = target.ownerDocument;
      this.id = Math.random().toString(36).substr(2, 9);
      if (!tc.settings.rememberSpeed) {
        tc.settings.speed = 1.0;
        tc.settings.resetSpeed = tc.settings.fastSpeed;
      }
      this.initializeControls();

      target.addEventListener('play', function(event) {
        target.playbackRate = tc.settings.speed;
      });

      target.addEventListener('ratechange', function(event) {
        // Ignore ratechange events on unitialized videos.
        // 0 == No information is available about the media resource.
        if (event.target.readyState > 0) {
          var speed = this.getSpeed();
          this.speedIndicator.textContent = speed;
          tc.settings.speed = speed;
          chrome.storage.sync.set({'speed': speed}, function() {
            console.log('Speed setting saved: ' + speed);
          });
        }
      }.bind(this));

      target.playbackRate = tc.settings.speed;
    };

    tc.videoController.prototype.getSpeed = function() {
      return parseFloat(this.video.playbackRate).toFixed(2);
    }

    tc.videoController.prototype.remove = function() {
      this.parentElement.removeChild(this);
    }

    tc.videoController.prototype.initializeControls = function() {
      var document = this.document;
      var speed = parseFloat(tc.settings.speed).toFixed(2),
        top = Math.max(this.video.offsetTop, 0) + "px",
        left = Math.max(this.video.offsetLeft, 0) + "px";

      var prevent = function(e) {
        e.preventDefault();
        e.stopPropagation();
      }

      var wrapper = document.createElement('div');
      wrapper.classList.add('vsc-controller');
      wrapper.dataset['vscid'] = this.id;
      wrapper.addEventListener('dblclick', prevent, true);
      wrapper.addEventListener('mousedown', prevent, true);
      wrapper.addEventListener('click', prevent, true);

      if (tc.settings.startHidden) {
        wrapper.classList.add('vsc-hidden');
      }

      var shadow = wrapper.createShadowRoot();
      var shadowTemplate = `

          ${speed}

            <button data-action="rewind" class="rw">«</button>
            <button data-action="slower">-</button>
            <button data-action="faster">+</button>
            <button data-action="advance" class="rw">»</button>
            <button data-action="display" class="hideButton">x</button>

      `;
      shadow.innerHTML = shadowTemplate;
      shadow.querySelector('.draggable').addEventListener('mousedown', (e) => {
        runAction(e.target.dataset['action'], document, false, e);
      });

      forEach.call(shadow.querySelectorAll('button'), function(button) {
        button.onclick = (e) => {
          runAction(e.target.dataset['action'], document, false, e);
        }
      });

      this.speedIndicator = shadow.querySelector('span');
      var fragment = document.createDocumentFragment();
      fragment.appendChild(wrapper);

      this.video.classList.add('vsc-initialized');
      this.video.dataset['vscid'] = this.id;

      switch (true) {
        case (location.hostname == 'www.amazon.com'):
        case (/hbogo\./).test(location.hostname):
          // insert before parent to bypass overlay
          this.parent.parentElement.insertBefore(fragment, this.parent);
          break;

        default:
          // Note: when triggered via a MutationRecord, it's possible that the
          // target is not the immediate parent. This appends the controller as
          // the first element of the target, which may not be the parent.
          this.parent.insertBefore(fragment, this.parent.firstChild);
      }
    }
  }

  function initializeWhenReady(document) {
    escapeStringRegExp.matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
    function escapeStringRegExp(str) {
      return str.replace(escapeStringRegExp.matchOperatorsRe, '\\$&');
    }

    var blacklisted = false;
    tc.settings.blacklist.split("\n").forEach(match => {
      match = match.replace(/^\s+|\s+$/g,'')
      if (match.length == 0) {
        return;
      }

      var regexp = new RegExp(escapeStringRegExp(match));
      if (regexp.test(location.href)) {
        blacklisted = true;
        return;
      }
    })

    if (blacklisted)
      return;

    window.onload = () => {
      initializeNow(window.document)
    };
    if (document && document.doctype && document.doctype.name == "html") {
      if (document.readyState === "complete") {
        initializeNow(document);
      } else {
        document.onreadystatechange = () => {
          if (document.readyState === "complete") {
            initializeNow(document);
          }
        }
      }
    }
  }

  function initializeNow(document) {
      // enforce init-once due to redundant callers
      if (document.body.classList.contains('vsc-initialized')) {
        return;
      }
      document.body.classList.add('vsc-initialized');

      if (document === window.document) {
        defineVideoController();
      } else {
        var link = document.createElement('link');
        link.href = chrome.runtime.getURL('inject.css');
        link.type = 'text/css';
        link.rel = 'stylesheet';
        document.head.appendChild(link);
      }

      document.addEventListener('keydown', function(event) {
        var keyCode = event.keyCode;

        // Ignore if following modifier is active.
        if (!event.getModifierState
            || event.getModifierState("Alt")
            || event.getModifierState("Control")
            || event.getModifierState("Fn")
            || event.getModifierState("Meta")
            || event.getModifierState("Hyper")
            || event.getModifierState("OS")) {
          return;
        }

        // Ignore keydown event if typing in an input box
        if ((document.activeElement.nodeName === 'INPUT'
              && document.activeElement.getAttribute('type') === 'text')
            || document.activeElement.nodeName === 'TEXTAREA'
            || document.activeElement.isContentEditable) {
          return false;
        }

        if (keyCode == tc.settings.rewindKeyCode) {
          runAction('rewind', document, true)
        } else if (keyCode == tc.settings.advanceKeyCode) {
          runAction('advance', document, true)
        } else if (keyCode == tc.settings.fasterKeyCode) {
          runAction('faster', document, true)
        } else if (keyCode == tc.settings.slowerKeyCode) {
          runAction('slower', document, true)
        } else if (keyCode == tc.settings.resetKeyCode) {
          runAction('reset', document, true)
        } else if (keyCode == tc.settings.displayKeyCode) {
          runAction('display', document, true)
        } else if (keyCode == tc.settings.fastKeyCode) {
          runAction('fast', document, true);
        }

        return false;
      }, true);

      function checkForVideo(node, parent, added) {
        if (node.nodeName === 'VIDEO') {
          if (added) {
            new tc.videoController(node, parent);
          } else {
            if (node.classList.contains('vsc-initialized')) {
              let id = node.dataset['vscid'];
              let ctrl = document.querySelector(`div[data-vscid="${id}"]`)
              if (ctrl) {
                ctrl.remove();
              }
              node.classList.remove('vsc-initialized');
              delete node.dataset['vscid'];
            }
          }
        } else if (node.children != undefined) {
          for (var i = 0; i < node.children.length; i++) {
            const child = node.children[i];
            checkForVideo(child, child.parentNode || parent, added);
          }
        }
      }

      var observer = new MutationObserver(function(mutations) {
        // Process the DOM nodes lazily
        requestIdleCallback(_ => {
          mutations.forEach(function(mutation) {
            forEach.call(mutation.addedNodes, function(node) {
              if (typeof node === "function")
                return;
              checkForVideo(node, node.parentNode || mutation.target, true);
            });
            forEach.call(mutation.removedNodes, function(node) {
              if (typeof node === "function")
                return;
              checkForVideo(node, node.parentNode || mutation.target, false);
            });
          });
        }, {timeout: 1000});
      });
      observer.observe(document, { childList: true, subtree: true });

      var videoTags = document.getElementsByTagName('video');
      forEach.call(videoTags, function(video) {
        new tc.videoController(video);
      });

      var frameTags = document.getElementsByTagName('iframe');
      forEach.call(frameTags, function(frame) {
        // Ignore frames we don't have permission to access (different origin).
        try { var childDocument = frame.contentDocument } catch (e) { return }
        initializeWhenReady(childDocument);
      });
  }

  function runAction(action, document, keyboard, e) {
    var videoTags = document.getElementsByTagName('video');
    videoTags.forEach = Array.prototype.forEach;

    videoTags.forEach(function(v) {
      var id = v.dataset['vscid'];
      var controller = document.querySelector(`div[data-vscid="${id}"]`);

      showController(controller);

      if (!v.classList.contains('vsc-cancelled')) {
        if (action === 'rewind') {
          v.currentTime -= tc.settings.rewindTime;
        } else if (action === 'advance') {
          v.currentTime += tc.settings.advanceTime;
        } else if (action === 'faster') {
          // Maximum playback speed in Chrome is set to 16:
          // https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/html/media/HTMLMediaElement.cpp?l=168
          var s = Math.min( (v.playbackRate < 0.1 ? 0.0 : v.playbackRate) + tc.settings.speedStep, 16);
          v.playbackRate = Number(s.toFixed(2));
        } else if (action === 'slower') {
          // Video min rate is 0.0625:
          // https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/html/media/HTMLMediaElement.cpp?l=167
          var s = Math.max(v.playbackRate - tc.settings.speedStep, 0.07);
          v.playbackRate = Number(s.toFixed(2));
        } else if (action === 'reset') {
          resetSpeed(v, 1.0);
        } else if (action === 'display') {
          controller.classList.add('vsc-manual');
          controller.classList.toggle('vsc-hidden');
        } else if (action === 'drag') {
          handleDrag(v, controller, e);
        } else if (action === 'fast') {
          resetSpeed(v, tc.settings.fastSpeed);
        }
      }
    });
  }

  function resetSpeed(v, target) {
    if (v.playbackRate === target) {
      if(v.playbackRate === tc.settings.resetSpeed)
      {
        if (target !== 1.0) {
          v.playbackRate = 1.0;
        } else {
          v.playbackRate = tc.settings.fastSpeed;
        }
      }
      else
      {
        v.playbackRate = tc.settings.resetSpeed;
      }
    } else {
      tc.settings.resetSpeed = v.playbackRate;
      chrome.storage.sync.set({'resetSpeed': v.playbackRate});
      v.playbackRate = target;
    }
  }

  function handleDrag(video, controller, e) {
    const shadowController = controller.shadowRoot.querySelector('#controller');

    // Find nearest parent of same size as video parent.
    var parentElement = controller.parentElement;
    while (parentElement.parentNode &&
      parentElement.parentNode.offsetHeight === parentElement.offsetHeight &&
      parentElement.parentNode.offsetWidth === parentElement.offsetWidth) {
      parentElement = parentElement.parentNode;
    }

    video.classList.add('vcs-dragging');
    shadowController.classList.add('dragging');

    const initialMouseXY = [e.clientX, e.clientY];
    const initialControllerXY = [
      parseInt(shadowController.style.left),
      parseInt(shadowController.style.top)
    ];

    const startDragging = (e) => {
      let style = shadowController.style;
      let dx = e.clientX - initialMouseXY[0];
      let dy = e.clientY -initialMouseXY[1];
      style.left = (initialControllerXY[0] + dx) + 'px';
      style.top  = (initialControllerXY[1] + dy) + 'px';
    }

    const stopDragging = () => {
      parentElement.removeEventListener('mousemove', startDragging);
      parentElement.removeEventListener('mouseup', stopDragging);
      parentElement.removeEventListener('mouseleave', stopDragging);

      shadowController.classList.remove('dragging');
      video.classList.remove('vcs-dragging');
    }

    parentElement.addEventListener('mouseup',stopDragging);
    parentElement.addEventListener('mouseleave',stopDragging);
    parentElement.addEventListener('mousemove', startDragging);
  }

  var timer;
  var animation = false;
  function showController(controller) {
    controller.classList.add('vcs-show');

    if (animation)
      clearTimeout(timer);

    animation = true;
    timer = setTimeout(function() {
      controller.classList.remove('vcs-show');
      animation = false;
    }, 2000);
  }
});
<commit_msg>Fix for #326 (#327)<commit_after>chrome.runtime.sendMessage({}, function(response) {
  var tc = {
    settings: {
      speed: 1.0,           // default 1x
      resetSpeed: 1.0,      // default 1x
      speedStep: 0.1,       // default 0.1x
      fastSpeed: 1.8,       // default 1.8x
      rewindTime: 10,       // default 10s
      advanceTime: 10,      // default 10s
      resetKeyCode:  82,    // default: R
      slowerKeyCode: 83,    // default: S
      fasterKeyCode: 68,    // default: D
      rewindKeyCode: 90,    // default: Z
      advanceKeyCode: 88,   // default: X
      displayKeyCode: 86,   // default: V
      fastKeyCode: 71,      // default: G
      rememberSpeed: false, // default: false
      startHidden: false,   // default: false
      blacklist: `
        www.instagram.com
        twitter.com
        vine.co
        imgur.com
      `.replace(/^\s+|\s+$/gm,'')
    }
  };

  chrome.storage.sync.get(tc.settings, function(storage) {
    tc.settings.speed = Number(storage.speed);
    tc.settings.resetSpeed = Number(storage.resetSpeed);
    tc.settings.speedStep = Number(storage.speedStep);
    tc.settings.fastSpeed = Number(storage.fastSpeed);
    tc.settings.rewindTime = Number(storage.rewindTime);
    tc.settings.advanceTime = Number(storage.advanceTime);
    tc.settings.resetKeyCode = Number(storage.resetKeyCode);
    tc.settings.rewindKeyCode = Number(storage.rewindKeyCode);
    tc.settings.slowerKeyCode = Number(storage.slowerKeyCode);
    tc.settings.fasterKeyCode = Number(storage.fasterKeyCode);
    tc.settings.fastKeyCode = Number(storage.fastKeyCode);
    tc.settings.displayKeyCode = Number(storage.displayKeyCode);
    tc.settings.advanceKeyCode = Number(storage.advanceKeyCode);
    tc.settings.rememberSpeed = Boolean(storage.rememberSpeed);
    tc.settings.startHidden = Boolean(storage.startHidden);
    tc.settings.blacklist = String(storage.blacklist);

    initializeWhenReady(document);
  });

  var forEach = Array.prototype.forEach;

  function defineVideoController() {
    tc.videoController = function(target, parent) {
      if (target.dataset['vscid']) {
        return;
      }

      this.video = target;
      this.parent = target.parentElement || parent;
      this.document = target.ownerDocument;
      this.id = Math.random().toString(36).substr(2, 9);
      if (!tc.settings.rememberSpeed) {
        tc.settings.speed = 1.0;
        tc.settings.resetSpeed = tc.settings.fastSpeed;
      }
      this.initializeControls();

      target.addEventListener('play', function(event) {
        target.playbackRate = tc.settings.speed;
      });

      target.addEventListener('ratechange', function(event) {
        // Ignore ratechange events on unitialized videos.
        // 0 == No information is available about the media resource.
        if (event.target.readyState > 0) {
          var speed = this.getSpeed();
          this.speedIndicator.textContent = speed;
          tc.settings.speed = speed;
          chrome.storage.sync.set({'speed': speed}, function() {
            console.log('Speed setting saved: ' + speed);
          });
        }
      }.bind(this));

      target.playbackRate = tc.settings.speed;
    };

    tc.videoController.prototype.getSpeed = function() {
      return parseFloat(this.video.playbackRate).toFixed(2);
    }

    tc.videoController.prototype.remove = function() {
      this.parentElement.removeChild(this);
    }

    tc.videoController.prototype.initializeControls = function() {
      var document = this.document;
      var speed = parseFloat(tc.settings.speed).toFixed(2),
        top = Math.max(this.video.offsetTop, 0) + "px",
        left = Math.max(this.video.offsetLeft, 0) + "px";

      var prevent = function(e) {
        e.preventDefault();
        e.stopPropagation();
      }

      var wrapper = document.createElement('div');
      wrapper.classList.add('vsc-controller');
      wrapper.dataset['vscid'] = this.id;
      wrapper.addEventListener('dblclick', prevent, true);
      wrapper.addEventListener('mousedown', prevent, true);
      wrapper.addEventListener('click', prevent, true);

      if (tc.settings.startHidden) {
        wrapper.classList.add('vsc-hidden');
      }

      var shadow = wrapper.createShadowRoot();
      var shadowTemplate = `

          ${speed}

            <button data-action="rewind" class="rw">«</button>
            <button data-action="slower">-</button>
            <button data-action="faster">+</button>
            <button data-action="advance" class="rw">»</button>
            <button data-action="display" class="hideButton">x</button>

      `;
      shadow.innerHTML = shadowTemplate;
      shadow.querySelector('.draggable').addEventListener('mousedown', (e) => {
        runAction(e.target.dataset['action'], document, false, e);
      });

      forEach.call(shadow.querySelectorAll('button'), function(button) {
        button.onclick = (e) => {
          runAction(e.target.dataset['action'], document, false, e);
        }
      });

      this.speedIndicator = shadow.querySelector('span');
      var fragment = document.createDocumentFragment();
      fragment.appendChild(wrapper);

      this.video.classList.add('vsc-initialized');
      this.video.dataset['vscid'] = this.id;

      switch (true) {
        case (location.hostname == 'www.amazon.com'):
        case (/hbogo\./).test(location.hostname):
          // insert before parent to bypass overlay
          this.parent.parentElement.insertBefore(fragment, this.parent);
          break;

        default:
          // Note: when triggered via a MutationRecord, it's possible that the
          // target is not the immediate parent. This appends the controller as
          // the first element of the target, which may not be the parent.
          this.parent.insertBefore(fragment, this.parent.firstChild);
      }
    }
  }

  function initializeWhenReady(document) {
    escapeStringRegExp.matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
    function escapeStringRegExp(str) {
      return str.replace(escapeStringRegExp.matchOperatorsRe, '\\$&');
    }

    var blacklisted = false;
    tc.settings.blacklist.split("\n").forEach(match => {
      match = match.replace(/^\s+|\s+$/g,'')
      if (match.length == 0) {
        return;
      }

      var regexp = new RegExp(escapeStringRegExp(match));
      if (regexp.test(location.href)) {
        blacklisted = true;
        return;
      }
    })

    if (blacklisted)
      return;

    window.onload = () => {
      initializeNow(window.document)
    };
    if (document) {
      if (document.readyState === "complete") {
        initializeNow(document);
      } else {
        document.onreadystatechange = () => {
          if (document.readyState === "complete") {
            initializeNow(document);
          }
        }
      }
    }
  }

  function initializeNow(document) {
      // enforce init-once due to redundant callers
      if (!document.body || document.body.classList.contains('vsc-initialized')) {
        return;
      }
      document.body.classList.add('vsc-initialized');

      if (document === window.document) {
        defineVideoController();
      } else {
        var link = document.createElement('link');
        link.href = chrome.runtime.getURL('inject.css');
        link.type = 'text/css';
        link.rel = 'stylesheet';
        document.head.appendChild(link);
      }

      document.addEventListener('keydown', function(event) {
        var keyCode = event.keyCode;

        // Ignore if following modifier is active.
        if (!event.getModifierState
            || event.getModifierState("Alt")
            || event.getModifierState("Control")
            || event.getModifierState("Fn")
            || event.getModifierState("Meta")
            || event.getModifierState("Hyper")
            || event.getModifierState("OS")) {
          return;
        }

        // Ignore keydown event if typing in an input box
        if ((document.activeElement.nodeName === 'INPUT'
              && document.activeElement.getAttribute('type') === 'text')
            || document.activeElement.nodeName === 'TEXTAREA'
            || document.activeElement.isContentEditable) {
          return false;
        }

        if (keyCode == tc.settings.rewindKeyCode) {
          runAction('rewind', document, true)
        } else if (keyCode == tc.settings.advanceKeyCode) {
          runAction('advance', document, true)
        } else if (keyCode == tc.settings.fasterKeyCode) {
          runAction('faster', document, true)
        } else if (keyCode == tc.settings.slowerKeyCode) {
          runAction('slower', document, true)
        } else if (keyCode == tc.settings.resetKeyCode) {
          runAction('reset', document, true)
        } else if (keyCode == tc.settings.displayKeyCode) {
          runAction('display', document, true)
        } else if (keyCode == tc.settings.fastKeyCode) {
          runAction('fast', document, true);
        }

        return false;
      }, true);

      function checkForVideo(node, parent, added) {
        if (node.nodeName === 'VIDEO') {
          if (added) {
            new tc.videoController(node, parent);
          } else {
            if (node.classList.contains('vsc-initialized')) {
              let id = node.dataset['vscid'];
              let ctrl = document.querySelector(`div[data-vscid="${id}"]`)
              if (ctrl) {
                ctrl.remove();
              }
              node.classList.remove('vsc-initialized');
              delete node.dataset['vscid'];
            }
          }
        } else if (node.children != undefined) {
          for (var i = 0; i < node.children.length; i++) {
            const child = node.children[i];
            checkForVideo(child, child.parentNode || parent, added);
          }
        }
      }

      var observer = new MutationObserver(function(mutations) {
        // Process the DOM nodes lazily
        requestIdleCallback(_ => {
          mutations.forEach(function(mutation) {
            forEach.call(mutation.addedNodes, function(node) {
              if (typeof node === "function")
                return;
              checkForVideo(node, node.parentNode || mutation.target, true);
            });
            forEach.call(mutation.removedNodes, function(node) {
              if (typeof node === "function")
                return;
              checkForVideo(node, node.parentNode || mutation.target, false);
            });
          });
        }, {timeout: 1000});
      });
      observer.observe(document, { childList: true, subtree: true });

      var videoTags = document.getElementsByTagName('video');
      forEach.call(videoTags, function(video) {
        new tc.videoController(video);
      });

      var frameTags = document.getElementsByTagName('iframe');
      forEach.call(frameTags, function(frame) {
        // Ignore frames we don't have permission to access (different origin).
        try { var childDocument = frame.contentDocument } catch (e) { return }
        initializeWhenReady(childDocument);
      });
  }

  function runAction(action, document, keyboard, e) {
    var videoTags = document.getElementsByTagName('video');
    videoTags.forEach = Array.prototype.forEach;

    videoTags.forEach(function(v) {
      var id = v.dataset['vscid'];
      var controller = document.querySelector(`div[data-vscid="${id}"]`);

      showController(controller);

      if (!v.classList.contains('vsc-cancelled')) {
        if (action === 'rewind') {
          v.currentTime -= tc.settings.rewindTime;
        } else if (action === 'advance') {
          v.currentTime += tc.settings.advanceTime;
        } else if (action === 'faster') {
          // Maximum playback speed in Chrome is set to 16:
          // https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/html/media/HTMLMediaElement.cpp?l=168
          var s = Math.min( (v.playbackRate < 0.1 ? 0.0 : v.playbackRate) + tc.settings.speedStep, 16);
          v.playbackRate = Number(s.toFixed(2));
        } else if (action === 'slower') {
          // Video min rate is 0.0625:
          // https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/html/media/HTMLMediaElement.cpp?l=167
          var s = Math.max(v.playbackRate - tc.settings.speedStep, 0.07);
          v.playbackRate = Number(s.toFixed(2));
        } else if (action === 'reset') {
          resetSpeed(v, 1.0);
        } else if (action === 'display') {
          controller.classList.add('vsc-manual');
          controller.classList.toggle('vsc-hidden');
        } else if (action === 'drag') {
          handleDrag(v, controller, e);
        } else if (action === 'fast') {
          resetSpeed(v, tc.settings.fastSpeed);
        }
      }
    });
  }

  function resetSpeed(v, target) {
    if (v.playbackRate === target) {
      if(v.playbackRate === tc.settings.resetSpeed)
      {
        if (target !== 1.0) {
          v.playbackRate = 1.0;
        } else {
          v.playbackRate = tc.settings.fastSpeed;
        }
      }
      else
      {
        v.playbackRate = tc.settings.resetSpeed;
      }
    } else {
      tc.settings.resetSpeed = v.playbackRate;
      chrome.storage.sync.set({'resetSpeed': v.playbackRate});
      v.playbackRate = target;
    }
  }

  function handleDrag(video, controller, e) {
    const shadowController = controller.shadowRoot.querySelector('#controller');

    // Find nearest parent of same size as video parent.
    var parentElement = controller.parentElement;
    while (parentElement.parentNode &&
      parentElement.parentNode.offsetHeight === parentElement.offsetHeight &&
      parentElement.parentNode.offsetWidth === parentElement.offsetWidth) {
      parentElement = parentElement.parentNode;
    }

    video.classList.add('vcs-dragging');
    shadowController.classList.add('dragging');

    const initialMouseXY = [e.clientX, e.clientY];
    const initialControllerXY = [
      parseInt(shadowController.style.left),
      parseInt(shadowController.style.top)
    ];

    const startDragging = (e) => {
      let style = shadowController.style;
      let dx = e.clientX - initialMouseXY[0];
      let dy = e.clientY -initialMouseXY[1];
      style.left = (initialControllerXY[0] + dx) + 'px';
      style.top  = (initialControllerXY[1] + dy) + 'px';
    }

    const stopDragging = () => {
      parentElement.removeEventListener('mousemove', startDragging);
      parentElement.removeEventListener('mouseup', stopDragging);
      parentElement.removeEventListener('mouseleave', stopDragging);

      shadowController.classList.remove('dragging');
      video.classList.remove('vcs-dragging');
    }

    parentElement.addEventListener('mouseup',stopDragging);
    parentElement.addEventListener('mouseleave',stopDragging);
    parentElement.addEventListener('mousemove', startDragging);
  }

  var timer;
  var animation = false;
  function showController(controller) {
    controller.classList.add('vcs-show');

    if (animation)
      clearTimeout(timer);

    animation = true;
    timer = setTimeout(function() {
      controller.classList.remove('vcs-show');
      animation = false;
    }, 2000);
  }
});
<|endoftext|>