<commit_before>/*global config*/
import Model from './Model';
import BuildStates from '../constants/BuildStates';
import utf8 from 'utf8';
import {getByteLength} from '../utils/logHelpers';
import {rest, initial, first, last, find, compact} from 'underscore';

class Log extends Model {

  constructor(options) {
    this.init(options);
    super(options);
  }

  init(options) {
    this.logLines = [];
    this.fetchCount = 0;
    this.baseRequestLength = config.offsetLength;
    this.requestOffset = this.getMaxOffset(options.size);
    this.lengthOverride = false;
    this.shouldPoll = options.buildState === BuildStates.IN_PROGRESS;
    this.currentOffsetLine = null;
    // keep track of if we have loaded the beginning or the end of the log
    this.maxOffsetLoaded = options.size;
    this.minOffsetLoaded = this.requestOffset;
  }

  updateLogForNavigationChange(options) {
    this.init(options);
    this.requestOffset = options.position === 'top' ? 0 : this.getMaxOffset(options.size);
    this.fetchAction = options.position;
    this.maxOffsetLoaded = options.position === 'top' ? 0 : options.size;

    return this;
  }

  url() {
    if (this.requestOffset === -1) {
      console.warn('requestOffset not properly set');
      return;
    }
    return `${config.apiRoot}/modules/builds/${this.options.buildId}/log?offset=${this.requestOffset}&length=${this.lengthOverride || this.baseRequestLength}`;
  }

  getMaxOffset(size) {
    return Math.max(size - this.baseRequestLength, 0);
  }

  parseInactiveBuild() {
    // First fetch or navigated 'To Bottom'
    if (!this.fetchAction || this.fetchAction === 'bottom') {
      this.handleEndOfLogFetch();
    }

    // Navigated 'To Top'
    else if (this.fetchAction === 'top') {
      this.handleToTopFetch();
    }

    // Scrolling Down
    else if (this.fetchAction === 'next') {
      this.handleNextFetch();
    }

    // Scrolling up
    else if (this.fetchAction === 'previous') {
      this.handlePreviousFetch();
    }

    else {
      console.warn('Parse condition not met. ', this);
    }
  }

  parseActiveBuild() {
    // Scrolling up
    if (this.fetchAction === 'previous') {
      this.handlePreviousFetch();
    }

    else {
      if (this.fetchCount === 1) {
        this.handleFirstActiveFetch();
      }

      else {
        this.handleMoreActiveFetch();
      }
    }
  }

  parse() {
    this.data = this.raw;
    this.fetchCount++;
    this.fetchTimestamp = Date.now();
    this.maxOffsetLoaded = Math.max(this.data.nextOffset, this.maxOffsetLoaded);
    this.minOffsetLoaded = Math.min(this.requestOffset, this.minOffsetLoaded);
    this.buildInProgress = this.options.buildState === BuildStates.IN_PROGRESS;
    this.cancelledBuild = this.options.buildState === BuildStates.CANCELLED;
    this.endOfLogLoaded = this.maxOffsetLoaded === this.options.size;
    this.newLogLines = this.formatLog();

    if (this.buildInProgress) {
      this.parseActiveBuild();
    }

    else {
      this.parseInactiveBuild();
    }
  }

  // active build helpers
  handleFirstActiveFetch() {
    // we already have at least 1 offset
    if (this.requestOffset > 0) {
      // save and remove incomplete first line so we can append it to the
      // incomplete last line of the next fetch if we continue to scroll up
      this.firstLine = first(this.newLogLines);
      this.newLogLines = rest(this.newLogLines);
    }
    // our log is larger than at least 1 offset
    // e.g. we used the "To Top" button
    else if (this.options.size > this.baseRequestLength) {
      // save and chop off last incomplete line
      this.lastLine = last(this.newLogLines);
      this.newLogLines = initial(this.newLogLines);
    }

    this.logLines = this.newLogLines;
  }

  handleMoreActiveFetch() {
    // if we are tailing/actively polling
    if (this.shouldPoll) {
      // if our next offset is offsetLength, we need to
      // do some chopping as we may have incomplete lines
      if (this.data.nextOffset - this.data.offset >= this.baseRequestLength) {
        // cutoff last line and save it
        this.lastLine = last(this.newLogLines);
        this.newLogLines = initial(this.newLogLines);
      }
      // as long as our offset is less than offsetLength
      // we dont need to worry about lines being cutoff
      this.logLines = [...this.logLines, ...this.newLogLines];
    }

    // if we are not polling
    else if (!this.shouldPoll) {
      this.parseNextFetch();
    }
  }

  handleEndOfLogFetch() {
    // we already have at least 1 offset
    if (this.requestOffset > 0) {
      // save and remove incomplete first line so we can append it to
      // the incomplete last line of the next fetch if we scroll up
      this.firstLine = first(this.newLogLines);
      this.newLogLines = rest(this.newLogLines);
    }

    if (this.options.buildState === BuildStates.IN_PROGRESS) {

    }

    this.logLines = [...this.logLines, ...this.newLogLines];
  }

  handleNextFetch() {
    this.parseNextFetch();
  }

  handlePreviousFetch() {
    this.parsePreviousFetch({
      removeFirstLine: this.requestOffset > 0
    });
  }

  handleToTopFetch() {
    // save and remove incomplete last line so we can prepend it to
    // the incomplete last line of the next fetch if we scroll up
    this.lastLine = last(this.newLogLines);
    this.newLogLines = initial(this.newLogLines);
    this.logLines = this.newLogLines;
  }

  // Helper for parse, used to manage incomplete lines when scrolling up
  parseNextFetch(options = {}) {
    // save incomplete last line so we can prepend it to the
    // incomplete first line of the next fetch if we continue to scroll down
    const tempLast = last(this.newLogLines);
    // chop off last incomplete line, as long as we have more to fetch
    if (!this.endOfLogLoaded) {
      this.newLogLines = initial(this.newLogLines);
    }
    // prepend the rest of the first line that was cutoff last fetch
    if (this.lastLine) {
      this.newLogLines[0].text = this.lastLine.text + this.newLogLines[0].text;
    }
    this.lastLine = tempLast;
    this.logLines = [...this.logLines, ...this.newLogLines];
  }

  // Helper for parse, used to manage incomplete lines when scrolling down
  parsePreviousFetch(options = {}) {
    let {removeFirstLine} = options;
    // save incomplete first line so we can append it to the
    // incomplete last line of the next fetch if we continue to scroll up
    const tempFirst = first(this.newLogLines);
    // remove the first line which may be incomplete
    // as long as we are not at the beginning of the log
    if (this.requestOffset !== 0 || removeFirstLine) {
      this.newLogLines = rest(this.newLogLines);
    }
    // append extra text to last log line that is incomplete
    this.newLogLines[this.newLogLines.length - 1].text = this.newLogLines[this.newLogLines.length - 1].text + this.firstLine.text;
    // prepend new logLines to existing logLines
    this.firstLine = tempFirst;

    this.logLines = [...this.newLogLines, ...this.logLines];
  }

  fetchPrevious() {
    this.fetchAction = 'previous';

    this.requestOffset = Math.max(this.minOffsetLoaded - this.baseRequestLength, 0);
    // check if we have any overlap if scrolling up
    if (this.requestOffset === 0 && this.minOffsetLoaded < this.baseRequestLength) {
      this.lengthOverride = this.minOffsetLoaded;
    }
    return this.fetch();
  }

  fetchNext() {
    this.fetchAction = 'next';
    this.requestOffset = this.data.nextOffset;
    return this.fetch();
  }

  formatLog() {
    const WHITE_SPACE = /^\s*$/;
    const NEW_LINE = '\n';
    const logData = this.data.data;

    if (logData.match(WHITE_SPACE)) {
      return [];
    }

    if (logData.length === 0) {
      return [];
    }

    let offsetRunningTotal = this.requestOffset;

    const splitLines = logData.split(NEW_LINE);

    const formattedLogLines = splitLines.map((line, i) => {
      // store second line because we may chop off the first
      if (i === 1) {
        this.pastOffsetLine = this.currentOffsetLine;
        this.currentOffsetLine = offsetRunningTotal + getByteLength(line);
      }

      if (i === splitLines.length - 1) {
        this.lastOffsetLine = offsetRunningTotal + getByteLength(length);
      }

      if (line.length === 0) {
        return false;
      }

      try {
        line = utf8.decode(line);
      }

      catch (err) {
        // doesn't matter
      }

      return {
        text: line,
        offset: offsetRunningTotal += getByteLength(line)
      };

    });

    return compact(formattedLogLines);

  }

}

export default Log;
<commit_msg>log a warning if we couldn't decode<commit_after>/*global config*/
import Model from './Model';
import BuildStates from '../constants/BuildStates';
import utf8 from 'utf8';
import {getByteLength} from '../utils/logHelpers';
import {rest, initial, first, last, find, compact} from 'underscore';

class Log extends Model {

  constructor(options) {
    this.init(options);
    super(options);
  }

  init(options) {
    this.logLines = [];
    this.fetchCount = 0;
    this.baseRequestLength = config.offsetLength;
    this.requestOffset = this.getMaxOffset(options.size);
    this.lengthOverride = false;
    this.shouldPoll = options.buildState === BuildStates.IN_PROGRESS;
    this.currentOffsetLine = null;
    // keep track of if we have loaded the beginning or the end of the log
    this.maxOffsetLoaded = options.size;
    this.minOffsetLoaded = this.requestOffset;
  }

  updateLogForNavigationChange(options) {
    this.init(options);
    this.requestOffset = options.position === 'top' ? 0 : this.getMaxOffset(options.size);
    this.fetchAction = options.position;
    this.maxOffsetLoaded = options.position === 'top' ? 0 : options.size;

    return this;
  }

  url() {
    if (this.requestOffset === -1) {
      console.warn('requestOffset not properly set');
      return;
    }
    return `${config.apiRoot}/modules/builds/${this.options.buildId}/log?offset=${this.requestOffset}&length=${this.lengthOverride || this.baseRequestLength}`;
  }

  getMaxOffset(size) {
    return Math.max(size - this.baseRequestLength, 0);
  }

  parseInactiveBuild() {
    // First fetch or navigated 'To Bottom'
    if (!this.fetchAction || this.fetchAction === 'bottom') {
      this.handleEndOfLogFetch();
    }

    // Navigated 'To Top'
    else if (this.fetchAction === 'top') {
      this.handleToTopFetch();
    }

    // Scrolling Down
    else if (this.fetchAction === 'next') {
      this.handleNextFetch();
    }

    // Scrolling up
    else if (this.fetchAction === 'previous') {
      this.handlePreviousFetch();
    }

    else {
      console.warn('Parse condition not met. ', this);
    }
  }

  parseActiveBuild() {
    // Scrolling up
    if (this.fetchAction === 'previous') {
      this.handlePreviousFetch();
    }

    else {
      if (this.fetchCount === 1) {
        this.handleFirstActiveFetch();
      }

      else {
        this.handleMoreActiveFetch();
      }
    }
  }

  parse() {
    this.data = this.raw;
    this.fetchCount++;
    this.fetchTimestamp = Date.now();
    this.maxOffsetLoaded = Math.max(this.data.nextOffset, this.maxOffsetLoaded);
    this.minOffsetLoaded = Math.min(this.requestOffset, this.minOffsetLoaded);
    this.buildInProgress = this.options.buildState === BuildStates.IN_PROGRESS;
    this.cancelledBuild = this.options.buildState === BuildStates.CANCELLED;
    this.endOfLogLoaded = this.maxOffsetLoaded === this.options.size;
    this.newLogLines = this.formatLog();

    if (this.buildInProgress) {
      this.parseActiveBuild();
    }

    else {
      this.parseInactiveBuild();
    }
  }

  // active build helpers
  handleFirstActiveFetch() {
    // we already have at least 1 offset
    if (this.requestOffset > 0) {
      // save and remove incomplete first line so we can append it to the
      // incomplete last line of the next fetch if we continue to scroll up
      this.firstLine = first(this.newLogLines);
      this.newLogLines = rest(this.newLogLines);
    }
    // our log is larger than at least 1 offset
    // e.g. we used the "To Top" button
    else if (this.options.size > this.baseRequestLength) {
      // save and chop off last incomplete line
      this.lastLine = last(this.newLogLines);
      this.newLogLines = initial(this.newLogLines);
    }

    this.logLines = this.newLogLines;
  }

  handleMoreActiveFetch() {
    // if we are tailing/actively polling
    if (this.shouldPoll) {
      // if our next offset is offsetLength, we need to
      // do some chopping as we may have incomplete lines
      if (this.data.nextOffset - this.data.offset >= this.baseRequestLength) {
        // cutoff last line and save it
        this.lastLine = last(this.newLogLines);
        this.newLogLines = initial(this.newLogLines);
      }
      // as long as our offset is less than offsetLength
      // we dont need to worry about lines being cutoff
      this.logLines = [...this.logLines, ...this.newLogLines];
    }

    // if we are not polling
    else if (!this.shouldPoll) {
      this.parseNextFetch();
    }
  }

  handleEndOfLogFetch() {
    // we already have at least 1 offset
    if (this.requestOffset > 0) {
      // save and remove incomplete first line so we can append it to
      // the incomplete last line of the next fetch if we scroll up
      this.firstLine = first(this.newLogLines);
      this.newLogLines = rest(this.newLogLines);
    }

    if (this.options.buildState === BuildStates.IN_PROGRESS) {

    }

    this.logLines = [...this.logLines, ...this.newLogLines];
  }

  handleNextFetch() {
    this.parseNextFetch();
  }

  handlePreviousFetch() {
    this.parsePreviousFetch({
      removeFirstLine: this.requestOffset > 0
    });
  }

  handleToTopFetch() {
    // save and remove incomplete last line so we can prepend it to
    // the incomplete last line of the next fetch if we scroll up
    this.lastLine = last(this.newLogLines);
    this.newLogLines = initial(this.newLogLines);
    this.logLines = this.newLogLines;
  }

  // Helper for parse, used to manage incomplete lines when scrolling up
  parseNextFetch(options = {}) {
    // save incomplete last line so we can prepend it to the
    // incomplete first line of the next fetch if we continue to scroll down
    const tempLast = last(this.newLogLines);
    // chop off last incomplete line, as long as we have more to fetch
    if (!this.endOfLogLoaded) {
      this.newLogLines = initial(this.newLogLines);
    }
    // prepend the rest of the first line that was cutoff last fetch
    if (this.lastLine) {
      this.newLogLines[0].text = this.lastLine.text + this.newLogLines[0].text;
    }
    this.lastLine = tempLast;
    this.logLines = [...this.logLines, ...this.newLogLines];
  }

  // Helper for parse, used to manage incomplete lines when scrolling down
  parsePreviousFetch(options = {}) {
    let {removeFirstLine} = options;
    // save incomplete first line so we can append it to the
    // incomplete last line of the next fetch if we continue to scroll up
    const tempFirst = first(this.newLogLines);
    // remove the first line which may be incomplete
    // as long as we are not at the beginning of the log
    if (this.requestOffset !== 0 || removeFirstLine) {
      this.newLogLines = rest(this.newLogLines);
    }
    // append extra text to last log line that is incomplete
    this.newLogLines[this.newLogLines.length - 1].text = this.newLogLines[this.newLogLines.length - 1].text + this.firstLine.text;
    // prepend new logLines to existing logLines
    this.firstLine = tempFirst;

    this.logLines = [...this.newLogLines, ...this.logLines];
  }

  fetchPrevious() {
    this.fetchAction = 'previous';

    this.requestOffset = Math.max(this.minOffsetLoaded - this.baseRequestLength, 0);
    // check if we have any overlap if scrolling up
    if (this.requestOffset === 0 && this.minOffsetLoaded < this.baseRequestLength) {
      this.lengthOverride = this.minOffsetLoaded;
    }
    return this.fetch();
  }

  fetchNext() {
    this.fetchAction = 'next';
    this.requestOffset = this.data.nextOffset;
    return this.fetch();
  }

  formatLog() {
    const WHITE_SPACE = /^\s*$/;
    const NEW_LINE = '\n';
    const logData = this.data.data;

    if (logData.match(WHITE_SPACE)) {
      return [];
    }

    if (logData.length === 0) {
      return [];
    }

    let offsetRunningTotal = this.requestOffset;

    const splitLines = logData.split(NEW_LINE);

    const formattedLogLines = splitLines.map((line, i) => {
      // store second line because we may chop off the first
      if (i === 1) {
        this.pastOffsetLine = this.currentOffsetLine;
        this.currentOffsetLine = offsetRunningTotal + getByteLength(line);
      }

      if (i === splitLines.length - 1) {
        this.lastOffsetLine = offsetRunningTotal + getByteLength(length);
      }

      if (line.length === 0) {
        return false;
      }

      try {
        line = utf8.decode(line);
      }

      catch (err) {
        console.warn("We couldn't decode some UTF-8, so we're displaying this log line undecoded:\n", line);
      }

      return {
        text: line,
        offset: offsetRunningTotal += getByteLength(line)
      };

    });

    return compact(formattedLogLines);

  }

}

export default Log;
<|endoftext|>


<commit_before>/*globals requirejs, define*/
requirejs.config({
  shim: {
    'underscore': {
      exports: '_'
    },
    'backbone': {
      deps: [ 'jquery', 'underscore' ],
      exports: 'Backbone'
    }
  },

  paths: {
    'backbone': '../../app/components/backbone/backbone-min',
    'jquery': '../../app/components/jquery/jquery.min',
    'underscore': '../../app/components/underscore/underscore-min',
    'text': '../../app/components/requirejs-text/text'
  }
});

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

  var _ = require( 'underscore' ),
      $ = require( 'jquery' );

  var Transform     = require( 'models/transform' ),
      Transforms    = require( 'collections/transforms' ),
      TransformView = require( 'views/transform-view' );

  var Box     = require( 'models/box' ),
      BoxView = require( 'views/box-view' );

  var robotTemplate = require( 'text!templates/robot.html' );

  var $robot = $( '.robot' );
  $robot.html( _.template( robotTemplate ) );

  // Haphazard configuration object.
  var config = (function() {
    var head = {
      width: 40,
      height: 40,
      depth: 40
    };

    var chest = {
      width: 80,
      height: 100,
      depth: 35
    };

    var hips = {
      width: 50,
      height: 30,
      depth: 35
    };

    var arm = {
      width: 20,
      depth: 20
    };

    var upperArm = {
      width: arm.width,
      height: 60,
      depth: arm.depth
    };

    var lowerArm = {
      width: arm.width,
      height: 40,
      depth: arm.depth
    };

    var hand = {
      width: arm.width,
      height: 20,
      depth: arm.depth
    };

    var leg = {
      width: 25,
      depth: 25
    };

    // Note: the femur should be ~26% of body height.
    var upperLeg = {
      width: leg.width,
      height: 50,
      depth: leg.depth
    };

    // Femur:tibia ratio should be 56:44. This guy's proportions are out of wack.
    var lowerLeg = {
      width: leg.width,
      height: 70,
      depth: leg.depth
    };

    var foot = {
      width: 30,
      height: 15,
      depth: 50
    };

    var spacing = 10;

    var headOffsetY = 0.5 * ( head.height + chest.height ) + spacing;
    var hipsOffsetY = 0.5 * ( hips.height + chest.height ) + spacing;

    var armOffsetX = 0.5 * ( arm.width + chest.width ) + spacing;
    var lowerArmOffsetY = 0.5 * ( lowerArm.height + upperArm.height ) + spacing;
    var handOffsetY = 0.5 * ( hand.height + lowerArm.height ) + spacing;

    // Position legs such that they line up with the hips.
    var legOffsetX = 0.5 * hips.width;

    var upperLegOffsetY = 0.5 * ( upperLeg.height + hips.height ) + spacing;
    var lowerLegOffsetY = 0.5 * ( lowerLeg.height + upperLeg.height ) + spacing;
    var footOffsetY = 0.5 * ( foot.height + lowerLeg.height ) + spacing;

    return {
      head: {
        dimensions: [ head.width, head.height, head.depth ],
        translate3d: [ 0, -headOffsetY, 0 ],
      },
      chest: {
        dimensions: [ chest.width, chest.height, chest.depth ]
      },
      hips: {
        dimensions: [ hips.width, hips.height, hips.depth ],
        translate3d: [ 0, hipsOffsetY, 0 ],
        transformOrigin: [ 0, -0.5 * hips.height ]
      },

      'upper-arm': {
        dimensions: [ upperArm.width, upperArm.height, upperArm.depth ],
        transformOrigin: [ 0, -0.5 * upperArm.height ]
      },
      'lower-arm': {
        dimensions: [ lowerArm.width, lowerArm.height, lowerArm.depth ],
        translate3d: [ 0, lowerArmOffsetY, 0 ],
        transformOrigin: [ 0, -0.5 * lowerArm.height ]
      },
      hand: {
        dimensions: [ hand.width, hand.height, hand.depth ],
        translate3d: [ 0, handOffsetY, 0 ],
        transformOrigin: [ 0, -0.5 * hand.height ]
      },
      'arm-left': [ armOffsetX ],
      'arm-right': [ -armOffsetX ],

      'upper-leg': {
        dimensions: [ upperLeg.width, upperLeg.height, upperLeg.depth ],
        translate3d: [ 0, upperLegOffsetY, 0 ],
        transformOrigin: [ 0, -0.5 * upperLeg.height ]
      },
      'lower-leg': {
        dimensions: [ lowerLeg.width, lowerLeg.height, lowerLeg.depth ],
        translate3d: [ 0, lowerLegOffsetY, 0 ],
        transformOrigin: [ 0, -0.5 * lowerLeg.height ]
      },
      foot: {
        dimensions: [ foot.width, foot.height, foot.depth ],
        translate3d: [ 0, footOffsetY, 10 ],
        transformOrigin: [ 0, -0.5 * foot.height ]
      },
      'leg-left': [ legOffsetX ],
      'leg-right': [ -legOffsetX ]
    };
  }) ();

  function createBoxViews( name ) {
    var views = $robot.find( '.' + name ).toArray().map(function( element ) {
      return new BoxView({
        el: element,
        model: new Box( config[ name ].dimensions ),
        transforms: new Transforms([
          new Transform.Translate3D( config[ name ].translate3d ),
          new Transform.RotateX(),
          new Transform.RotateY(),
          new Transform.RotateZ()
        ]),
        transformOrigin: new Transform.Origin( config[ name ].transformOrigin || {} )
      });
    });

    return views;
  }

  var $transformViews = $( '.transform-views' );

  [
    'head', 'chest', 'hips',
    'upper-arm', 'lower-arm', 'hand',
    'upper-leg', 'lower-leg', 'foot'
  ].forEach(function( className ) {
    createBoxViews( className ).forEach(function( view, index, array ) {
      view.render();

      var $transformEl = $( '<div>', { class: 'edit' } );

      var $dimensionEl = $( '<div>', { class: 'dimension' } ),
          $translateEl = $( '<div>', { class: 'translate' } );

      var $rotationsEl = $( '<div>', { class: 'rotations' } );

      var $rotateXEl = $( '<div>', { class: 'rotate rotateX' } ),
          $rotateYEl = $( '<div>', { class: 'rotate rotateY' } ),
          $rotateZEl = $( '<div>', { class: 'rotate rotateZ' } );

      $rotationsEl.append([
        $rotateXEl,
        $rotateYEl,
        $rotateZEl
      ]);

      // Unique ids if more than one of the same class.
      var id = className;
      if ( array.length > 1 ) {
        id += '-' + index;
      }

      $transformEl.append([
        $( '<input>', {
          class: 'checkbox',
          type: 'checkbox',
          id: id
        }),
        $( '<label>', {
          class: 'name',
          text: className,
          for: id
        }),
        $dimensionEl,
        $translateEl,
        $rotationsEl
      ]);

      $transformViews.append( $transformEl );

      var dimensionEl = new TransformView({
        el: $dimensionEl,
        model: view.model
      });

      dimensionEl.render();

      var translateView = new TransformView({
        el: $translateEl,
        model: view.transforms.at(0)
      });

      translateView.render();

      var rotateXView = new TransformView({
        el: $rotateXEl,
        model: view.transforms.at(1)
      })

      rotateXView.render();

      var rotateYView = new TransformView({
        el: $rotateYEl,
        model: view.transforms.at(2)
      });

      rotateYView.render();

      var rotateZView = new TransformView({
        el: $rotateZEl,
        model: view.transforms.at(3)
      });

      rotateZView.render();
    });
  });

  [ 'arm-left', 'arm-right', 'leg-left', 'leg-right' ].map(function( className ) {
    var transforms = new Transforms([
      new Transform.Translate3D( config[ className ] )
    ]);

    var transformsString = transforms.toString();

    $robot.find( '.' + className ).css({
      '-webkit-transform': transformsString,
      transform: transformsString,

      '-webkit-transform-style': 'preserve-3d',
      'transform-style': 'preserve-3d'
    });

    return transforms;
  });

  function onMouseMove( event ) {
    var rx = -( event.clientY / window.innerHeight - 0.5 ) * 180,
        ry =  ( event.clientX / window.innerWidth  - 0.5 ) * 180;

    var transform = 'rotateX(' + rx + 'deg) rotateY(' + ry + 'deg)';
    $robot.css({
      '-webkit-transform': transform,
      transform: transform
    });
  }

  $( window ).on( 'mousemove', onMouseMove );
});
<commit_msg>robot-animation: Foot translateZ is now relative to foot depth.<commit_after>/*globals requirejs, define*/
requirejs.config({
  shim: {
    'underscore': {
      exports: '_'
    },
    'backbone': {
      deps: [ 'jquery', 'underscore' ],
      exports: 'Backbone'
    }
  },

  paths: {
    'backbone': '../../app/components/backbone/backbone-min',
    'jquery': '../../app/components/jquery/jquery.min',
    'underscore': '../../app/components/underscore/underscore-min',
    'text': '../../app/components/requirejs-text/text'
  }
});

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

  var _ = require( 'underscore' ),
      $ = require( 'jquery' );

  var Transform     = require( 'models/transform' ),
      Transforms    = require( 'collections/transforms' ),
      TransformView = require( 'views/transform-view' );

  var Box     = require( 'models/box' ),
      BoxView = require( 'views/box-view' );

  var robotTemplate = require( 'text!templates/robot.html' );

  var $robot = $( '.robot' );
  $robot.html( _.template( robotTemplate ) );

  // Haphazard configuration object.
  var config = (function() {
    var head = {
      width: 40,
      height: 40,
      depth: 40
    };

    var chest = {
      width: 80,
      height: 100,
      depth: 35
    };

    var hips = {
      width: 50,
      height: 30,
      depth: 35
    };

    var arm = {
      width: 20,
      depth: 20
    };

    var upperArm = {
      width: arm.width,
      height: 60,
      depth: arm.depth
    };

    var lowerArm = {
      width: arm.width,
      height: 40,
      depth: arm.depth
    };

    var hand = {
      width: arm.width,
      height: 20,
      depth: arm.depth
    };

    var leg = {
      width: 25,
      depth: 25
    };

    // Note: the femur should be ~26% of body height.
    var upperLeg = {
      width: leg.width,
      height: 50,
      depth: leg.depth
    };

    // Femur:tibia ratio should be 56:44. This guy's proportions are out of wack.
    var lowerLeg = {
      width: leg.width,
      height: 70,
      depth: leg.depth
    };

    var foot = {
      width: 30,
      height: 15,
      depth: 50
    };

    var spacing = 10;

    var headOffsetY = 0.5 * ( head.height + chest.height ) + spacing;
    var hipsOffsetY = 0.5 * ( hips.height + chest.height ) + spacing;

    var armOffsetX = 0.5 * ( arm.width + chest.width ) + spacing;
    var lowerArmOffsetY = 0.5 * ( lowerArm.height + upperArm.height ) + spacing;
    var handOffsetY = 0.5 * ( hand.height + lowerArm.height ) + spacing;

    // Position legs such that they line up with the hips.
    var legOffsetX = 0.5 * hips.width;

    var upperLegOffsetY = 0.5 * ( upperLeg.height + hips.height ) + spacing;
    var lowerLegOffsetY = 0.5 * ( lowerLeg.height + upperLeg.height ) + spacing;
    var footOffsetY = 0.5 * ( foot.height + lowerLeg.height ) + spacing;

    return {
      head: {
        dimensions: [ head.width, head.height, head.depth ],
        translate3d: [ 0, -headOffsetY, 0 ],
      },
      chest: {
        dimensions: [ chest.width, chest.height, chest.depth ]
      },
      hips: {
        dimensions: [ hips.width, hips.height, hips.depth ],
        translate3d: [ 0, hipsOffsetY, 0 ],
        transformOrigin: [ 0, -0.5 * hips.height ]
      },

      'upper-arm': {
        dimensions: [ upperArm.width, upperArm.height, upperArm.depth ],
        transformOrigin: [ 0, -0.5 * upperArm.height ]
      },
      'lower-arm': {
        dimensions: [ lowerArm.width, lowerArm.height, lowerArm.depth ],
        translate3d: [ 0, lowerArmOffsetY, 0 ],
        transformOrigin: [ 0, -0.5 * lowerArm.height ]
      },
      hand: {
        dimensions: [ hand.width, hand.height, hand.depth ],
        translate3d: [ 0, handOffsetY, 0 ],
        transformOrigin: [ 0, -0.5 * hand.height ]
      },
      'arm-left': [ armOffsetX ],
      'arm-right': [ -armOffsetX ],

      'upper-leg': {
        dimensions: [ upperLeg.width, upperLeg.height, upperLeg.depth ],
        translate3d: [ 0, upperLegOffsetY, 0 ],
        transformOrigin: [ 0, -0.5 * upperLeg.height ]
      },
      'lower-leg': {
        dimensions: [ lowerLeg.width, lowerLeg.height, lowerLeg.depth ],
        translate3d: [ 0, lowerLegOffsetY, 0 ],
        transformOrigin: [ 0, -0.5 * lowerLeg.height ]
      },
      foot: {
        dimensions: [ foot.width, foot.height, foot.depth ],
        translate3d: [ 0, footOffsetY, 0.2 * foot.depth ],
        transformOrigin: [ 0, -0.5 * foot.height ]
      },
      'leg-left': [ legOffsetX ],
      'leg-right': [ -legOffsetX ]
    };
  }) ();

  function createBoxViews( name ) {
    var views = $robot.find( '.' + name ).toArray().map(function( element ) {
      return new BoxView({
        el: element,
        model: new Box( config[ name ].dimensions ),
        transforms: new Transforms([
          new Transform.Translate3D( config[ name ].translate3d ),
          new Transform.RotateX(),
          new Transform.RotateY(),
          new Transform.RotateZ()
        ]),
        transformOrigin: new Transform.Origin( config[ name ].transformOrigin || {} )
      });
    });

    return views;
  }

  var $transformViews = $( '.transform-views' );

  [
    'head', 'chest', 'hips',
    'upper-arm', 'lower-arm', 'hand',
    'upper-leg', 'lower-leg', 'foot'
  ].forEach(function( className ) {
    createBoxViews( className ).forEach(function( view, index, array ) {
      view.render();

      var $transformEl = $( '<div>', { class: 'edit' } );

      var $dimensionEl = $( '<div>', { class: 'dimension' } ),
          $translateEl = $( '<div>', { class: 'translate' } );

      var $rotationsEl = $( '<div>', { class: 'rotations' } );

      var $rotateXEl = $( '<div>', { class: 'rotate rotateX' } ),
          $rotateYEl = $( '<div>', { class: 'rotate rotateY' } ),
          $rotateZEl = $( '<div>', { class: 'rotate rotateZ' } );

      $rotationsEl.append([
        $rotateXEl,
        $rotateYEl,
        $rotateZEl
      ]);

      // Unique ids if more than one of the same class.
      var id = className;
      if ( array.length > 1 ) {
        id += '-' + index;
      }

      $transformEl.append([
        $( '<input>', {
          class: 'checkbox',
          type: 'checkbox',
          id: id
        }),
        $( '<label>', {
          class: 'name',
          text: className,
          for: id
        }),
        $dimensionEl,
        $translateEl,
        $rotationsEl
      ]);

      $transformViews.append( $transformEl );

      var dimensionEl = new TransformView({
        el: $dimensionEl,
        model: view.model
      });

      dimensionEl.render();

      var translateView = new TransformView({
        el: $translateEl,
        model: view.transforms.at(0)
      });

      translateView.render();

      var rotateXView = new TransformView({
        el: $rotateXEl,
        model: view.transforms.at(1)
      });

      rotateXView.render();

      var rotateYView = new TransformView({
        el: $rotateYEl,
        model: view.transforms.at(2)
      });

      rotateYView.render();

      var rotateZView = new TransformView({
        el: $rotateZEl,
        model: view.transforms.at(3)
      });

      rotateZView.render();
    });
  });

  [ 'arm-left', 'arm-right', 'leg-left', 'leg-right' ].map(function( className ) {
    var transforms = new Transforms([
      new Transform.Translate3D( config[ className ] )
    ]);

    var transformsString = transforms.toString();

    $robot.find( '.' + className ).css({
      '-webkit-transform': transformsString,
      transform: transformsString,

      '-webkit-transform-style': 'preserve-3d',
      'transform-style': 'preserve-3d'
    });

    return transforms;
  });

  function onMouseMove( event ) {
    var rx = -( event.clientY / window.innerHeight - 0.5 ) * 180,
        ry =  ( event.clientX / window.innerWidth  - 0.5 ) * 180;

    var transform = 'rotateX(' + rx + 'deg) rotateY(' + ry + 'deg)';
    $robot.css({
      '-webkit-transform': transform,
      transform: transform
    });
  }

  $( window ).on( 'mousemove', onMouseMove );
});
<|endoftext|>


<commit_before>export { MinMax, ScaleTranslate, Base };

import * as ValueDesc from "../Unpacker/ValueDesc.js";

/**
 *
 * @member {number} min
 *   The lower bound of the range.
 *
 * @member {number} max
 *   The upper bound of the range.
 */
class MinMax {

  constructor( min, max ) {
    this.min = Math.min( min, max ); // Confirm ( min <= max ).
    this.max = Math.max( min, max );
    this.difference = this.max - this.min;
  }

  /**
   * @param {MinMax} target
   *   The range of the target value.
   *
   * @return {number}
   *   Return the scale value for mapping values from this range to target range.
   */
  getScale_ForTo( target ) {

    // Suppose x is a value inside the source range. y is the corresponding value inside the target range.
    //
    //   y = target.min + ( target.difference * ( x - source.min ) / source.difference )
    //     = target.min + ( ( ( target.difference * x ) - ( target.difference * source.min ) ) / source.difference )
    //     = target.min + ( ( ( target.difference * x ) / source.difference ) - ( ( target.difference * source.min ) / source.difference ) )
    //     = target.min + ( ( ( target.difference / source.difference ) * x ) - ( ( target.difference * source.min ) / source.difference ) )
    //     = target.min + ( ( target.difference / source.difference ) * x ) - ( ( target.difference * source.min ) / source.difference )
    //     = ( ( target.difference / source.difference ) * x ) + ( target.min - ( ( target.difference * source.min ) / source.difference ) )
    //     = ( scale * x ) + translate
    //
    // Got:
    //   scale = ( target.difference / source.difference )
    //   translate = ( target.min - ( ( target.difference * source.min ) / source.difference ) )
    //
    // For example:
    //   - from [ 2, 12 ] to [ -3, -1 ]
    //   - scale  = 0.2
    //   - translate = -3.4
    //
    let scale = ( target.difference / this.difference );
    return scale;
  }

  /**
   * @param {MinMax} target
   *   The range of the target value.
   *
   * @return {number}
   *   Return the translate value for mapping values from this range to target range.
   */
  getTranslate_ForTo( target ) {
    let translate = ( target.min - ( ( target.difference * this.min ) / this.difference ) ); // (Please see MinMax.getScale_ForTo().)
    return translate;
  }

}

/**
 * Describe a scale (i.e. multiplier) value, and then a translate (i.e. offset; bias) value after the scale.
 *
 */
class ScaleTranslate {

  constructor( scale, translate ) {
    this.scale = scale;
    this.translate = translate;
  }

  /**
   * Set this.scale and this.translate for mapping values from sourceMinMax to targetMinMax.
   *
   * @param {MinMax} source
   *   The range of the source value.
   *
   * @param {MinMax} target
   *   The range of the target value.
   */
  setBy_FromTo( source, target ) {
    this.scale = source.getScale_ForTo( target );
    this.translate = source.getTranslate_ForTo( target );
  }

  /**
   * @param {MinMax} source
   *   The range of the source value.
   *
   * @param {MinMax} target
   *   The range of the target value.
   *
   * @return {ScaleTranslate}
   *   Create and return { scale, translate } for mapping values from sourceMinMax to targetMinMax.
   */
  static createBy_FromTo( source, target ) {
    let scale = source.getScale_ForTo( target );
    let translate = source.getTranslate_ForTo( target );
    let result = new ScaleTranslate( scale, translate );
    return result;
  }

}

/**
 * Describe the input value lower and upper bounds of an activation function for keeping the output almost linear.
 *
 * Activation function is non-linear in the whole domain. However, inside a part of the domain, it may be very
 * approximate a linear function. This class describe that part of domain.
 *
 *
 * @member {number} nActivationId
 *   The activation function id (ValueDesc.ActivationFunction.Singleton.Ids.Xxx).
 *
 * @member {MinMax} linearDomain
 *   The domain (i.e. input value of the activation function) could keep the output almost linear.
 */
class Base {

  /**
   * @member {number} lowerBound
   *   The lower bound of domain (i.e. input value of the activation function) for keeping the output almost linear.
   *
   * @member {number} upperBound
   *   The upper bound of domain (i.e. input value of the activation function) for keeping the output almost linear.
   */
  constructor( nActivationId, lowerBound, upperBound ) {
    this.nActivationId = nActivationId;
    this.linearDomain = new MinMax( lowerBound, upperBound );
  }

}

/**
 * Predefined linear domain for every activation function.
 *
 *
 * For example,
 *   - RELU6 is linear between[ 0, 6 ].
 *   - SIGMOID is alomost linear between[ -0.125, +0.125 ].
 *   - TANH is almost linear between[ -0.125, +0.125 ].
 *   - COS is almost linear between[ -( ( PI / 2 ) + 0.025 ), -( ( PI / 2 ) - 0.025 ) ].
 *   - SIN is almost linear between[ -0.025, +0.025 ].
 *   - RELU is linear between[ 0, 6 ].
 *
 */
Base.PredefinedById = new Map( [
  [ ValueDesc.ActivationFunction.Singleton.Ids.RELU6,   new Base(                            0,                            6 ) ],
  [ ValueDesc.ActivationFunction.Singleton.Ids.SIGMOID, new Base(                       -0.125,                       +0.125 ) ],
  [ ValueDesc.ActivationFunction.Singleton.Ids.TANH,    new Base(                       -0.125,                       +0.125 ) ],
  [ ValueDesc.ActivationFunction.Singleton.Ids.COS,     new Base( -( ( Math.PI / 2 ) + 0.025 ), -( ( Math.PI / 2 ) - 0.025 ) ) ],
  [ ValueDesc.ActivationFunction.Singleton.Ids.SIN,     new Base(                       -0.025,                       +0.025 ) ],
  [ ValueDesc.ActivationFunction.Singleton.Ids.RELU,    new Base(                            0,                            6 ) ],
] );
<commit_msg>Update ActivationLinearDomain.js<commit_after>export { MinMax, ScaleTranslate, Base };

import * as ValueDesc from "../Unpacker/ValueDesc.js";

/**
 *
 * @member {number} min
 *   The lower bound of the range.
 *
 * @member {number} max
 *   The upper bound of the range.
 */
class MinMax {

  constructor( min, max ) {
    this.min = Math.min( min, max ); // Confirm ( min <= max ).
    this.max = Math.max( min, max );
    this.difference = this.max - this.min;
  }

  /**
   * @param {MinMax} target
   *   The range of the target value.
   *
   * @return {number}
   *   Return the scale value for mapping values from this range to target range.
   */
  getScale_ForTo( target ) {
    // Suppose x is a value inside the source range. y is the corresponding value inside the target range.
    //
    //   y = target.min + ( target.difference * ( x - source.min ) / source.difference )
    //     = target.min + ( ( ( target.difference * x ) - ( target.difference * source.min ) ) / source.difference )
    //     = target.min + ( ( ( target.difference * x ) / source.difference ) - ( ( target.difference * source.min ) / source.difference ) )
    //     = target.min + ( ( ( target.difference / source.difference ) * x ) - ( ( target.difference * source.min ) / source.difference ) )
    //     = target.min + ( ( target.difference / source.difference ) * x ) - ( ( target.difference * source.min ) / source.difference )
    //     = ( ( target.difference / source.difference ) * x ) + ( target.min - ( ( target.difference * source.min ) / source.difference ) )
    //     = ( scale * x ) + translate
    //
    // Got:
    //   scale = ( target.difference / source.difference )
    //   translate = ( target.min - ( ( target.difference * source.min ) / source.difference ) )
    //
    // For example:
    //   - from [ 2, 12 ] to [ -3, -1 ]
    //   - scale  = 0.2
    //   - translate = -3.4
    //
    let scale = ( target.difference / this.difference );
    return scale;
  }

  /**
   * @param {MinMax} target
   *   The range of the target value.
   *
   * @return {number}
   *   Return the translate value for mapping values from this range to target range.
   */
  getTranslate_ForTo( target ) {
    let translate = ( target.min - ( ( target.difference * this.min ) / this.difference ) ); // (Please see MinMax.getScale_ForTo().)
    return translate;
  }

}

/**
 * Describe a scale (i.e. multiplier) value, and then a translate (i.e. offset; bias) value after the scale.
 *
 */
class ScaleTranslate {

  constructor( scale, translate ) {
    this.scale = scale;
    this.translate = translate;
  }

  /**
   * Set this.scale and this.translate for mapping values from sourceMinMax to targetMinMax.
   *
   * @param {MinMax} source
   *   The range of the source value.
   *
   * @param {MinMax} target
   *   The range of the target value.
   */
  setBy_FromTo( source, target ) {
    this.scale = source.getScale_ForTo( target );
    this.translate = source.getTranslate_ForTo( target );
  }

  /**
   * @param {MinMax} source
   *   The range of the source value.
   *
   * @param {MinMax} target
   *   The range of the target value.
   *
   * @return {ScaleTranslate}
   *   Create and return { scale, translate } for mapping values from sourceMinMax to targetMinMax.
   */
  static createBy_FromTo( source, target ) {
    let scale = source.getScale_ForTo( target );
    let translate = source.getTranslate_ForTo( target );
    let result = new ScaleTranslate( scale, translate );
    return result;
  }

}

/**
 * Describe the input value lower and upper bounds of an activation function for keeping the output almost linear.
 *
 * Activation function is non-linear in the whole domain. However, inside a part of the domain, it may be very
 * approximate a linear function. This class describe that part of domain.
 *
 *
 * @member {number} nActivationId
 *   The activation function id (ValueDesc.ActivationFunction.Singleton.Ids.Xxx).
 *
 * @member {MinMax} linearDomain
 *   The domain (i.e. input value of the activation function) could keep the output almost linear.
 */
class Base {

  /**
   * @member {number} lowerBound
   *   The lower bound of domain (i.e. input value of the activation function) for keeping the output almost linear.
   *
   * @member {number} upperBound
   *   The upper bound of domain (i.e. input value of the activation function) for keeping the output almost linear.
   */
  constructor( nActivationId, lowerBound, upperBound ) {
    this.nActivationId = nActivationId;
    this.linearDomain = new MinMax( lowerBound, upperBound );
  }

}

/**
 * Predefined linear domain for every activation function.
 *
 *
 * For example,
 *   - RELU6 is linear between[ 0, 6 ].
 *   - SIGMOID is alomost linear between[ -0.125, +0.125 ].
 *   - TANH is almost linear between[ -0.125, +0.125 ].
 *   - COS is almost linear between[ -( ( PI / 2 ) + 0.025 ), -( ( PI / 2 ) - 0.025 ) ].
 *   - SIN is almost linear between[ -0.025, +0.025 ].
 *   - RELU is linear between[ 0, 6 ].
 *
 */
Base.PredefinedById = new Map( [
  [ ValueDesc.ActivationFunction.Singleton.Ids.RELU6,   new Base(                            0,                            6 ) ],
  [ ValueDesc.ActivationFunction.Singleton.Ids.SIGMOID, new Base(                       -0.125,                       +0.125 ) ],
  [ ValueDesc.ActivationFunction.Singleton.Ids.TANH,    new Base(                       -0.125,                       +0.125 ) ],
  [ ValueDesc.ActivationFunction.Singleton.Ids.COS,     new Base( -( ( Math.PI / 2 ) + 0.025 ), -( ( Math.PI / 2 ) - 0.025 ) ) ],
  [ ValueDesc.ActivationFunction.Singleton.Ids.SIN,     new Base(                       -0.025,                       +0.025 ) ],
  [ ValueDesc.ActivationFunction.Singleton.Ids.RELU,    new Base(                            0,                            6 ) ],
] );
<|endoftext|>


<commit_before>
function setEntityInclude(selectEleId, pagePath) {
	var entityType = document.getElementById(selectEleId).value;
	var inclueBlock = document.getElementById("entityInclude");
	if(entityType == 'metal particle' ||
		entityType == 'quantum dot') {
		inclueBlock.style.display = "none";
	} else {
		inclueBlock.style.display = "inline";
		CompositionManager.getEntityIncludePage(entityType, pagePath, populatePage);
	}
}
function populatePage(pageData) {
	dwr.util.setValue("entityInclude", pageData, {escapeHtml:false});
}
function getComposingElementOptions(selectEleId) {
	var compFuncTypeValue = dwr.util.getValue(selectEleId);
	CompositionManager.getComposingElementTypeOptions(compFuncTypeValue, function (data) {
		dwr.util.removeAllOptions("compElemType");
		dwr.util.addOptions("compElemType", [""]);
		dwr.util.addOptions("compElemType", data);
		dwr.util.addOptions("compElemType", ["[Other]"]);
	});
}

function getBiopolymerOptions(selectEleId) {
	var compFuncTypeValue = dwr.util.getValue(selectEleId);

	if (compFuncTypeValue == "biopolymer") {
		CompositionManager.getBiopolymerTypeOptions(compFuncTypeValue, {callback:function (data) {
			dwr.util.removeAllOptions('biopolymerType');
			dwr.util.addOptions('biopolymerType', ['']);
			dwr.util.addOptions('biopolymerType', data);
			dwr.util.addOptions('biopolymerType', ['[Other]']);

			}, async:true
		});
	}
}
function getWallTypeOptions(selectEleId) {
	var compFuncTypeValue = dwr.util.getValue(selectEleId);
	if (compFuncTypeValue == "carbon nanotube") {
		CompositionManager.getWallTypeOptions(compFuncTypeValue, {
			callback:function (data) {
			var ele = document.getElementById("wallType");

			dwr.util.removeAllOptions("wallType");
			dwr.util.addOptions("wallType", [""]);
			dwr.util.addOptions("wallType", data);
		}, async:true
		} );

	}
}

var timeoutInterval = 500;
function getNETypeOptions() {
	window.setTimeout("getNETypes()", timeoutInterval);
}
function getNETypes() {
	var selectEleId = "peType";
	getBiopolymerOptions(selectEleId);
	getWallTypeOptions(selectEleId);
	getComposingElementOptions(selectEleId);
	setEntityTypeTitle(selectEleId);
}
function getAntibodyTypeOptions(selectEleId) {
	var compFuncTypeValue = dwr.util.getValue(selectEleId);
	if (compFuncTypeValue == "antibody") {
		CompositionManager.getAntibodyTypeOptions(compFuncTypeValue, {
			callback:function (data) {
				dwr.util.removeAllOptions("antibodyType");
				dwr.util.addOptions("antibodyType", [""]);
				dwr.util.addOptions("antibodyType", data);
				dwr.util.addOptions("antibodyType", ["[Other]"]);
			}, async:true
		});
	}
}
function getAntibodyIsotypeOptions(selectEleId) {
	var compFuncTypeValue = dwr.util.getValue(selectEleId);
	if (compFuncTypeValue == "antibody") {
		CompositionManager.getAntibodyIsotypeOptions(compFuncTypeValue, {
			callback:function (data) {

				dwr.util.removeAllOptions("antibodyIsotype");
				dwr.util.addOptions("antibodyIsotype", [""]);
				dwr.util.addOptions("antibodyIsotype", data);
				dwr.util.addOptions("antibodyIsotype", ["[Other]"]);
			}, async:true
		});
	}
}
function getAntibodySpeciesOptions(selectEleId) {
	var compFuncTypeValue = dwr.util.getValue(selectEleId);
	if (compFuncTypeValue == "antibody") {
		CompositionManager.getAntibodySpeciesOptions(compFuncTypeValue, function (data) {
			dwr.util.removeAllOptions("antibodySpecies");
			dwr.util.addOptions("antibodySpecies", [""]);
			dwr.util.addOptions("antibodySpecies", data);
		});
	}
}
function setEntityTypeTitle(selectEleId) {
	var selectEle = document.getElementById(selectEleId);
	document.getElementById("entityTypeTitle").innerHTML = selectEle.options[selectEle.options.selectedIndex].text;
}
function getFETypeOptions() {
	window.setTimeout("getFETypes()", timeoutInterval);
}

function getFETypes() {
	var selectEleId = "feType";
	getBiopolymerOptions(selectEleId);
	getAntibodyTypeOptions(selectEleId);
	getAntibodyIsotypeOptions(selectEleId);
	getAntibodySpeciesOptions(selectEleId);
	setEntityTypeTitle(selectEleId);
}
function displayFEModality(functionIndex) {
	var functionType = document.getElementById("funcType_" + functionIndex).value;
	var modalityDiv = document.getElementById("modalityDiv_" + functionIndex);
	var modalityStrong = document.getElementById("modalityStrong_" + functionIndex);
	if (functionType == "imaging") {
		modalityDiv.style.display = "inline";
		modalityStrong.style.display = "inline";
	} else {
		modalityDiv.style.display = "none";
		modalityStrong.style.display = "none";
	}
	return false;
}
function displayTarget(functionIndex) {
	var functionType = document.getElementById("funcType_" + functionIndex).value;
	var targetSpan = document.getElementById("targetSpan_" + functionIndex);
	var targetDiv = document.getElementById("targetDiv_" + functionIndex);
	if (functionType == "targeting") {
		targetSpan.style.display = "block";
		targetDiv.style.display = "block";
	} else {
		targetSpan.style.display = "none";
		targetDiv.style.display = "none";
	}
	return false;
}
function displayModality(compEleIndex, functionIndex) {
	var functionType = document.getElementById("funcType_" + compEleIndex + "_" + functionIndex).value;
	var modalityTd = document.getElementById("modalityTypeTd_" + compEleIndex + "_" + functionIndex);
	var modalityTitle = document.getElementById("modalityTitle_" + compEleIndex + "_" + functionIndex);
	if (functionType == "imaging") {
		modalityTd.style.display = "inline";
		modalityTitle.style.display = "inline";
	} else {
		modalityTd.style.display = "none";
		modalityTitle.style.display = "none";
	}
	return false;
}
function displayAntigenSpecies(parentIndex, childIndex) {
	var type = document.getElementById("targetType_" + parentIndex + "_" + childIndex).value;
	var sdiv = document.getElementById("speciesDiv_" + parentIndex + "_" + childIndex);
	var removeSpan = document.getElementById("removeSpan_" + parentIndex + "_" + childIndex);
	if (type == "antigen") {
		sdiv.style.display = "inline";
	} else {
		sdiv.style.display = "none";
	}
	return false;
}
function radLinkOrUpload(fileIndex) {
	var linkEle = document.getElementById("link_" + fileIndex);
	var loadEle = document.getElementById("load_" + fileIndex);
	if (document.getElementById("external_" + fileIndex).checked) {
		loadEle.style.display = "inline";
		linkEle.style.display = "none";
	} else {
		loadEle.style.display = "none";
		linkEle.style.display = "inline";
	}
}
/*
 * for chemical association
 */
function getAssociatedElementOptions(compositionTypeId, entityTypeId, compEleId) {
	var compositionType = dwr.util.getValue(compositionTypeId);
	var compEle = document.getElementById(compEleId);
	if (compositionType != "") {
		CompositionManager.getAssociatedElementOptions(compositionType, function (data) {
			dwr.util.removeAllOptions(entityTypeId);
			if (data != null) {
				dwr.util.addOptions(entityTypeId, [""]);
				dwr.util.addOptions(entityTypeId, data, "dataId", "dataDisplayType");
			}
		});
	} else {
		dwr.util.removeAllOptions(entityTypeId);
	}
	if (compositionType != "Nanoparticle Entity") {
		compEle.style.display = "none";
	}
}
function getAssociatedComposingElements(compositionTypeId, entityTypeId, compEleTypeId, compEleId) {
	var compositionType = dwr.util.getValue(compositionTypeId);
	var compEle = document.getElementById(compEleId);
	if (compositionType == "Nanoparticle Entity") {
		var entityId = dwr.util.getValue(entityTypeId);
		if (entityId != "") {
			CompositionManager.getAssociatedComposingElements(entityId, function (data) {
				dwr.util.removeAllOptions(compEleTypeId);
				if (data != null) {
					dwr.util.addOptions(compEleTypeId, [""]);
					dwr.util.addOptions(compEleTypeId, data, "domainComposingElementId", "displayName");
				}
			});
		}
		compEle.style.display = "inline";
	} else {
		dwr.util.removeAllOptions(compEleTypeId);
		compEle.style.display = "none";
	}
	return false;
}
function displayBondType() {
	var type = document.getElementById("assoType").value;
	var btTitleEle = document.getElementById("bondTypeTitle");
	var btLineEle = document.getElementById("bondTypeLine");
	if (type == "attachment") {
		btTitleEle.style.display = "inline";
		btLineEle.style.display = "inline";
	} else {
		btTitleEle.style.display = "none";
		btLineEle.style.display = "none";
	}
}
function setCompositionType(entityTypeId, displayNameEleId) {
	var selectEle = document.getElementById(entityTypeId);
	var selectedName = selectEle.options[selectEle.options.selectedIndex].text;
	document.getElementById(displayNameEleId).value = selectedName;
	// alert(document.getElementById(displayNameEleId).value);
}
function setEntityDisplayName(entityTypeId, displayNameEleId) {
	var selectEle = document.getElementById(entityTypeId);
	var selectedName = selectEle.options[selectEle.options.selectedIndex].text;
	document.getElementById(displayNameEleId).value = selectedName;
	// alert(document.getElementById(displayNameEleId).value);
}
/*
 * the following functions using AJAX to display modality dropdown menu in the
 * bodyNanoparticleEntityUpdate.jsp and bodyFunctionUpdate.jsp
 *
 */
/*
function setModalityTypeOptions(compEleIndex, functionIndex) {
	var functionType = dwr.util.getValue("funcType_" + compEleIndex + "_" + functionIndex);
	CompositionManager.getModalityTypeOptions(functionType, function(data) {

			dwr.util.removeAllOptions("modalityType_" + compEleIndex + "_" + functionIndex);
			dwr.util.addOptions("modalityType_" + compEleIndex + "_" + functionIndex, ['']);
    		dwr.util.addOptions("modalityType_" + compEleIndex + "_" + functionIndex, data);
    		dwr.util.addOptions("modalityType_" + compEleIndex + "_" + functionIndex, ['[Other]']);
  	});
}

function setModalityInclude(compEleIndex, functionIndex) {
	var functionType = dwr.util.getValue("funcType_" + compEleIndex + "_" + functionIndex);

	CompositionManager.getModalityIncludePage(compEleIndex, functionIndex, functionType, function(pageData) {

		document.getElementById("modalityTypeTd_" + compEleIndex + "_" + functionIndex).innerHTML = "";
		dwr.util.setValue("modalityTypeTd_" + compEleIndex + "_" + functionIndex, pageData, {escapeHtml:false});

	});

  	if(functionType != "imaging") {
  		document.getElementById("modalityType_" + compEleIndex + "_" + functionIndex).innerHTML =
  			" ";
  	}
}
*/

<commit_msg>updated to clear composing element dropdowns in chemical association form<commit_after>
function setEntityInclude(selectEleId, pagePath) {
	var entityType = document.getElementById(selectEleId).value;
	var inclueBlock = document.getElementById("entityInclude");
	if (entityType == "metal particle" || entityType == "quantum dot") {
		inclueBlock.style.display = "none";
	} else {
		inclueBlock.style.display = "inline";
		CompositionManager.getEntityIncludePage(entityType, pagePath, populatePage);
	}
}
function populatePage(pageData) {
	dwr.util.setValue("entityInclude", pageData, {escapeHtml:false});
}
function getComposingElementOptions(selectEleId) {
	var compFuncTypeValue = dwr.util.getValue(selectEleId);
	CompositionManager.getComposingElementTypeOptions(compFuncTypeValue, function (data) {
		dwr.util.removeAllOptions("compElemType");
		dwr.util.addOptions("compElemType", [""]);
		dwr.util.addOptions("compElemType", data);
		dwr.util.addOptions("compElemType", ["[Other]"]);
	});
}
function getBiopolymerOptions(selectEleId) {
	var compFuncTypeValue = dwr.util.getValue(selectEleId);
	if (compFuncTypeValue == "biopolymer") {
		CompositionManager.getBiopolymerTypeOptions(compFuncTypeValue, {callback:function (data) {
			dwr.util.removeAllOptions("biopolymerType");
			dwr.util.addOptions("biopolymerType", [""]);
			dwr.util.addOptions("biopolymerType", data);
			dwr.util.addOptions("biopolymerType", ["[Other]"]);
		}, async:true});
	}
}
function getWallTypeOptions(selectEleId) {
	var compFuncTypeValue = dwr.util.getValue(selectEleId);
	if (compFuncTypeValue == "carbon nanotube") {
		CompositionManager.getWallTypeOptions(compFuncTypeValue, {callback:function (data) {
			var ele = document.getElementById("wallType");
			dwr.util.removeAllOptions("wallType");
			dwr.util.addOptions("wallType", [""]);
			dwr.util.addOptions("wallType", data);
		}, async:true});
	}
}
var timeoutInterval = 500;
function getNETypeOptions() {
	window.setTimeout("getNETypes()", timeoutInterval);
}
function getNETypes() {
	var selectEleId = "peType";
	getBiopolymerOptions(selectEleId);
	getWallTypeOptions(selectEleId);
	getComposingElementOptions(selectEleId);
	setEntityTypeTitle(selectEleId);
}
function getAntibodyTypeOptions(selectEleId) {
	var compFuncTypeValue = dwr.util.getValue(selectEleId);
	if (compFuncTypeValue == "antibody") {
		CompositionManager.getAntibodyTypeOptions(compFuncTypeValue, {callback:function (data) {
			dwr.util.removeAllOptions("antibodyType");
			dwr.util.addOptions("antibodyType", [""]);
			dwr.util.addOptions("antibodyType", data);
			dwr.util.addOptions("antibodyType", ["[Other]"]);
		}, async:true});
	}
}
function getAntibodyIsotypeOptions(selectEleId) {
	var compFuncTypeValue = dwr.util.getValue(selectEleId);
	if (compFuncTypeValue == "antibody") {
		CompositionManager.getAntibodyIsotypeOptions(compFuncTypeValue, {callback:function (data) {
			dwr.util.removeAllOptions("antibodyIsotype");
			dwr.util.addOptions("antibodyIsotype", [""]);
			dwr.util.addOptions("antibodyIsotype", data);
			dwr.util.addOptions("antibodyIsotype", ["[Other]"]);
		}, async:true});
	}
}
function getAntibodySpeciesOptions(selectEleId) {
	var compFuncTypeValue = dwr.util.getValue(selectEleId);
	if (compFuncTypeValue == "antibody") {
		CompositionManager.getAntibodySpeciesOptions(compFuncTypeValue, function (data) {
			dwr.util.removeAllOptions("antibodySpecies");
			dwr.util.addOptions("antibodySpecies", [""]);
			dwr.util.addOptions("antibodySpecies", data);
		});
	}
}
function setEntityTypeTitle(selectEleId) {
	var selectEle = document.getElementById(selectEleId);
	document.getElementById("entityTypeTitle").innerHTML = selectEle.options[selectEle.options.selectedIndex].text;
}
function getFETypeOptions() {
	window.setTimeout("getFETypes()", timeoutInterval);
}
function getFETypes() {
	var selectEleId = "feType";
	getBiopolymerOptions(selectEleId);
	getAntibodyTypeOptions(selectEleId);
	getAntibodyIsotypeOptions(selectEleId);
	getAntibodySpeciesOptions(selectEleId);
	setEntityTypeTitle(selectEleId);
}
function displayFEModality(functionIndex) {
	var functionType = document.getElementById("funcType_" + functionIndex).value;
	var modalityDiv = document.getElementById("modalityDiv_" + functionIndex);
	var modalityStrong = document.getElementById("modalityStrong_" + functionIndex);
	if (functionType == "imaging") {
		modalityDiv.style.display = "inline";
		modalityStrong.style.display = "inline";
	} else {
		modalityDiv.style.display = "none";
		modalityStrong.style.display = "none";
	}
	return false;
}
function displayTarget(functionIndex) {
	var functionType = document.getElementById("funcType_" + functionIndex).value;
	var targetSpan = document.getElementById("targetSpan_" + functionIndex);
	var targetDiv = document.getElementById("targetDiv_" + functionIndex);
	if (functionType == "targeting") {
		targetSpan.style.display = "block";
		targetDiv.style.display = "block";
	} else {
		targetSpan.style.display = "none";
		targetDiv.style.display = "none";
	}
	return false;
}
function displayModality(compEleIndex, functionIndex) {
	var functionType = document.getElementById("funcType_" + compEleIndex + "_" + functionIndex).value;
	var modalityTd = document.getElementById("modalityTypeTd_" + compEleIndex + "_" + functionIndex);
	var modalityTitle = document.getElementById("modalityTitle_" + compEleIndex + "_" + functionIndex);
	if (functionType == "imaging") {
		modalityTd.style.display = "inline";
		modalityTitle.style.display = "inline";
	} else {
		modalityTd.style.display = "none";
		modalityTitle.style.display = "none";
	}
	return false;
}
function displayAntigenSpecies(parentIndex, childIndex) {
	var type = document.getElementById("targetType_" + parentIndex + "_" + childIndex).value;
	var sdiv = document.getElementById("speciesDiv_" + parentIndex + "_" + childIndex);
	var removeSpan = document.getElementById("removeSpan_" + parentIndex + "_" + childIndex);
	if (type == "antigen") {
		sdiv.style.display = "inline";
	} else {
		sdiv.style.display = "none";
	}
	return false;
}
function radLinkOrUpload(fileIndex) {
	var linkEle = document.getElementById("link_" + fileIndex);
	var loadEle = document.getElementById("load_" + fileIndex);
	if (document.getElementById("external_" + fileIndex).checked) {
		loadEle.style.display = "inline";
		linkEle.style.display = "none";
	} else {
		loadEle.style.display = "none";
		linkEle.style.display = "inline";
	}
}
/*
 * for chemical association
 */
function getAssociatedElementOptions(compositionTypeId, entityTypeId, compEleId) {
	var compositionType = dwr.util.getValue(compositionTypeId);
	var compEle = document.getElementById(compEleId);
	if (compositionType != "") {
		CompositionManager.getAssociatedElementOptions(compositionType, function (data) {
			dwr.util.removeAllOptions(entityTypeId);
			if (data != null) {
				dwr.util.addOptions(entityTypeId, [""]);
				dwr.util.addOptions(entityTypeId, data, "dataId", "dataDisplayType");
			}
		});
	} else {
		dwr.util.removeAllOptions(entityTypeId);
	}
	if (compositionType != "Nanoparticle Entity") {
		compEle.style.display = "none";
	}
}
function getAssociatedComposingElements(compositionTypeId, entityTypeId, compEleTypeId, compEleId) {
	var compositionType = dwr.util.getValue(compositionTypeId);
	var compEle = document.getElementById(compEleId);
	if (compositionType == "Nanoparticle Entity") {
		var entityId = dwr.util.getValue(entityTypeId);
		if (entityId != "") {
			CompositionManager.getAssociatedComposingElements(entityId, function (data) {
				dwr.util.removeAllOptions(compEleTypeId);
				if (data != null) {
					dwr.util.addOptions(compEleTypeId, [""]);
					dwr.util.addOptions(compEleTypeId, data, "domainComposingElementId", "displayName");
				}
			});
		} else {
			dwr.util.removeAllOptions(compEleTypeId);
		}
		compEle.style.display = "inline";
	} else {
		dwr.util.removeAllOptions(compEleTypeId);
		compEle.style.display = "none";
	}
	return false;
}
function displayBondType() {
	var type = document.getElementById("assoType").value;
	var btTitleEle = document.getElementById("bondTypeTitle");
	var btLineEle = document.getElementById("bondTypeLine");
	if (type == "attachment") {
		btTitleEle.style.display = "inline";
		btLineEle.style.display = "inline";
	} else {
		btTitleEle.style.display = "none";
		btLineEle.style.display = "none";
	}
}
function setCompositionType(entityTypeId, displayNameEleId) {
	var selectEle = document.getElementById(entityTypeId);
	var selectedName = selectEle.options[selectEle.options.selectedIndex].text;
	document.getElementById(displayNameEleId).value = selectedName;
	// alert(document.getElementById(displayNameEleId).value);
}
function setEntityDisplayName(entityTypeId, displayNameEleId) {
	var selectEle = document.getElementById(entityTypeId);
	var selectedName = selectEle.options[selectEle.options.selectedIndex].text;
	document.getElementById(displayNameEleId).value = selectedName;
	// alert(document.getElementById(displayNameEleId).value);
}
/*
 * the following functions using AJAX to display modality dropdown menu in the
 * bodyNanoparticleEntityUpdate.jsp and bodyFunctionUpdate.jsp
 *
 */
/*
function setModalityTypeOptions(compEleIndex, functionIndex) {
	var functionType = dwr.util.getValue("funcType_" + compEleIndex + "_" + functionIndex);
	CompositionManager.getModalityTypeOptions(functionType, function(data) {

			dwr.util.removeAllOptions("modalityType_" + compEleIndex + "_" + functionIndex);
			dwr.util.addOptions("modalityType_" + compEleIndex + "_" + functionIndex, ['']);
    		dwr.util.addOptions("modalityType_" + compEleIndex + "_" + functionIndex, data);
    		dwr.util.addOptions("modalityType_" + compEleIndex + "_" + functionIndex, ['[Other]']);
  	});
}

function setModalityInclude(compEleIndex, functionIndex) {
	var functionType = dwr.util.getValue("funcType_" + compEleIndex + "_" + functionIndex);

	CompositionManager.getModalityIncludePage(compEleIndex, functionIndex, functionType, function(pageData) {

		document.getElementById("modalityTypeTd_" + compEleIndex + "_" + functionIndex).innerHTML = "";
		dwr.util.setValue("modalityTypeTd_" + compEleIndex + "_" + functionIndex, pageData, {escapeHtml:false});

	});

  	if(functionType != "imaging") {
  		document.getElementById("modalityType_" + compEleIndex + "_" + functionIndex).innerHTML =
  			" ";
  	}
}
*/

<|endoftext|>


<commit_before>var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
  __hasProp = {}.hasOwnProperty,
  __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

module.exports = function(env) {
  var M, PioRemotePlugin, PioRemoteActionProvider, PioRemoteActionHandler, pioRemotePlugin, pioRemoteActionHandler, client, pluginConfig, connected;
  M = env.matcher;

  var Promise = env.require('bluebird');
  var net = env.require('net');
  var util = env.require('util');
  var assert = env.require('cassert');

  /**
  * Default connection parameters
  **/
  var defaultHost = '192.168.0.15';
  var defaultPort= 23;
  var defaultMaxVolume = 100;

  var currentVolume = 0;
  var currentDisplay = '';

  /**
  * Additional codes (control iPod etc.) could be found here:
  * http://www.pioneerelectronics.com/StaticFiles/PUSA/Files/Home%20Custom%20Install/VSX-1120-K-RS232.PDF
  **/
  var controlCodes = {};

  /**
  * codes for handling the volume
  * xxxVL range is from 000 (mute) to 185 (+12dB)
  * but should be limited to 100 (-30,5dB)
  **/
  controlCodes.volume = {
    'down':     'VD',
    'up':       'VU',
    'set':      'VL',
    'mute':     'MZ',
    'status':   '?V'
  };

  /**
  * codes for handling the power
  **/
  controlCodes.power = {
  	'on':       'PO',
    'off':      'PF',
    'status':   '?Q'
  };

  /**
  * codes for handling the input
  **/
  controlCodes.input = {
    'phono':    '00FN',
    'cd':       '01FN',
    'cdr_tape': '03FN',
    'dvd':      '04FN',
    'tv_sat':   '05FN',
    'video1':   '10FN',
    'video2':   '14FN',
    'dvr_bdr':  '15FN',
    'ipod_usb': '17FN',
    'hdmi1':    '19FN',
    'hdmi2':    '20FN',
    'hdmi3':    '21FN',
    'hdmi4':    '22FN',
    'hdmi5':    '23FN',
    'hdmi6':    '24FN',
    'bd':       '25FN',
    'hmg':      '26FN',
    'internet_radio':'38FN',
    'change':'FU',
    'status':   '?F'
  };

  /**
   * Gets the current displayed information
  **/
  controlCodes.display = {
    'status':   'STS'
  };

  /**
   * codes for handling the cursor
   **/
  controlCodes.cursor = {
    'up':       'CUP',
    'down':     'CDN',
    'right':    'CRI',
    'left':     'CLE',
    'enter':    'CEN',
    'back':     'CRT'
  };

  /**
   * THE PLUGIN ITSELF
  **/
  PioRemotePlugin = (function(_super) {
    __extends(PioRemotePlugin, _super);

    function PioRemotePlugin() {
      this.init = __bind(this.init, this);
      return PioRemotePlugin.__super__.constructor.apply(this, arguments);
    }

    PioRemotePlugin.prototype.init = function(app, framework, config) {
      var deviceConfigDef;
      this.framework = framework;
      this.config = config;
      pluginConfig = config;
      this.framework.ruleManager.addActionProvider(new PioRemoteActionProvider(this.framework));

      deviceConfigDef = require("./device-config-schema");

      this.framework.deviceManager.registerDeviceClass("AVRSensor", {
        configDef: deviceConfigDef.AVRSensor,
        createCallback: (function(_this) {
          return function(config) {
            return new AVRSensor(config, framework);
          };
        })(this)
      });
    };

    return PioRemotePlugin;

  })(env.plugins.Plugin);

  pioRemotePlugin = new PioRemotePlugin;
  /**
   * THE ACTION PROVIDER
  **/
  PioRemoteActionProvider = (function(_super) {
    __extends(PioRemoteActionProvider, _super);

    function PioRemoteActionProvider(framework, config) {
      this.framework = framework;
      this.config = config;
      this.connectAction = __bind(this.connectAction, this);
      this.myOptionKeys = ['connect'];
      //this.configWithDefaults = _.assign(config.__proto__, config);
    }

    /**
     *This function handles action in the form of `sendAvr "some string"`
    **/

    PioRemoteActionProvider.prototype.parseAction = function(input, context) {
      var commandTokens, fullMatch, m, match, onEnd, retVal, setCommand;
      retVal = null;
      commandTokens = null;
      fullMatch = false;
      setCommand = (function(_this) {
        return function(m, tokens) {
          return commandTokens = tokens;
        };
      })(this);
      onEnd = (function(_this) {
        return function() {
          return fullMatch = true;
        };
      })(this);
      m = M(input, context).match("sendAvr ").matchStringWithVars(setCommand);
      if (m.hadMatch()) {
        match = m.getFullMatch();
        return {
          token: match,
          nextInput: input.substring(match.length),
          actionHandler: new PioRemoteActionHandler(this.framework, commandTokens)
        };
      } else {
        return null;
      }
    };

    return PioRemoteActionProvider;

  })(env.actions.ActionProvider);

  /**
   * THE ACTION HANDLER
  **/

  PioRemoteActionHandler = (function(_super) {
    __extends(PioRemoteActionHandler, _super);

    function PioRemoteActionHandler(framework, commandTokens) {
      this.framework = framework;
      this.commandTokens = commandTokens;
      this.executeAction = __bind(this.executeAction, this);
      this.connect = __bind(this.connect, this);
      this.disconnect = __bind(this.disconnect, this);
      this.sendCommand = __bind(this.sendCommand, this);
    }

    /**
    * Method for establishing connection to the receiver
    **/
    PioRemoteActionHandler.prototype.connect = function(command) {
      if(!client || !connected) {
        env.logger.info('Client isn\'t connected yet, establish new connection...');

        client = new net.Socket();

        client.on('data', function(data) {
          //env.logger.debug('Received: ' + data);

          var stringyfiedData = data.toString();

          if(stringyfiedData.indexOf('VOL') === 0) {
            currentVolume = (0.5 * stringyfiedData.substring(3,6) - 80.5);
          } else if(stringyfiedData.indexOf('FL') === 0) {
            var str = '';
            for (var i = 4; i < stringyfiedData.length; i += 2) {
              str += String.fromCharCode(parseInt(stringyfiedData.substr(i, 2), 16));
            }

            /**
            * Cut text to 15 characters
            **/
            if(str.length > 15) {
              str = str.substring(0, 15);
            }

            //currentDisplay = str.trim();
            currentDisplay = str;
          } else {
            env.logger.debug('Received: ' + data);
          }
        });

        /**
        * this event is triggered, if the connection is successfully established
        **/
        client.on('connect', function() {
          env.logger.info('Connection successfully established');
          connected = true;
        });

        client.on('error', function(ex) {
          env.logger.info('An error occured during connecting to avr: ' + ex);
          connected = false;
        });

        client.on('close', function() {
          if(client) {
            env.logger.info('Connection closed');
            client.disconnect();
          }
        });

        client.connect(pluginConfig.port ? pluginConfig.port : defaultPort, pluginConfig.host ? pluginConfig.host : defaultHost, function() {
          if(command) {
            /**
            * assuming everything went okay, because there is no callback
            **/
            return pioRemoteActionHandler.sendCommand(command);
          } else {
            return 'done';
          }
        });
      } else {
        env.logger.info('Client already connected, nothing to do.');
      }

      return 'done';
    };

    /**
    * Method to call when connection isn't needed any longer
    **/
    PioRemoteActionHandler.prototype.disconnect = function() {
      if(client) {
        env.logger.info('Closing telnet session...');
        client.destroy();
        client = undefined;
        connected = false;

        return 'disconnected';
      } else {
        env.logger.info('Nothing to disconnect from.');
      }

      return 'disconnected';
    };

    /**
    * Method for sending a command
    **/
    PioRemoteActionHandler.prototype.sendCommand = function(command) {
      /**
      * parse the command
      **/
      if(command && command.indexOf('\.') > 0) {
        var splittedCommand = command.split('\.');
        var category = splittedCommand[0];
        var func = splittedCommand[1];

        var value = '';

        /**
         * Handle max volume to avoid damage on user and equipment
        **/
        if(category === 'volume' && func === 'up') {
          var volLevel = (currentVolume + 80.5) * 2;

          if(volLevel >= pluginConfig.maxVolume) {
            return 'Vol max reached!';
          }
        }

        if(category === 'volume' && func === 'down') {
          if(currentVolume > 0) {
            return 'Vol min reached!';
          }
        }

        if(splittedCommand.size === 3) {
          value = splittedCommand[2];
          if(category === 'volume' && value > defaultMaxVolume) {
            value = defaultMaxVolume;
          }

          /**
           * Catch negative values
          **/
          if(category === 'volume' && value < 0) {
            value = 0;
          }
        }

        if(controlCodes[category][func]) {
          var dataToWrite = value + controlCodes[category][func] + '\r';

          var dataSent = client.write(dataToWrite);
          if(dataSent) {
            return 'Sent command: ' + dataToWrite;
          } else {
            return 'An error occured while sending data: ' + dataToWrite;
          }
        }
      } else {
        return 'Unsupported command!';
      }
    };

    /**
    * This function handles action in the form of `sendAVR "command"`
    **/
    PioRemoteActionHandler.prototype.executeAction = function() {
      return this.framework.variableManager.evaluateStringExpression(this.commandTokens).then((function(_this) {
        return function(command) {
          switch(command) {
            case 'connect':
              return pioRemoteActionHandler.connect();
              break;
            case 'disconnect':
              return pioRemoteActionHandler.disconnect();
              break;
            default:
              /**
              * If no connection to the AVR exists, connect first and send the command afterwards
              **/
              if(!client || !connected) {
                return pioRemoteActionHandler.connect(command);
				      } else {
					      return pioRemoteActionHandler.sendCommand(command);
				      }
          }
        };

      })(this));
    };

    return PioRemoteActionHandler;

  })(env.actions.ActionHandler);
  pioRemoteActionHandler = new PioRemoteActionHandler;

  /**
  * THE VOLUME SENSOR
  **/
  AVRSensor = (function(_super) {
    __extends(AVRSensor, _super);

    function AVRSensor(config, framework) {
      var attr;
      this.config = config;
      this.id = config.id;
      this.name = config.name;
      this.attributes = {};

      var func = (function(_this) {
        return function(attr) {
          var name = attr.name;
          env.logger.info('#### ATTR NAME: ' + name);

          assert(name === 'vol' || name === 'display');

          switch (name) {
            case 'vol':
              _this.attributes[name] = {
                description: name,
                type: "number"
              };

              var getter = (function() {
                if(!client) {
                  pioRemoteActionHandler.connect();
                } else {
                  pioRemoteActionHandler.sendCommand('volume.status');
                }
                return Promise.resolve(currentVolume);
              });
              _this.attributes[name].unit = 'dB';
              _this.attributes[name].acronym = 'VOL';
              break;
            case 'display':
              _this.attributes[name] = {
                description: name,
                type: "string"
              };

              var getter = (function() {
                if(!client) {
                  pioRemoteActionHandler.connect();
                }
                return Promise.resolve(currentDisplay);
              });
              break;
            default:
              throw new Error("Illegal attribute name: " + name + " in AVRSensor.");
          }

          _this._createGetter(name, getter);

          return setInterval((function() {
            return getter().then(function(value) {
              return _this.emit(name, value);
            })["catch"](function(error) {
              return env.logger.debug(error.stack);
            });
          }), 1000);

        };
      })(this);

      for (var i = 0; i < this.config.attributes.length; i++) {
        attr = this.config.attributes[i];
        func(attr);
      }

      AVRSensor.__super__.constructor.call(this);
    }

    return AVRSensor;

  })(env.devices.Sensor);

  module.exports.PioRemoteActionProvider = PioRemoteActionProvider;

  return pioRemotePlugin;
};<commit_msg>added H.M.G. display handler<commit_after>var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
  __hasProp = {}.hasOwnProperty,
  __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

module.exports = function(env) {
  var M, PioRemotePlugin, PioRemoteActionProvider, PioRemoteActionHandler, pioRemotePlugin, pioRemoteActionHandler, client, pluginConfig, connected;
  M = env.matcher;

  var Promise = env.require('bluebird');
  var net = env.require('net');
  var util = env.require('util');
  var assert = env.require('cassert');

  /**
  * Default connection parameters
  **/
  var defaultHost = '192.168.0.15';
  var defaultPort= 23;
  var defaultMaxVolume = 100;

  var currentVolume = 0;
  var currentDisplay = '';
  var hmgDisplay = '';
  var currentInput = '';

  /**
  * Additional codes (control iPod etc.) could be found here:
  * http://www.pioneerelectronics.com/StaticFiles/PUSA/Files/Home%20Custom%20Install/VSX-1120-K-RS232.PDF
  **/
  var controlCodes = {};

  /**
  * codes for handling the volume
  * xxxVL range is from 000 (mute) to 185 (+12dB)
  * but should be limited to 100 (-30,5dB)
  **/
  controlCodes.volume = {
    'down':     'VD',
    'up':       'VU',
    'set':      'VL',
    'mute':     'MZ',
    'status':   '?V'
  };

  /**
  * codes for handling the power
  **/
  controlCodes.power = {
  	'on':       'PO',
    'off':      'PF',
    'status':   '?Q'
  };

  /**
  * codes for handling the input
  **/
  controlCodes.input = {
    'phono':    '00FN',
    'cd':       '01FN',
    'cdr_tape': '03FN',
    'dvd':      '04FN',
    'tv_sat':   '05FN',
    'video1':   '10FN',
    'video2':   '14FN',
    'dvr_bdr':  '15FN',
    'ipod_usb': '17FN',
    'hdmi1':    '19FN',
    'hdmi2':    '20FN',
    'hdmi3':    '21FN',
    'hdmi4':    '22FN',
    'hdmi5':    '23FN',
    'hdmi6':    '24FN',
    'bd':       '25FN',
    'hmg':      '26FN',
    'internet_radio':'38FN',
    'change':'FU',
    'status':   '?F'
  };

  /**
   * Gets the current displayed information
  **/
  controlCodes.display = {
    'status':   'STS'
  };

  /**
   * codes for handling the cursor
   **/
  controlCodes.cursor = {
    'up':       'CUP',
    'down':     'CDN',
    'right':    'CRI',
    'left':     'CLE',
    'enter':    'CEN',
    'back':     'CRT'
  };

  /**
   * THE PLUGIN ITSELF
  **/
  PioRemotePlugin = (function(_super) {
    __extends(PioRemotePlugin, _super);

    function PioRemotePlugin() {
      this.init = __bind(this.init, this);
      return PioRemotePlugin.__super__.constructor.apply(this, arguments);
    }

    PioRemotePlugin.prototype.init = function(app, framework, config) {
      var deviceConfigDef;
      this.framework = framework;
      this.config = config;
      pluginConfig = config;
      this.framework.ruleManager.addActionProvider(new PioRemoteActionProvider(this.framework));

      deviceConfigDef = require("./device-config-schema");

      this.framework.deviceManager.registerDeviceClass("AVRSensor", {
        configDef: deviceConfigDef.AVRSensor,
        createCallback: (function(_this) {
          return function(config) {
            return new AVRSensor(config, framework);
          };
        })(this)
      });
    };

    return PioRemotePlugin;

  })(env.plugins.Plugin);

  pioRemotePlugin = new PioRemotePlugin;
  /**
   * THE ACTION PROVIDER
  **/
  PioRemoteActionProvider = (function(_super) {
    __extends(PioRemoteActionProvider, _super);

    function PioRemoteActionProvider(framework, config) {
      this.framework = framework;
      this.config = config;
      this.connectAction = __bind(this.connectAction, this);
      this.myOptionKeys = ['connect'];
      //this.configWithDefaults = _.assign(config.__proto__, config);
    }

    /**
     *This function handles action in the form of `sendAvr "some string"`
    **/

    PioRemoteActionProvider.prototype.parseAction = function(input, context) {
      var commandTokens, fullMatch, m, match, onEnd, retVal, setCommand;
      retVal = null;
      commandTokens = null;
      fullMatch = false;
      setCommand = (function(_this) {
        return function(m, tokens) {
          return commandTokens = tokens;
        };
      })(this);
      onEnd = (function(_this) {
        return function() {
          return fullMatch = true;
        };
      })(this);
      m = M(input, context).match("sendAvr ").matchStringWithVars(setCommand);
      if (m.hadMatch()) {
        match = m.getFullMatch();
        return {
          token: match,
          nextInput: input.substring(match.length),
          actionHandler: new PioRemoteActionHandler(this.framework, commandTokens)
        };
      } else {
        return null;
      }
    };

    return PioRemoteActionProvider;

  })(env.actions.ActionProvider);

  /**
   * THE ACTION HANDLER
  **/

  PioRemoteActionHandler = (function(_super) {
    __extends(PioRemoteActionHandler, _super);

    function PioRemoteActionHandler(framework, commandTokens) {
      this.framework = framework;
      this.commandTokens = commandTokens;
      this.executeAction = __bind(this.executeAction, this);
      this.connect = __bind(this.connect, this);
      this.disconnect = __bind(this.disconnect, this);
      this.sendCommand = __bind(this.sendCommand, this);
    }

    /**
    * Method for establishing connection to the receiver
    **/
    PioRemoteActionHandler.prototype.connect = function(command) {
      if(!client || !connected) {
        env.logger.info('Client isn\'t connected yet, establish new connection...');

        client = new net.Socket();

        client.on('data', function(data) {
          //env.logger.debug('Received: ' + data);

          var stringyfiedData = data.toString();

          if(stringyfiedData.indexOf('VOL') === 0) {
            currentVolume = (0.5 * stringyfiedData.substring(3,6) - 80.5);
          } else if(stringyfiedData.indexOf('FL02') === 0) { // handle default display
            var str = '';
            for (var i = 4; i < stringyfiedData.length; i += 2) {
              str += String.fromCharCode(parseInt(stringyfiedData.substr(i, 2), 16));
            }

            /**
            * Cut text to 15 characters
            **/
            if(str.length > 15) {
              str = str.substring(0, 15);
            }

            if(hmgDisplay.length) {
              currentDisplay = hmgDisplay;
            } else {
              currentDisplay = str;
            }
          } else if(stringyfiedData.indexOf('FL00') === 0) { // Handle volume display
            var str = '';
            for (var i = 4; i < stringyfiedData.length; i += 2) {
              str += String.fromCharCode(parseInt(stringyfiedData.substr(i, 2), 16));
            }

            currentDisplay = str;
          } else if(stringyfiedData.indexOf('FN') === 0) { // handle input display
            currentInput = stringyfiedData;
          } else if(stringyfiedData.indexOf('GEH01020') >= 0) { // handle H.M.G. display
            var start = stringyfiedData.indexOf('GEH01020') + 9;
            var stop = stringyfiedData.indexOf('GEH02023');
            hmgDisplay = stringyfiedData.substring(start, stop);
          } else {
            env.logger.info('Received: ' + data);
          }
        });

        /**
        * this event is triggered, if the connection is successfully established
        **/
        client.on('connect', function() {
          env.logger.info('Connection successfully established');
          connected = true;
        });

        client.on('error', function(ex) {
          env.logger.info('An error occured during connecting to avr: ' + ex);
          connected = false;
        });

        client.on('close', function() {
          if(client) {
            env.logger.info('Connection closed');
            client.disconnect();
          }
        });

        client.connect(pluginConfig.port ? pluginConfig.port : defaultPort, pluginConfig.host ? pluginConfig.host : defaultHost, function() {
          if(command) {
            /**
            * assuming everything went okay, because there is no callback
            **/
            return pioRemoteActionHandler.sendCommand(command);
          } else {
            return 'done';
          }
        });
      } else {
        env.logger.info('Client already connected, nothing to do.');
      }

      return 'done';
    };

    /**
    * Method to call when connection isn't needed any longer
    **/
    PioRemoteActionHandler.prototype.disconnect = function() {
      if(client) {
        env.logger.info('Closing telnet session...');
        client.destroy();
        client = undefined;
        connected = false;

        return 'disconnected';
      } else {
        env.logger.info('Nothing to disconnect from.');
      }

      return 'disconnected';
    };

    /**
    * Method for sending a command
    **/
    PioRemoteActionHandler.prototype.sendCommand = function(command) {
      /**
      * parse the command
      **/
      if(command && command.indexOf('\.') > 0) {
        var splittedCommand = command.split('\.');
        var category = splittedCommand[0];
        var func = splittedCommand[1];

        var value = '';

        /**
         * Handle max volume to avoid damage on user and equipment
        **/
        if(category === 'volume' && func === 'up') {
          var volLevel = (currentVolume + 80.5) * 2;

          if(volLevel >= pluginConfig.maxVolume) {
            currentDisplay = 'Vol max reached!';
            return 'Vol max reached!';
          }
        }

        if(category === 'volume' && func === 'down') {
          if(currentVolume > 0) {
            currentDisplay = 'Vol min reached!';
            return 'Vol min reached!';
          }
        }

        if(splittedCommand.size === 3) {
          value = splittedCommand[2];
          if(category === 'volume' && value > defaultMaxVolume) {
            value = defaultMaxVolume;
          }

          /**
           * Catch negative values
          **/
          if(category === 'volume' && value < 0) {
            value = 0;
          }
        }

        if(controlCodes[category][func]) {
          var dataToWrite = value + controlCodes[category][func] + '\r';

          var dataSent = client.write(dataToWrite);
          if(dataSent) {
            return 'Sent command: ' + dataToWrite;
          } else {
            return 'An error occured while sending data: ' + dataToWrite;
          }
        }
      } else {
        return 'Unsupported command!';
      }
    };

    /**
    * This function handles action in the form of `sendAVR "command"`
    **/
    PioRemoteActionHandler.prototype.executeAction = function() {
      return this.framework.variableManager.evaluateStringExpression(this.commandTokens).then((function(_this) {
        return function(command) {
          switch(command) {
            case 'connect':
              return pioRemoteActionHandler.connect();
              break;
            case 'disconnect':
              return pioRemoteActionHandler.disconnect();
              break;
            default:
              /**
              * If no connection to the AVR exists, connect first and send the command afterwards
              **/
              if(!client || !connected) {
                return pioRemoteActionHandler.connect(command);
				      } else {
					      return pioRemoteActionHandler.sendCommand(command);
				      }
          }
        };

      })(this));
    };

    return PioRemoteActionHandler;

  })(env.actions.ActionHandler);
  pioRemoteActionHandler = new PioRemoteActionHandler;

  /**
  * THE VOLUME SENSOR
  **/
  AVRSensor = (function(_super) {
    __extends(AVRSensor, _super);

    function AVRSensor(config, framework) {
      var attr;
      this.config = config;
      this.id = config.id;
      this.name = config.name;
      this.attributes = {};

      var func = (function(_this) {
        return function(attr) {
          var name = attr.name;
          env.logger.info('#### ATTR NAME: ' + name);

          assert(name === 'vol' || name === 'display');

          switch (name) {
            case 'vol':
              _this.attributes[name] = {
                description: name,
                type: "number"
              };

              var getter = (function() {
                if(!client) {
                  pioRemoteActionHandler.connect();
                } else {
                  pioRemoteActionHandler.sendCommand('volume.status');
                }
                return Promise.resolve(currentVolume);
              });
              _this.attributes[name].unit = 'dB';
              _this.attributes[name].acronym = 'VOL';
              break;
            case 'display':
              _this.attributes[name] = {
                description: name,
                type: "string"
              };

              var getter = (function() {
                if(!client) {
                  pioRemoteActionHandler.connect();
                }
                return Promise.resolve(currentDisplay);
              });
              break;
            default:
              throw new Error("Illegal attribute name: " + name + " in AVRSensor.");
          }

          _this._createGetter(name, getter);

          return setInterval((function() {
            return getter().then(function(value) {
              return _this.emit(name, value);
            })["catch"](function(error) {
              return env.logger.debug(error.stack);
            });
          }), 1000);

        };
      })(this);

      for (var i = 0; i < this.config.attributes.length; i++) {
        attr = this.config.attributes[i];
        func(attr);
      }

      AVRSensor.__super__.constructor.call(this);
    }

    return AVRSensor;

  })(env.devices.Sensor);

  module.exports.PioRemoteActionProvider = PioRemoteActionProvider;

  return pioRemotePlugin;
};<|endoftext|>


<commit_before>// (function(module) {
  function Brewery (opts) {
    Object.keys(opts).forEach(function(b, index, keys) {
      this[b] = opts[b];
    }, this);
  }

  Brewery.all = [];
  Brewery.ids = [];

  var breweryIds = function() {
    $.getJSON('/data/breweries.json', function(data) {
      data.forEach(function(b){
        if (Brewery.ids.indexOf(b.id) === -1) {
          Brewery.ids.push(b.id);
        }
      });
    });
  };

  Brewery.handleLocationEndpoint = function() {
    Brewery.ids.forEach(function(id) {
      $.get('/locations/' + id, function(data) {
        Brewery.all.push(new Brewery(data));
      });
    });
  };

  Brewery.createTable = function(callback) {
    webDB.execute(
      'CREATE TABLE IF NOT EXISTS breweries (' +
        'id PRIMARY KEY, ' +
        'name VARCHAR(255), ' +
        'description TEXT, ' +
        'website VARCHAR(255), ' +
        'established DATE, ' +
        'isOrganic BOOLEAN, ' +
        'streetAddress VARCHAR(255), ' +
        'phone VARCHAR(255), ' +
        'latitude FLOAT, ' +
        'longitude FLOAT, ' +
        'openToPublic BOOLEAN, ' +
        'hoursOfOperation VARCHAR(255));'
    );
  };

  Brewery.fetchAll = function(callback) {
    webDB.execute('SELECT * FROM breweries', function(rows) {
      if (rows.length) {
        Brewery.loadAll(rows);
        callback();
      } else {
        //TODO: refactor to build brewery table.
        // $getJSON('data/breweries.json', function(rawData) {
        //   rawData.forEach(function(breweryEle) {
        //     Brewery.insertRecord();
        //   });
        //   webDB.execute('SELECT * FROM breweries', function(rows) {
        //     Brewery.loadAll(rows);
        //     callback();
        //   });
        // });
      }
    });
  };

  Brewery.insertLocationRecord = function(callback) {
    webDB.execute(
      [
        {
          'sql': 'INSERT INTO breweries (id, streetAddress, postalCode, phone, latitude, longitude, openToPublic, hoursOfOperation) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
          'data': [this.id, this.streetAddress, this.postalCode, this.phone, this.latitude, this.longitude, this.openToPublic, this.hoursOfOperation],
        }
      ],
      callback
    );
  };

  Brewery.createTable();
  breweryIds();

  // module.Brewery = Brewery;
// })(window);
<commit_msg>breweries table with unique breweryid filled<commit_after>// (function(module) {
  function Brewery (opts) {
    Object.keys(opts).forEach(function(b, index, keys) {
      this[b] = opts[b];
    }, this);
  }

  Brewery.all = [];
  Brewery.ids = [];

  var breweryIds = function() {
    $.getJSON('/data/breweries.json', function(data) {
      data.forEach(function(b){
        if (Brewery.ids.indexOf(b.id) === -1) {
          Brewery.ids.push(b.id);
        }
      });
    });
  };

  Brewery.handleLocationEndpoint = function() {
    Brewery.ids.forEach(function(id) {
      $.get('/locations/' + id, function(data) {
        var breweryInstance = new Brewery(data);
        breweryInstance.insertLocationRecord();
      });
    });
  };

  Brewery.loadAll = function(rows) {
    Brewery.all = rows.map(function(b) {
      return new Brewery(b);
    });
  };

  Brewery.idRecord = function() {
    Brewery.ids.forEach(function(id) {
      Brewery.insertBreweryId(id);
    });
  };

  Brewery.insertBreweryId = function(id) {
    webDB.execute(
      [
        {
          'sql': 'INSERT INTO breweries (breweryId) VALUES (?)',
          'data': [id],
        }
      ]
    );
  };

  Brewery.prototype.insertLocationRecord = function(callback) {
    webDB.execute(
      [
        {
          'sql': 'INSERT INTO breweries (streetAddress, postalCode, phone, latitude, longitude, openToPublic, hoursOfOperation) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
          'data': [this.streetAddress, this.postalCode, this.phone, this.latitude, this.longitude, this.openToPublic, this.hoursOfOperation],
        }
      ],
      callback
    );
  };

  Brewery.createTable = function(callback) {
    webDB.execute(
      'CREATE TABLE IF NOT EXISTS breweries (' +
        'id INTEGER PRIMARY KEY, ' +
        'name VARCHAR(255), ' +
        'breweryId, ' +
        'description TEXT, ' +
        'website VARCHAR(255), ' +
        'established DATE, ' +
        'isOrganic BOOLEAN, ' +
        'streetAddress VARCHAR(255), ' +
        'postalCode, ' +
        'phone VARCHAR(255), ' +
        'latitude FLOAT, ' +
        'longitude FLOAT, ' +
        'openToPublic BOOLEAN, ' +
        'hoursOfOperation VARCHAR(255));'
    );
  };

  Brewery.fetchAll = function(callback) {
    webDB.execute('SELECT * FROM breweries', function(rows) {
      if (rows.length) {
        Brewery.loadAll(rows);
        callback();
      } else {
        //TODO: refactor to build brewery table.
        // $getJSON('data/breweries.json', function(rawData) {
        //   rawData.forEach(function(breweryEle) {
        //     Brewery.insertRecord();
        //   });
        //   webDB.execute('SELECT * FROM breweries', function(rows) {
        //     Brewery.loadAll(rows);
        //     callback();
        //   });
        // });
      }
    });
  };

  Brewery.findWhere = function(field, value, callback) {
    webDB.execute(
      [
        {
          sql: 'SELECT * FROM breweries WHERE ' + field + ' = ?;',
          data: [value]
        }
      ],
      callback
    );
  };

  Brewery.createTable();
  breweryIds();

  // module.Brewery = Brewery;
// })(window);
<|endoftext|>