<commit_before>SirTrevor.BlockTransformers = {};

SirTrevor.BlockTransformer = (function() {

  var BlockTransformer = function(options) {
    this.initialize.apply(this, arguments);
  };

  _.extend(BlockTransformer.prototype, {

    WHITESPACE_AND_BR: new RegExp('^(?:\s*
)*\s*$', 'gim'),

    /**
     * These constant are few use with Range.comparePoint
     * https://developer.mozilla.org/en-US/docs/Web/API/range.comparePoint
     */
    TEXT_BEFORE: -1,
    TEXT_AFTER: 1,

    initialize: function() {},

    isTextBlock: function(block) {
      return block.data('type') === SirTrevor.Blocks.Text.prototype.type;
    },

    isHeadingBlock: function(block) {
      return block.data('type') === SirTrevor.Blocks.Heading.prototype.type;
    },

    isQuoteBlock: function(block) {
      return block.data('type') === SirTrevor.Blocks.Quote.prototype.type;
    },

    isStyledBlock: function(block) {
      return this.isQuoteBlock(block) || this.isHeadingBlock(block);
    },

    getBlockFromPosition: function(editor, position) {
      return editor.$wrapper.find('.st-block').eq(position);
    },

    removeParagraphs: function(paragraphs) {
      _.each(paragraphs, function(p) {
        p.parentNode.removeChild(p);
      });
    },

    addBlocks: function(paragraphs, addAt, editor, blockType) {
      _.each(paragraphs, function(paragraph) {
        // Ignore whitespace and
 "paragraphs"
        if (paragraph.innerHTML.match(this.WHITESPACE_AND_BR) === null) {
          editor.createBlock(blockType, { text: paragraph.innerHTML }, addAt);
          addAt += 1; // increment index to account for each block
        }
      }, this);

      return addAt;
    },

    /**
     * Convert an array of block elements into a
     * markdown-like string
     */
    convertParagraphsToText: function(paragraphs) {
      return _.chain(paragraphs)
          .map(function(p) {
            var contents = p.innerHTML;
            if (contents.match('^<')) {
              contents = contents.replace(/
/ig, '').replace(/<\/DIV>/ig, '').replace(/
/ig, '');
            }
            return contents;
          })
          .value() // get filtered array
          .join('\n'); // to string;
    },

    isOnlyWhitespaceParagraphs: function(paragraphs) {
      return _.every(paragraphs, function(p) {
        return p.innerHTML.match(this.WHITESPACE_AND_BR) !== null;
      }, this);
    },

    addTextBlock: function(text, addAt, editor) {
      return editor.createBlock('Text', { text: text }, addAt);
    },

    /**
     * Examines the current selection and retrieves any
     * wholly or partially selected paragraphs
     */
    getSelectedParagraphs: function(range, blockInner) {
      var startContainer = this._getRangeContainerElement(range.startContainer);
      var endContainer = this._getRangeContainerElement(range.endContainer);

      if (startContainer === endContainer) {
        return [startContainer];
      }

      var paragraphs = [startContainer];

      var otherParagraphs = _.filter(blockInner.children, function(el) {
        return range.comparePoint(el, 0) === 0;
      });

      paragraphs = paragraphs.concat(otherParagraphs, endContainer);

      return _.unique(paragraphs);
    },

    /**
     * Return all the paragraphs that are after
     * the selection the text block
     */
    getParagraphsAfterSelection: function(range, blockInner) {
      return this._getParagraphsRelativeToRange(range, this.TEXT_AFTER, blockInner);
    },

    /**
     * Return all the paragraphs that are before
     * the selection the text block
     */
    getParagraphsBeforeSelection: function(range, blockInner) {
      return this._getParagraphsRelativeToRange(range, this.TEXT_BEFORE, blockInner);
    },

    getWholeParagraphsBeforeSelection: function(range, blockInner) {
      var text = Array.prototype.filter.call(blockInner.children, function(child) {
        var childRange = document.createRange();
        childRange.selectNode(child)
        return range.compareBoundaryPoints(Range.END_TO_START, childRange) === 1;
      }.bind(this));
      return text;
    },

    /**
     * Position integer:
     *  1 - After
     * -1 - Before
     *
     * See here: https://developer.mozilla.org/en-US/docs/Web/API/range.comparePoint
     */
    _getParagraphsRelativeToRange: function(range, position, blockInner) {
      var text = Array.prototype.filter.call(blockInner.children, function(child) {
        return range.comparePoint(child, 0) === position;
      }.bind(this));

      return text;
    },

    /**
     * Sometimes range containers will be text fragments
     * or HTML elements
     */
    _getRangeContainerElement: function(container) {
      if (container.nodeName !== 'DIV') {
        if (container.parentNode.nodeName !== 'DIV') {
          return this._getRangeContainerElement(container.parentNode)
        }
        container = container.parentNode;
      }

      return container;
    },

    _getSelectedBlock: function(range) {
      var block = this._getRangeContainerElement(range.startContainer);
      while (!block.classList.contains('st-block')) {
        block = block.parentNode;
      }
      return block;
    },

    ensureLastBlockIsText: function(editor) {
      var totalNumberOfBlocks = editor.blocks.length;
      if (totalNumberOfBlocks > 0) {
        var lastBlock = this.getBlockFromPosition(editor, totalNumberOfBlocks - 1);
        if (this.isStyledBlock(lastBlock)) {
          this.addTextBlock("", totalNumberOfBlocks, editor);
        }
      }
    },

    ensureNoConsecutiveStyledBlocks: function(editor) {
      var totalNumberOfBlocks = editor.blocks.length;
      var currentBlockPosition = 0;
      while (currentBlockPosition < (totalNumberOfBlocks - 1)) {
        var block = this.getBlockFromPosition(editor, currentBlockPosition);
        var nextBlock = this.getBlockFromPosition(editor, currentBlockPosition + 1);
        if (this.isStyledBlock(block) && this.isStyledBlock(nextBlock)) {
          this.addTextBlock("", currentBlockPosition + 1, editor);
          totalNumberOfBlocks = editor.blocks.length;
        }
        currentBlockPosition++;
      }
    },

    merge: function(range, block, blockInner, editor) {
      var blockPosition = editor.getBlockPosition(block);

      // Remove non-editable content before copying
      $(blockInner).find('[contenteditable=false]').remove();
      // Create a text block from the contents of the existing block
      this.addTextBlock(blockInner.innerHTML, blockPosition, editor);

      // remove the old block
      editor.removeBlock(block.id);

      var totalNumberOfBlocks = editor.blocks.length;
      if (totalNumberOfBlocks === 1) {
        return;
      }

      var newlyCreatedTextBlock = this.getBlockFromPosition(editor, blockPosition);
      var previousBlock = this.getBlockFromPosition(editor, blockPosition - 1);
      var nextBlock = this.getBlockFromPosition(editor, blockPosition + 1);

      if (totalNumberOfBlocks === (blockPosition + 1)) {
        // Merge into the block above
        this._mergeIfTextBlock(editor, previousBlock, previousBlock, newlyCreatedTextBlock, blockPosition);
        return;
      }

      if (blockPosition === 0) {
        // if block below is not a quote then merge into it
        this._mergeIfTextBlock(editor, nextBlock, newlyCreatedTextBlock, nextBlock, blockPosition);
      } else {
        // merge top and bottom blocks
        this._mergeIfTextBlock(editor, nextBlock, newlyCreatedTextBlock, nextBlock, blockPosition);
        newlyCreatedTextBlock = this.getBlockFromPosition(editor, blockPosition);
        this._mergeIfTextBlock(editor, previousBlock, previousBlock, newlyCreatedTextBlock, blockPosition);
      }
    },

    mergeTextBlocks: function(editor, firstBlock, secondBlock, blockPositionToInsert) {
      // Remove non-editable content before copying
      firstBlock.find('[contenteditable=false]').remove();
      secondBlock.find('[contenteditable=false]').remove();
      var textFromPreviousBlock = this.convertParagraphsToText(firstBlock.find('.st-text-block').children());
      var textFromNewlyCreatedTextBlock = this.convertParagraphsToText(secondBlock.find('.st-text-block').children());

      var textForNewBlock = '';
      if (textFromPreviousBlock.length > 0 && textFromNewlyCreatedTextBlock.length > 0) {
        textForNewBlock = textFromPreviousBlock + '\n' + textFromNewlyCreatedTextBlock;
      } else if (textFromPreviousBlock.length === 0 && textFromNewlyCreatedTextBlock.length > 0) {
        textForNewBlock = textFromNewlyCreatedTextBlock;
      } else if (textFromPreviousBlock.length > 0 && textFromNewlyCreatedTextBlock.length === 0) {
        textForNewBlock = textFromPreviousBlock;
      }

      this.addTextBlock(textForNewBlock, blockPositionToInsert, editor);
      editor.removeBlock(firstBlock.attr('id'));
      editor.removeBlock(secondBlock.attr('id'));
    },

    _mergeIfTextBlock: function(editor, blockToCheck, firstBlock, secondBlock, blockPosition) {
      if (this.isTextBlock(blockToCheck)) {
        this.mergeTextBlocks(editor, firstBlock, secondBlock, blockPosition);
      }
    },

    split: function(range, block, blockInner, editor, blockType) {
      var position = editor.getBlockPosition(block);
      var paragraphsAfterSelection = this.getParagraphsAfterSelection(range, blockInner);
      var newQuotes = this.getSelectedParagraphs(range, blockInner);
      var paragraphsBeforeSelection = this.getWholeParagraphsBeforeSelection(range, blockInner);

      // Remove non-editable content before copying
      $('[contenteditable=false]', paragraphsAfterSelection).remove();
      $('[contenteditable=false]', newQuotes).remove();

      // Remove the quotes and paragraphs after from the current text block
      this.removeParagraphs([].concat(paragraphsAfterSelection, newQuotes));

      // if paragraphs exist before this new styled block that is going to be added then insert after this paragraph
      if (paragraphsBeforeSelection.length !== 0){
        position++;
      }

      // Add a new quote block for each paragraph that was selected
      position = this.addBlocks(newQuotes, position, editor, blockType);

      // Move text after the selection into a new text block,
      // after the quote block(s) we just created
      if (paragraphsAfterSelection.length !== 0) {
        var textAfter = this.convertParagraphsToText(paragraphsAfterSelection);
        if (textAfter) {
          this.addTextBlock(textAfter, position, editor);
        }
      }

      // Delete current block if it's now empty
      if (this.isOnlyWhitespaceParagraphs(blockInner.children)) {
        editor.removeBlock(block.id);
      }

      this.ensureNoConsecutiveStyledBlocks(editor);
      this.ensureLastBlockIsText(editor);
    }

  });

  BlockTransformer = new BlockTransformer();

  BlockTransformer.extend = extend; // Allow our reconfigurer to be extended.

  return BlockTransformer;

})();
<commit_msg>Fix Whitespace Paragraph detection<commit_after>SirTrevor.BlockTransformers = {};

SirTrevor.BlockTransformer = (function() {

  var BlockTransformer = function(options) {
    this.initialize.apply(this, arguments);
  };

  _.extend(BlockTransformer.prototype, {

    WHITESPACE_AND_BR: new RegExp('^(?:\s*
)*\s*$', 'gi'),

    /**
     * These constant are few use with Range.comparePoint
     * https://developer.mozilla.org/en-US/docs/Web/API/range.comparePoint
     */
    TEXT_BEFORE: -1,
    TEXT_AFTER: 1,

    initialize: function() {},

    isTextBlock: function(block) {
      return block.data('type') === SirTrevor.Blocks.Text.prototype.type;
    },

    isHeadingBlock: function(block) {
      return block.data('type') === SirTrevor.Blocks.Heading.prototype.type;
    },

    isQuoteBlock: function(block) {
      return block.data('type') === SirTrevor.Blocks.Quote.prototype.type;
    },

    isStyledBlock: function(block) {
      return this.isQuoteBlock(block) || this.isHeadingBlock(block);
    },

    getBlockFromPosition: function(editor, position) {
      return editor.$wrapper.find('.st-block').eq(position);
    },

    removeParagraphs: function(paragraphs) {
      _.each(paragraphs, function(p) {
        p.parentNode.removeChild(p);
      });
    },

    addBlocks: function(paragraphs, addAt, editor, blockType) {
      _.each(paragraphs, function(paragraph) {
        // Ignore whitespace and
 "paragraphs"
        if (paragraph.innerHTML.match(this.WHITESPACE_AND_BR) === null) {
          editor.createBlock(blockType, { text: paragraph.innerHTML }, addAt);
          addAt += 1; // increment index to account for each block
        }
      }, this);

      return addAt;
    },

    /**
     * Convert an array of block elements into a
     * markdown-like string
     */
    convertParagraphsToText: function(paragraphs) {
      return _.chain(paragraphs)
          .map(function(p) {
            var contents = p.innerHTML;
            if (contents.match('^<')) {
              contents = contents.replace(/
/ig, '').replace(/<\/DIV>/ig, '').replace(/
/ig, '');
            }
            return contents;
          })
          .value() // get filtered array
          .join('\n'); // to string;
    },

    isOnlyWhitespaceParagraphs: function(paragraphs) {
      return _.every(paragraphs, function(p) {
        return p.innerHTML.match(this.WHITESPACE_AND_BR) !== null;
      }, this);
    },

    addTextBlock: function(text, addAt, editor) {
      return editor.createBlock('Text', { text: text }, addAt);
    },

    /**
     * Examines the current selection and retrieves any
     * wholly or partially selected paragraphs
     */
    getSelectedParagraphs: function(range, blockInner) {
      var startContainer = this._getRangeContainerElement(range.startContainer);
      var endContainer = this._getRangeContainerElement(range.endContainer);

      if (startContainer === endContainer) {
        return [startContainer];
      }

      var paragraphs = [startContainer];

      var otherParagraphs = _.filter(blockInner.children, function(el) {
        return range.comparePoint(el, 0) === 0;
      });

      paragraphs = paragraphs.concat(otherParagraphs, endContainer);

      return _.unique(paragraphs);
    },

    /**
     * Return all the paragraphs that are after
     * the selection the text block
     */
    getParagraphsAfterSelection: function(range, blockInner) {
      return this._getParagraphsRelativeToRange(range, this.TEXT_AFTER, blockInner);
    },

    /**
     * Return all the paragraphs that are before
     * the selection the text block
     */
    getParagraphsBeforeSelection: function(range, blockInner) {
      return this._getParagraphsRelativeToRange(range, this.TEXT_BEFORE, blockInner);
    },

    getWholeParagraphsBeforeSelection: function(range, blockInner) {
      var text = Array.prototype.filter.call(blockInner.children, function(child) {
        var childRange = document.createRange();
        childRange.selectNode(child)
        return range.compareBoundaryPoints(Range.END_TO_START, childRange) === 1;
      }.bind(this));
      return text;
    },

    /**
     * Position integer:
     *  1 - After
     * -1 - Before
     *
     * See here: https://developer.mozilla.org/en-US/docs/Web/API/range.comparePoint
     */
    _getParagraphsRelativeToRange: function(range, position, blockInner) {
      var text = Array.prototype.filter.call(blockInner.children, function(child) {
        return range.comparePoint(child, 0) === position;
      }.bind(this));

      return text;
    },

    /**
     * Sometimes range containers will be text fragments
     * or HTML elements
     */
    _getRangeContainerElement: function(container) {
      if (container.nodeName !== 'DIV') {
        if (container.parentNode.nodeName !== 'DIV') {
          return this._getRangeContainerElement(container.parentNode)
        }
        container = container.parentNode;
      }

      return container;
    },

    _getSelectedBlock: function(range) {
      var block = this._getRangeContainerElement(range.startContainer);
      while (!block.classList.contains('st-block')) {
        block = block.parentNode;
      }
      return block;
    },

    ensureLastBlockIsText: function(editor) {
      var totalNumberOfBlocks = editor.blocks.length;
      if (totalNumberOfBlocks > 0) {
        var lastBlock = this.getBlockFromPosition(editor, totalNumberOfBlocks - 1);
        if (this.isStyledBlock(lastBlock)) {
          this.addTextBlock("", totalNumberOfBlocks, editor);
        }
      }
    },

    ensureNoConsecutiveStyledBlocks: function(editor) {
      var totalNumberOfBlocks = editor.blocks.length;
      var currentBlockPosition = 0;
      while (currentBlockPosition < (totalNumberOfBlocks - 1)) {
        var block = this.getBlockFromPosition(editor, currentBlockPosition);
        var nextBlock = this.getBlockFromPosition(editor, currentBlockPosition + 1);
        if (this.isStyledBlock(block) && this.isStyledBlock(nextBlock)) {
          this.addTextBlock("", currentBlockPosition + 1, editor);
          totalNumberOfBlocks = editor.blocks.length;
        }
        currentBlockPosition++;
      }
    },

    merge: function(range, block, blockInner, editor) {
      var blockPosition = editor.getBlockPosition(block);

      // Remove non-editable content before copying
      $(blockInner).find('[contenteditable=false]').remove();
      // Create a text block from the contents of the existing block
      this.addTextBlock(blockInner.innerHTML, blockPosition, editor);

      // remove the old block
      editor.removeBlock(block.id);

      var totalNumberOfBlocks = editor.blocks.length;
      if (totalNumberOfBlocks === 1) {
        return;
      }

      var newlyCreatedTextBlock = this.getBlockFromPosition(editor, blockPosition);
      var previousBlock = this.getBlockFromPosition(editor, blockPosition - 1);
      var nextBlock = this.getBlockFromPosition(editor, blockPosition + 1);

      if (totalNumberOfBlocks === (blockPosition + 1)) {
        // Merge into the block above
        this._mergeIfTextBlock(editor, previousBlock, previousBlock, newlyCreatedTextBlock, blockPosition);
        return;
      }

      if (blockPosition === 0) {
        // if block below is not a quote then merge into it
        this._mergeIfTextBlock(editor, nextBlock, newlyCreatedTextBlock, nextBlock, blockPosition);
      } else {
        // merge top and bottom blocks
        this._mergeIfTextBlock(editor, nextBlock, newlyCreatedTextBlock, nextBlock, blockPosition);
        newlyCreatedTextBlock = this.getBlockFromPosition(editor, blockPosition);
        this._mergeIfTextBlock(editor, previousBlock, previousBlock, newlyCreatedTextBlock, blockPosition);
      }
    },

    mergeTextBlocks: function(editor, firstBlock, secondBlock, blockPositionToInsert) {
      // Remove non-editable content before copying
      firstBlock.find('[contenteditable=false]').remove();
      secondBlock.find('[contenteditable=false]').remove();
      var textFromPreviousBlock = this.convertParagraphsToText(firstBlock.find('.st-text-block').children());
      var textFromNewlyCreatedTextBlock = this.convertParagraphsToText(secondBlock.find('.st-text-block').children());

      var textForNewBlock = '';
      if (textFromPreviousBlock.length > 0 && textFromNewlyCreatedTextBlock.length > 0) {
        textForNewBlock = textFromPreviousBlock + '\n' + textFromNewlyCreatedTextBlock;
      } else if (textFromPreviousBlock.length === 0 && textFromNewlyCreatedTextBlock.length > 0) {
        textForNewBlock = textFromNewlyCreatedTextBlock;
      } else if (textFromPreviousBlock.length > 0 && textFromNewlyCreatedTextBlock.length === 0) {
        textForNewBlock = textFromPreviousBlock;
      }

      this.addTextBlock(textForNewBlock, blockPositionToInsert, editor);
      editor.removeBlock(firstBlock.attr('id'));
      editor.removeBlock(secondBlock.attr('id'));
    },

    _mergeIfTextBlock: function(editor, blockToCheck, firstBlock, secondBlock, blockPosition) {
      if (this.isTextBlock(blockToCheck)) {
        this.mergeTextBlocks(editor, firstBlock, secondBlock, blockPosition);
      }
    },

    split: function(range, block, blockInner, editor, blockType) {
      var position = editor.getBlockPosition(block);
      var paragraphsAfterSelection = this.getParagraphsAfterSelection(range, blockInner);
      var newQuotes = this.getSelectedParagraphs(range, blockInner);
      var paragraphsBeforeSelection = this.getWholeParagraphsBeforeSelection(range, blockInner);

      // Remove non-editable content before copying
      $('[contenteditable=false]', paragraphsAfterSelection).remove();
      $('[contenteditable=false]', newQuotes).remove();

      // Remove the quotes and paragraphs after from the current text block
      this.removeParagraphs([].concat(paragraphsAfterSelection, newQuotes));

      // if paragraphs exist before this new styled block that is going to be added then insert after this paragraph
      if (paragraphsBeforeSelection.length !== 0){
        position++;
      }

      // Add a new quote block for each paragraph that was selected
      position = this.addBlocks(newQuotes, position, editor, blockType);

      // Move text after the selection into a new text block,
      // after the quote block(s) we just created
      if (paragraphsAfterSelection.length !== 0) {
        var textAfter = this.convertParagraphsToText(paragraphsAfterSelection);
        if (textAfter) {
          this.addTextBlock(textAfter, position, editor);
        }
      }

      // Delete current block if it's now empty
      if (this.isOnlyWhitespaceParagraphs(blockInner.children)) {
        editor.removeBlock(block.id);
      }

      this.ensureNoConsecutiveStyledBlocks(editor);
      this.ensureLastBlockIsText(editor);
    }

  });

  BlockTransformer = new BlockTransformer();

  BlockTransformer.extend = extend; // Allow our reconfigurer to be extended.

  return BlockTransformer;

})();
<|endoftext|>


<commit_before>// @flow
const PIXI = require('pixi.js')
const Ship = require('../common/Ship.js')
const { Howl } = require('howler')

const SMALL_THRUSTER_WIDTH = 0.5
const SMALL_THRUSTER_LONG = 0.4
const ENGINE_VOL_LOW = 0.4
const ENGINE_VOL_HIGH = 0.5
const FIRE_HIGH = 0x007AFF
const FIRE_LOW = 0x00FFD4

const turnSound = new Howl({
  urls: ['sounds/turn-brake2.wav'],
  volume: 0.7
})

const engineSound = new Howl({
  urls: ['sounds/engine.wav'],
  volume: ENGINE_VOL_LOW
})

function isSoundPaused (sound, nodeId) {
  const audioNode = sound._audioNode.find((node) => node.id === nodeId)
  if (audioNode == null) return true // doesn't exist, so consider it paused
  return audioNode.paused
}

class ShipController {
  sprite: PIXI.DisplayObject
  color: number
  frontLeftFire: PIXI.DisplayObject
  rearLeftFire: PIXI.DisplayObject
  frontRightFire: PIXI.DisplayObject
  rearRightFire: PIXI.DisplayObject
  mainFire: PIXI.DisplayObject
  engineSoundId: ?number

  turnLtimer: number
  turnRtimer: number

  constructor (ship: Ship) {
    this.regenerateSprites(ship)
    this.turnLtimer = 0
    this.turnRtimer = 0
  }

  regenerateSprites (ship: Ship) {
    this.color = ship.color
    // chasis
    const sprite = (this.sprite || new PIXI.Graphics())
    sprite.pivot = { x: 1, y: 1 }
    sprite.beginFill(ship.color)
    sprite.moveTo(0.2, 0)
    sprite.lineTo(1.8, 0)
    sprite.lineTo(2, 2)
    sprite.lineTo(0, 2)
    sprite.endFill()

    // chasis peak
    sprite.beginFill(0x000000)
    sprite.fillAlpha = 0.2
    sprite.moveTo(0.7, 0.9)
    sprite.lineTo(1.3, 0.9)
    sprite.lineTo(1.5, 2)
    sprite.lineTo(0.5, 2)
    sprite.endFill()

    this.sprite = sprite

    const mainFire = new PIXI.Graphics()
    mainFire.position = new PIXI.Point(1, 1.85)
    mainFire.beginFill(0xFFFFFF)
    mainFire.drawRect(-0.5, 0, 1, 0.5)
    mainFire.endFill()
    sprite.addChild(mainFire)
    this.mainFire = sprite.mainFire = mainFire

    const frontThrusterHeight = 0.4
    const rearThrusterHeight = 1.5

    const frontLeftFire = new PIXI.Graphics()
    frontLeftFire.beginFill(0xFFFFFF)
    frontLeftFire.drawRect(-SMALL_THRUSTER_WIDTH / 2, -0.1, SMALL_THRUSTER_WIDTH, SMALL_THRUSTER_LONG)
    frontLeftFire.endFill()
    frontLeftFire.position = new PIXI.Point(0.1, frontThrusterHeight)
    frontLeftFire.rotation = Math.PI / 2
    frontLeftFire.visible = false
    frontLeftFire.alpha = 0.5
    sprite.addChild(frontLeftFire)
    this.frontLeftFire = sprite.frontLeftFire = frontLeftFire

    const rearLeftFire = new PIXI.Graphics()
    rearLeftFire.beginFill(0xFFFFFF)
    rearLeftFire.drawRect(-SMALL_THRUSTER_WIDTH / 2, -0.1, SMALL_THRUSTER_WIDTH, SMALL_THRUSTER_LONG)
    rearLeftFire.endFill()
    rearLeftFire.position = new PIXI.Point(0, rearThrusterHeight)
    rearLeftFire.rotation = Math.PI / 2
    rearLeftFire.visible = false
    rearLeftFire.alpha = 0.5
    sprite.addChild(rearLeftFire)
    this.rearLeftFire = sprite.rearLeftFire = rearLeftFire

    const frontRightFire = new PIXI.Graphics()
    frontRightFire.beginFill(0xFFFFFF)
    frontRightFire.drawRect(-SMALL_THRUSTER_WIDTH / 2, -0.1, SMALL_THRUSTER_WIDTH, SMALL_THRUSTER_LONG)
    frontRightFire.endFill()
    frontRightFire.position = new PIXI.Point(1.9, frontThrusterHeight)
    frontRightFire.rotation = -Math.PI / 2
    frontRightFire.visible = false
    frontRightFire.alpha = 0.5
    sprite.addChild(frontRightFire)
    this.frontRightFire = sprite.frontRightFire = frontRightFire

    const rearRightFire = new PIXI.Graphics()
    rearRightFire.beginFill(0xFFFFFF)
    rearRightFire.drawRect(-SMALL_THRUSTER_WIDTH / 2, -0.1, SMALL_THRUSTER_WIDTH, SMALL_THRUSTER_LONG)
    rearRightFire.endFill()
    rearRightFire.position = new PIXI.Point(2, rearThrusterHeight)
    rearRightFire.rotation = -Math.PI / 2
    rearRightFire.visible = false
    rearRightFire.alpha = 0.5
    sprite.addChild(rearRightFire)
    this.rearRightFire = sprite.rearRightFire = rearRightFire
  }

  update (ship: Ship) {
    if (this.color !== ship.color) this.regenerateSprites(ship)

    const { position, angle, input } = ship
    this.sprite.position = new PIXI.Point(position[0], position[1])
    this.sprite.rotation = angle

    // turning sound
    if (input.turnL || input.turnR) turnSound.play(() => {})

    let boost = 0
    if (input.gas) boost = input.boost ? 2 : 1

    // thruster animation
    const thrusters = [
      this.mainFire,
      this.frontLeftFire,
      this.rearLeftFire,
      this.frontRightFire,
      this.rearRightFire
    ]
    thrusters.forEach((thruster, k) => {
      // random flame shiverring
      thruster.scale.x = 1 - Math.random() * 0.5
      thruster.scale.y = Math.random() * 1 + 1

      // main thruster flame scale depending on gas+boost
      if (k === 0) thruster.scale.y *= boost

      // randomly select color for flame
      thruster.tint = Math.random() < 0.5
        ? FIRE_HIGH
        : FIRE_LOW
    })

    // active / deactivate support thrusters' sprites
    const timerCount = 7
    if (input.turnL) this.turnLtimer = timerCount
    if (input.turnR) this.turnRtimer = timerCount
    const displayTurnRight = (this.turnRtimer > 0)
    const displayTurnLeft = (this.turnLtimer > 0)
    this.turnRtimer--
    this.turnLtimer--

    this.frontLeftFire.visible = input.leanR || displayTurnRight
    this.rearLeftFire.visible = input.leanR || displayTurnLeft
    this.frontRightFire.visible = input.leanL || displayTurnLeft
    this.rearRightFire.visible = input.leanL || displayTurnRight
    let { engineSoundId } = this

    if (boost > 0 || input.leanL || input.leanR) {
      if (isSoundPaused(engineSound, engineSoundId) ||
         engineSound.pos(undefined, engineSoundId) > 0.1) {
        if (engineSoundId != null) engineSound.stop(engineSoundId)
        engineSound.play((nodeId) => { this.engineSoundId = nodeId })
      }

      const volume = boost === 2
        ? ENGINE_VOL_HIGH
        : ENGINE_VOL_LOW
      engineSound.volume(volume, engineSoundId)
    } else if (engineSoundId != null) {
      engineSound.stop(engineSoundId)
      this.engineSoundId = null
    }
  }
}

module.exports = ShipController
<commit_msg>Render boosts under ship/chasis<commit_after>// @flow
const PIXI = require('pixi.js')
const Ship = require('../common/Ship.js')
const { Howl } = require('howler')

const SMALL_THRUSTER_WIDTH = 0.5
const SMALL_THRUSTER_LONG = 0.4
const ENGINE_VOL_LOW = 0.4
const ENGINE_VOL_HIGH = 0.5
const FIRE_HIGH = 0x007AFF
const FIRE_LOW = 0x00FFD4

const turnSound = new Howl({
  urls: ['sounds/turn-brake2.wav'],
  volume: 0.7
})

const engineSound = new Howl({
  urls: ['sounds/engine.wav'],
  volume: ENGINE_VOL_LOW
})

function isSoundPaused (sound, nodeId) {
  const audioNode = sound._audioNode.find((node) => node.id === nodeId)
  if (audioNode == null) return true // doesn't exist, so consider it paused
  return audioNode.paused
}

class ShipController {
  sprite: PIXI.DisplayObject
  color: number
  frontLeftFire: PIXI.DisplayObject
  rearLeftFire: PIXI.DisplayObject
  frontRightFire: PIXI.DisplayObject
  rearRightFire: PIXI.DisplayObject
  mainFire: PIXI.DisplayObject
  engineSoundId: ?number

  turnLtimer: number
  turnRtimer: number

  constructor (ship: Ship) {
    this.regenerateSprites(ship)
    this.turnLtimer = 0
    this.turnRtimer = 0
  }

  regenerateSprites (ship: Ship) {
    this.color = ship.color
    // chasis
    const sprite = (this.sprite || new PIXI.Container())
    sprite.pivot = { x: 1, y: 1 }
    this.sprite = sprite

    const chasis = new PIXI.Graphics()
    chasis.beginFill(ship.color)
    chasis.moveTo(0.2, 0)
    chasis.lineTo(1.8, 0)
    chasis.lineTo(2, 2)
    chasis.lineTo(0, 2)
    chasis.endFill()

    // chasis peak
    chasis.beginFill(0x000000)
    chasis.fillAlpha = 0.2
    chasis.moveTo(0.7, 0.9)
    chasis.lineTo(1.3, 0.9)
    chasis.lineTo(1.5, 2)
    chasis.lineTo(0.5, 2)
    chasis.endFill()
    sprite.chasis = chasis

    const mainFire = new PIXI.Graphics()
    mainFire.position = new PIXI.Point(1, 1.85)
    mainFire.beginFill(0xFFFFFF)
    mainFire.drawRect(-0.5, 0, 1, 0.5)
    mainFire.endFill()
    sprite.addChild(mainFire)
    this.mainFire = sprite.mainFire = mainFire

    const frontThrusterHeight = 0.4
    const rearThrusterHeight = 1.5

    const frontLeftFire = new PIXI.Graphics()
    frontLeftFire.beginFill(0xFFFFFF)
    frontLeftFire.drawRect(-SMALL_THRUSTER_WIDTH / 2, -0.1, SMALL_THRUSTER_WIDTH, SMALL_THRUSTER_LONG)
    frontLeftFire.endFill()
    frontLeftFire.position = new PIXI.Point(0.1, frontThrusterHeight)
    frontLeftFire.rotation = Math.PI / 2
    frontLeftFire.visible = false
    frontLeftFire.alpha = 0.5
    sprite.addChild(frontLeftFire)
    this.frontLeftFire = sprite.frontLeftFire = frontLeftFire

    const rearLeftFire = new PIXI.Graphics()
    rearLeftFire.beginFill(0xFFFFFF)
    rearLeftFire.drawRect(-SMALL_THRUSTER_WIDTH / 2, -0.1, SMALL_THRUSTER_WIDTH, SMALL_THRUSTER_LONG)
    rearLeftFire.endFill()
    rearLeftFire.position = new PIXI.Point(0, rearThrusterHeight)
    rearLeftFire.rotation = Math.PI / 2
    rearLeftFire.visible = false
    rearLeftFire.alpha = 0.5
    sprite.addChild(rearLeftFire)
    this.rearLeftFire = sprite.rearLeftFire = rearLeftFire

    const frontRightFire = new PIXI.Graphics()
    frontRightFire.beginFill(0xFFFFFF)
    frontRightFire.drawRect(-SMALL_THRUSTER_WIDTH / 2, -0.1, SMALL_THRUSTER_WIDTH, SMALL_THRUSTER_LONG)
    frontRightFire.endFill()
    frontRightFire.position = new PIXI.Point(1.9, frontThrusterHeight)
    frontRightFire.rotation = -Math.PI / 2
    frontRightFire.visible = false
    frontRightFire.alpha = 0.5
    sprite.addChild(frontRightFire)
    this.frontRightFire = sprite.frontRightFire = frontRightFire

    const rearRightFire = new PIXI.Graphics()
    rearRightFire.beginFill(0xFFFFFF)
    rearRightFire.drawRect(-SMALL_THRUSTER_WIDTH / 2, -0.1, SMALL_THRUSTER_WIDTH, SMALL_THRUSTER_LONG)
    rearRightFire.endFill()
    rearRightFire.position = new PIXI.Point(2, rearThrusterHeight)
    rearRightFire.rotation = -Math.PI / 2
    rearRightFire.visible = false
    rearRightFire.alpha = 0.5
    sprite.addChild(rearRightFire)
    this.rearRightFire = sprite.rearRightFire = rearRightFire

    sprite.addChild(chasis)
  }

  update (ship: Ship) {
    if (this.color !== ship.color) this.regenerateSprites(ship)

    const { position, angle, input } = ship
    this.sprite.position = new PIXI.Point(position[0], position[1])
    this.sprite.rotation = angle

    // turning sound
    if (input.turnL || input.turnR) turnSound.play(() => {})

    let boost = 0
    if (input.gas) boost = input.boost ? 2 : 1

    // thruster animation
    const thrusters = [
      this.mainFire,
      this.frontLeftFire,
      this.rearLeftFire,
      this.frontRightFire,
      this.rearRightFire
    ]
    thrusters.forEach((thruster, k) => {
      // random flame shiverring
      thruster.scale.x = 1 - Math.random() * 0.5
      thruster.scale.y = Math.random() * 1 + 1

      // main thruster flame scale depending on gas+boost
      if (k === 0) thruster.scale.y *= boost

      // randomly select color for flame
      thruster.tint = Math.random() < 0.5
        ? FIRE_HIGH
        : FIRE_LOW
    })

    // active / deactivate support thrusters' sprites
    const timerCount = 7
    if (input.turnL) this.turnLtimer = timerCount
    if (input.turnR) this.turnRtimer = timerCount
    const displayTurnRight = (this.turnRtimer > 0)
    const displayTurnLeft = (this.turnLtimer > 0)
    this.turnRtimer--
    this.turnLtimer--

    this.frontLeftFire.visible = input.leanR || displayTurnRight
    this.rearLeftFire.visible = input.leanR || displayTurnLeft
    this.frontRightFire.visible = input.leanL || displayTurnLeft
    this.rearRightFire.visible = input.leanL || displayTurnRight
    let { engineSoundId } = this

    if (boost > 0 || input.leanL || input.leanR) {
      if (isSoundPaused(engineSound, engineSoundId) ||
         engineSound.pos(undefined, engineSoundId) > 0.1) {
        if (engineSoundId != null) engineSound.stop(engineSoundId)
        engineSound.play((nodeId) => { this.engineSoundId = nodeId })
      }

      const volume = boost === 2
        ? ENGINE_VOL_HIGH
        : ENGINE_VOL_LOW
      engineSound.volume(volume, engineSoundId)
    } else if (engineSoundId != null) {
      engineSound.stop(engineSoundId)
      this.engineSoundId = null
    }
  }
}

module.exports = ShipController
<|endoftext|>


<commit_before>// @flow

const fetch: (url: string, opts?: Object) => Promise<FetchResponse> = require('node-fetch')
const ndjson = require('ndjson')
const byline = require('byline')
const serialize = require('../../metadata/serialize')

import type { Transform as TransformStream, Duplex as DuplexStream } from 'stream'
import type { StatementMsg, SimpleStatementMsg } from '../../protobuf/types'
export type NodeStatus = 'online' | 'offline' | 'public'

const DEFAULT_REQUEST_TIMEOUT = 15000

type FetchResponse = {
  text: () => Promise<string>,
  json: () => Promise<Object>,
  buffer: () => Promise<Buffer>,
  body: TransformStream,
  statusText: string,
  status: number,
  ok: boolean
}

class NDJsonResponse {
  fetchResponse: FetchResponse
  constructor (fetchResponse: FetchResponse) {
    this.fetchResponse = fetchResponse
  }

  stream (): DuplexStream {
    return this.fetchResponse.body.pipe(ndjson.parse())
  }

  values (): Promise<Array<Object>> {
    return new Promise((resolve, reject) => {
      const vals = []
      const stream = this.stream()
      stream.on('data', val => { vals.push(val) })
      stream.on('error', reject)
      stream.on('finish', () => { resolve(vals) })
    })
  }
}

class RestError extends Error {
  response: FetchResponse
  statusCode: number
  constructor (response, responseBody) {
    super(response.statusText + '\n' + responseBody)
    this.statusCode = response.status
    this.response = response
  }
}

class RestClient {
  apiUrl: string;
  client: Function;
  requestTimeout: number;

  constructor (options: {apiUrl?: string, requestTimeout?: number}) {
    this.apiUrl = options.apiUrl || ''
    this.requestTimeout = (options.requestTimeout != null)
      ? options.requestTimeout
      : DEFAULT_REQUEST_TIMEOUT
  }

  _makeUrl (path: string): string {
    const absPath = path.startsWith('/') ? path : '/' + path
    return this.apiUrl + absPath
  }

  req (path: string, args: Object = {}): Promise<FetchResponse> {
    const fullUrl = this._makeUrl(path)
    args = Object.assign({timeout: this.requestTimeout}, args)
    return fetch(fullUrl, args)
      .then(response => {
        if (!response.ok) {
          return response.text().then(responseBody => {
            throw new RestError(response, responseBody)
          })
        }
        return response
      })
  }

  getRequest (path: string): Promise<FetchResponse> {
    return this.req(path)
  }

  postRequest (path: string, body: Object | string, isJSON: boolean = true): Promise<FetchResponse> {
    return this.req(path, {
      method: 'POST',
      headers: isJSON ? { 'Content-Type': 'application/json' } : {},
      body: isJSON ? JSON.stringify(body) : body
    })
  }

  id (peerId?: string): Promise<Object> {
    let path = 'id'
    if (peerId != null) {
      path += '/' + peerId
    }
    return this.getRequest(path)
      .then(r => r.json())
  }

  ping (peerId: string): Promise<boolean> {
    return this.getRequest(`ping/${peerId}`)
      .then(response => true)
  }

  publish (opts: {namespace: string, compound?: number}, ...statements: Array<SimpleStatementMsg>): Promise<Array<string>> {
    const { namespace, compound } = opts
    const statementNDJSON = statements.map(s => JSON.stringify(s)).join('\n')

    let path = `publish/${namespace}`
    if (compound != null) {
      path += '/' + compound.toString()
    }

    return this.postRequest(path, statementNDJSON, false)
      .then(parseStringArrayResponse)
  }

  statement (statementId: string): Promise<StatementMsg> {
    return this.getRequest(`stmt/${statementId}`)
      .then(r => r.json())
  }

  query (queryString: string, remotePeer?: string): Promise<Array<Object>> {
    return this.queryStream(queryString, remotePeer)
      .then(r => r.values())
  }

  queryStream (queryString: string, remotePeer?: string): Promise<NDJsonResponse> {
    let path = 'query'
    if (remotePeer != null) {
      path += '/' + remotePeer
    }
    return this.postRequest(path, queryString, false)
      .then(r => new NDJsonResponse(r))
  }

  merge (queryString: string, remotePeer: string): Promise<{statementCount: number, objectCount: number}> {
    return this.postRequest(`merge/${remotePeer}`, queryString, false)
      .then(parseMergeResponse)
  }

  push (queryString: string, remotePeer: string): Promise<{statementCount: number, objectCount: number}> {
    return this.postRequest(`push/${remotePeer}`, queryString, false)
      .then(parseMergeResponse)
  }

  delete (queryString: string): Promise<number> {
    return this.postRequest('delete', queryString, false)
      .then(parseIntResponse)
  }

  putData (...objects: Array<Object | Buffer>): Promise<Array<string>> {
    const body: string =
      objects.filter(o => o != null)
        .map(o => {
          if (o instanceof Buffer) return o
          try {
            return serialize.encode(o)
          } catch (err) {
            console.error('Serialization error: ' + err.message)
            console.error('Failed Object: ' + JSON.stringify(o, null, 2))
            throw err
          }
        })
        .filter(buf => buf.length > 0)
        .map(buf => ({data: buf.toString('base64')}))
        .map(obj => JSON.stringify(obj))
        .join('\n')

    return this.postRequest('data/put', body, false)
      .then(parseStringArrayResponse)
  }

  getData (objectId: string): Promise<Object | Buffer> {
    return this.getRequest(`data/get/${objectId}`)
      .then(r => r.json())
      .then(o => Buffer.from(o.data, 'base64'))
      .then(bytes => {
        try {
          return serialize.decode(bytes)
        } catch (err) {
          return bytes
        }
      })
  }

  getDatastoreKeys (): Promise<Array<string>> {
    return this.getRequest('data/keys')
      .then(parseStringArrayResponse)
  }

  getDatastoreKeyStream (): Promise<TransformStream> {
    return this.getRequest('data/keys')
      .then(r => byline(r.body))
  }

  garbageCollectDatastore (): Promise<number> {
    return this.postRequest('data/gc', '', false)
      .then(parseIntResponse)
  }

  compactDatastore (): Promise<boolean> {
    return this.postRequest('data/compact', '', false)
      .then(parseBoolResponse)
  }

  syncDatastore (): Promise<boolean> {
    return this.postRequest('data/sync', '', false)
      .then(parseBoolResponse)
  }

  listPeers (): Promise<Array<string>> {
    return this.getRequest('dir/list')
      .then(parseStringArrayResponse)
  }

  getAuthorizations (): Promise<Object> {
    return this.getRequest('auth')
      .then(r => r.json())
  }

  authorize (peerId: string, namespaces: Array<string>): Promise<boolean> {
    return this.postRequest(`auth/${peerId}`, namespaces.join(','), false)
      .then(parseBoolResponse)
  }

  revokeAuthorization (peerId: string): Promise<boolean> {
    return this.authorize(peerId, [])
  }

  getStatus (): Promise<NodeStatus> {
    return this.getRequest('status')
      .then(trimTextResponse)
      .then(validateStatus)
  }

  setStatus (status: NodeStatus): Promise<NodeStatus> {
    return this.postRequest(`status/${status}`, '', false)
      .then(trimTextResponse)
      .then(validateStatus)
  }

  getDirectoryId (): Promise<string> {
    return this.getRequest('config/dir')
      .then(trimTextResponse)
  }

  setDirectoryId (id: string): Promise<boolean> {
    return this.postRequest('config/dir', id, false)
      .then(() => true)
  }

  getInfo (): Promise<string> {
    return this.getRequest('config/info')
      .then(trimTextResponse)
  }

  setInfo (info: string): Promise<string> {
    return this.postRequest('config/info', info, false)
      .then(trimTextResponse)
  }

  getNATConfig (): Promise<string> {
    return this.getRequest('config/nat')
      .then(trimTextResponse)
  }

  setNATConfig (config: string): Promise<boolean> {
    return this.postRequest('config/nat', config, false)
      .then(parseBoolResponse)
  }

  getNetAddresses (): Promise<Array<string>> {
    return this.getRequest('net/addr')
      .then(parseStringArrayResponse)
  }

  netLookup (peerId: string): Promise<Array<string>> {
    return this.getRequest(`net/lookup/${peerId}`)
      .then(parseStringArrayResponse)
  }

  shutdown (): Promise<boolean> {
    return this.postRequest('shutdown', '', false)
      .then(() => true)
      .catch(err => {
        if (err.errno === 'ECONNRESET') {
          return true // shutting down the node kills the request socket :)
        }
        throw err
      })
  }
}

function validateStatus (status: string): NodeStatus {
  status = status.trim()
  switch (status) {
    case 'online':
    case 'offline':
    case 'public':
      return status
  }
  throw new Error(`Unknown status: "${status}"`)
}

function trimTextResponse (response: FetchResponse): Promise<string> {
  return response.text()
    .then(s => s.trim())
}

function parseStringArrayResponse (response: FetchResponse): Promise<Array<string>> {
  return trimTextResponse(response)
    .then(body => body.split('\n').filter(s => s.length > 0))
}

function parseBoolResponse (response: FetchResponse): Promise<boolean> {
  return trimTextResponse(response)
    .then(s => s === 'OK')
}

function parseIntResponse (response: FetchResponse): Promise<number> {
  return trimTextResponse(response)
    .then(s => Number.parseInt(s))
}

function parseIntArrayResponse (response: FetchResponse): Promise<Array<number>> {
  return parseStringArrayResponse(response)
    .then(strings => strings.map(s => Number.parseInt(s)))
}

function parseMergeResponse (response: FetchResponse): Promise<{objectCount: number, statementCount: number}> {
  return parseIntArrayResponse(response)
    .then(counts => {
      const [statementCount, objectCount] = counts
      return {
        statementCount,
        objectCount
      }
    })
}

module.exports = RestClient
<commit_msg>add net/conn and net/addr/:peerId endpoints to RestClient<commit_after>// @flow

const fetch: (url: string, opts?: Object) => Promise<FetchResponse> = require('node-fetch')
const ndjson = require('ndjson')
const byline = require('byline')
const serialize = require('../../metadata/serialize')

import type { Transform as TransformStream, Duplex as DuplexStream } from 'stream'
import type { StatementMsg, SimpleStatementMsg } from '../../protobuf/types'
export type NodeStatus = 'online' | 'offline' | 'public'

const DEFAULT_REQUEST_TIMEOUT = 15000

type FetchResponse = {
  text: () => Promise<string>,
  json: () => Promise<Object>,
  buffer: () => Promise<Buffer>,
  body: TransformStream,
  statusText: string,
  status: number,
  ok: boolean
}

class NDJsonResponse {
  fetchResponse: FetchResponse
  constructor (fetchResponse: FetchResponse) {
    this.fetchResponse = fetchResponse
  }

  stream (): DuplexStream {
    return this.fetchResponse.body.pipe(ndjson.parse())
  }

  values (): Promise<Array<Object>> {
    return new Promise((resolve, reject) => {
      const vals = []
      const stream = this.stream()
      stream.on('data', val => { vals.push(val) })
      stream.on('error', reject)
      stream.on('finish', () => { resolve(vals) })
    })
  }
}

class RestError extends Error {
  response: FetchResponse
  statusCode: number
  constructor (response, responseBody) {
    super(response.statusText + '\n' + responseBody)
    this.statusCode = response.status
    this.response = response
  }
}

class RestClient {
  apiUrl: string;
  client: Function;
  requestTimeout: number;

  constructor (options: {apiUrl?: string, requestTimeout?: number}) {
    this.apiUrl = options.apiUrl || ''
    this.requestTimeout = (options.requestTimeout != null)
      ? options.requestTimeout
      : DEFAULT_REQUEST_TIMEOUT
  }

  _makeUrl (path: string): string {
    const absPath = path.startsWith('/') ? path : '/' + path
    return this.apiUrl + absPath
  }

  req (path: string, args: Object = {}): Promise<FetchResponse> {
    const fullUrl = this._makeUrl(path)
    args = Object.assign({timeout: this.requestTimeout}, args)
    return fetch(fullUrl, args)
      .then(response => {
        if (!response.ok) {
          return response.text().then(responseBody => {
            throw new RestError(response, responseBody)
          })
        }
        return response
      })
  }

  getRequest (path: string): Promise<FetchResponse> {
    return this.req(path)
  }

  postRequest (path: string, body: Object | string, isJSON: boolean = true): Promise<FetchResponse> {
    return this.req(path, {
      method: 'POST',
      headers: isJSON ? { 'Content-Type': 'application/json' } : {},
      body: isJSON ? JSON.stringify(body) : body
    })
  }

  id (peerId?: string): Promise<Object> {
    let path = 'id'
    if (peerId != null) {
      path += '/' + peerId
    }
    return this.getRequest(path)
      .then(r => r.json())
  }

  ping (peerId: string): Promise<boolean> {
    return this.getRequest(`ping/${peerId}`)
      .then(response => true)
  }

  publish (opts: {namespace: string, compound?: number}, ...statements: Array<SimpleStatementMsg>): Promise<Array<string>> {
    const { namespace, compound } = opts
    const statementNDJSON = statements.map(s => JSON.stringify(s)).join('\n')

    let path = `publish/${namespace}`
    if (compound != null) {
      path += '/' + compound.toString()
    }

    return this.postRequest(path, statementNDJSON, false)
      .then(parseStringArrayResponse)
  }

  statement (statementId: string): Promise<StatementMsg> {
    return this.getRequest(`stmt/${statementId}`)
      .then(r => r.json())
  }

  query (queryString: string, remotePeer?: string): Promise<Array<Object>> {
    return this.queryStream(queryString, remotePeer)
      .then(r => r.values())
  }

  queryStream (queryString: string, remotePeer?: string): Promise<NDJsonResponse> {
    let path = 'query'
    if (remotePeer != null) {
      path += '/' + remotePeer
    }
    return this.postRequest(path, queryString, false)
      .then(r => new NDJsonResponse(r))
  }

  merge (queryString: string, remotePeer: string): Promise<{statementCount: number, objectCount: number}> {
    return this.postRequest(`merge/${remotePeer}`, queryString, false)
      .then(parseMergeResponse)
  }

  push (queryString: string, remotePeer: string): Promise<{statementCount: number, objectCount: number}> {
    return this.postRequest(`push/${remotePeer}`, queryString, false)
      .then(parseMergeResponse)
  }

  delete (queryString: string): Promise<number> {
    return this.postRequest('delete', queryString, false)
      .then(parseIntResponse)
  }

  putData (...objects: Array<Object | Buffer>): Promise<Array<string>> {
    const body: string =
      objects.filter(o => o != null)
        .map(o => {
          if (o instanceof Buffer) return o
          try {
            return serialize.encode(o)
          } catch (err) {
            console.error('Serialization error: ' + err.message)
            console.error('Failed Object: ' + JSON.stringify(o, null, 2))
            throw err
          }
        })
        .filter(buf => buf.length > 0)
        .map(buf => ({data: buf.toString('base64')}))
        .map(obj => JSON.stringify(obj))
        .join('\n')

    return this.postRequest('data/put', body, false)
      .then(parseStringArrayResponse)
  }

  getData (objectId: string): Promise<Object | Buffer> {
    return this.getRequest(`data/get/${objectId}`)
      .then(r => r.json())
      .then(o => Buffer.from(o.data, 'base64'))
      .then(bytes => {
        try {
          return serialize.decode(bytes)
        } catch (err) {
          return bytes
        }
      })
  }

  getDatastoreKeys (): Promise<Array<string>> {
    return this.getRequest('data/keys')
      .then(parseStringArrayResponse)
  }

  getDatastoreKeyStream (): Promise<TransformStream> {
    return this.getRequest('data/keys')
      .then(r => byline(r.body))
  }

  garbageCollectDatastore (): Promise<number> {
    return this.postRequest('data/gc', '', false)
      .then(parseIntResponse)
  }

  compactDatastore (): Promise<boolean> {
    return this.postRequest('data/compact', '', false)
      .then(parseBoolResponse)
  }

  syncDatastore (): Promise<boolean> {
    return this.postRequest('data/sync', '', false)
      .then(parseBoolResponse)
  }

  listPeers (): Promise<Array<string>> {
    return this.getRequest('dir/list')
      .then(parseStringArrayResponse)
  }

  getAuthorizations (): Promise<Object> {
    return this.getRequest('auth')
      .then(r => r.json())
  }

  authorize (peerId: string, namespaces: Array<string>): Promise<boolean> {
    return this.postRequest(`auth/${peerId}`, namespaces.join(','), false)
      .then(parseBoolResponse)
  }

  revokeAuthorization (peerId: string): Promise<boolean> {
    return this.authorize(peerId, [])
  }

  getStatus (): Promise<NodeStatus> {
    return this.getRequest('status')
      .then(trimTextResponse)
      .then(validateStatus)
  }

  setStatus (status: NodeStatus): Promise<NodeStatus> {
    return this.postRequest(`status/${status}`, '', false)
      .then(trimTextResponse)
      .then(validateStatus)
  }

  getDirectoryId (): Promise<string> {
    return this.getRequest('config/dir')
      .then(trimTextResponse)
  }

  setDirectoryId (id: string): Promise<boolean> {
    return this.postRequest('config/dir', id, false)
      .then(() => true)
  }

  getInfo (): Promise<string> {
    return this.getRequest('config/info')
      .then(trimTextResponse)
  }

  setInfo (info: string): Promise<string> {
    return this.postRequest('config/info', info, false)
      .then(trimTextResponse)
  }

  getNATConfig (): Promise<string> {
    return this.getRequest('config/nat')
      .then(trimTextResponse)
  }

  setNATConfig (config: string): Promise<boolean> {
    return this.postRequest('config/nat', config, false)
      .then(parseBoolResponse)
  }

  getNetAddresses (peerId: ?string = null): Promise<Array<string>> {
    if (peerId != null) {
      return this.getRequest(`net/addr/${peerId}`)
        .then(parseStringArrayResponse)
    }

    return this.getRequest('net/addr')
      .then(parseStringArrayResponse)
  }

  getNetConnections (): Promise<Array<string>> {
    return this.getRequest('net/conns')
      .then(parseStringArrayResponse)
  }

  netLookup (peerId: string): Promise<Array<string>> {
    return this.getRequest(`net/lookup/${peerId}`)
      .then(parseStringArrayResponse)
  }

  shutdown (): Promise<boolean> {
    return this.postRequest('shutdown', '', false)
      .then(() => true)
      .catch(err => {
        if (err.errno === 'ECONNRESET') {
          return true // shutting down the node kills the request socket :)
        }
        throw err
      })
  }
}

function validateStatus (status: string): NodeStatus {
  status = status.trim()
  switch (status) {
    case 'online':
    case 'offline':
    case 'public':
      return status
  }
  throw new Error(`Unknown status: "${status}"`)
}

function trimTextResponse (response: FetchResponse): Promise<string> {
  return response.text()
    .then(s => s.trim())
}

function parseStringArrayResponse (response: FetchResponse): Promise<Array<string>> {
  return trimTextResponse(response)
    .then(body => body.split('\n').filter(s => s.length > 0))
}

function parseBoolResponse (response: FetchResponse): Promise<boolean> {
  return trimTextResponse(response)
    .then(s => s === 'OK')
}

function parseIntResponse (response: FetchResponse): Promise<number> {
  return trimTextResponse(response)
    .then(s => Number.parseInt(s))
}

function parseIntArrayResponse (response: FetchResponse): Promise<Array<number>> {
  return parseStringArrayResponse(response)
    .then(strings => strings.map(s => Number.parseInt(s)))
}

function parseMergeResponse (response: FetchResponse): Promise<{objectCount: number, statementCount: number}> {
  return parseIntArrayResponse(response)
    .then(counts => {
      const [statementCount, objectCount] = counts
      return {
        statementCount,
        objectCount
      }
    })
}

module.exports = RestClient
<|endoftext|>


<commit_before>/* globals window, _, VIZI, THREE */
(function() {
	"use strict";

	VIZI.Mouse = (function() {
		var Mouse = function(camera) {
			VIZI.Log("Inititialising mouse manager");

			_.extend(this, VIZI.Mediator);

			// Reference to camera for 3D projection
			this.camera = camera;
			this.projector = new THREE.Projector();

			this.state = {
				buttons: {
					left: false,
					right: false,
					middle: false
				},
				pos2d: new THREE.Vector2(),
				downPos2d: new THREE.Vector2(),
				downPos2dDelta: new THREE.Vector2(),
				pos3d: new THREE.Vector3(),
				downPos3d: new THREE.Vector3(),
				pos2dDelta: new THREE.Vector2(),
				pos3dDelta: new THREE.Vector3(),
				wheelDelta: 0,
				camera: {
					startTheta: this.camera.theta,
					startPhi: this.camera.phi
				}
			};

			this.initDOMEvents();
		};

		Mouse.prototype.initDOMEvents = function() {
			var self = this;

			document.addEventListener("mousedown", function(event) {
				self.onMouseDown(event);
			}, false);

			document.addEventListener("mousemove", function(event) {
				self.onMouseMove(event);
			}, false);

			document.addEventListener("mouseup", function(event) {
				self.onMouseUp(event);
			}, false);

			document.addEventListener("mousewheel", function(event) {
				self.onMouseWheel(event);
			}, false);
		};

		Mouse.prototype.onMouseDown = function(event) {
			event.preventDefault();

			var state = this.state;

			if (event.button === 0) {
				state.buttons.left = true;
			}

			if (event.button === 1) {
				state.buttons.middle = true;
			}

			if (event.button === 2) {
				state.buttons.right = true;
			}

			state.pos2d.x = event.clientX;
			state.pos2d.y = event.clientY;

			state.downPos2d.x = event.clientX;
			state.downPos2d.y = event.clientY;

			state.pos2dDelta.x = 0;
			state.pos2dDelta.y = 0;

			var pos3d = this.mouseIn3d(state.downPos2d);

			state.pos3d.x = pos3d.x;
			state.pos3d.y = pos3d.y;
			state.pos3d.z = pos3d.z;

			state.downPos3d.x = pos3d.x;
			state.downPos3d.y = pos3d.y;
			state.downPos3d.z = pos3d.z;

			state.pos3dDelta.x = 0;
			state.pos3dDelta.y = 0;
			state.pos3dDelta.z = 0;

			state.camera.startTheta = this.camera.theta;
			state.camera.startPhi = this.camera.phi;
		};

		Mouse.prototype.onMouseMove = function(event) {
			event.preventDefault();

			var state = this.state;

			state.pos2dDelta.x = event.clientX - state.pos2d.x;
			state.pos2dDelta.y = event.clientY - state.pos2d.y;

			state.pos2d.x = event.clientX;
			state.pos2d.y = event.clientY;

			var pos3d = this.mouseIn3d(state.pos2d);

			state.pos3d.x = pos3d.x;
			state.pos3d.y = pos3d.y;
			state.pos3d.z = pos3d.z;

			if (state.buttons.left) {
				state.downPos2dDelta.x = event.clientX - state.downPos2d.x;
				state.downPos2dDelta.y = event.clientY - state.downPos2d.y;

				state.pos3dDelta.x = state.downPos3d.x - pos3d.x;
				state.pos3dDelta.y = state.downPos3d.y - pos3d.y;
				state.pos3dDelta.z = state.downPos3d.z - pos3d.z;
			}
		};

		Mouse.prototype.onMouseUp = function(event) {
			event.preventDefault();

			var state = this.state;

			if (event.button === 0) {
				state.buttons.left = false;
			}

			if (event.button === 1) {
				state.buttons.middle = false;
			}

			if (event.button === 2) {
				state.buttons.right = false;
			}

			// Reset mouse down positions and deltas
			state.downPos2dDelta.x = 0;
			state.downPos2dDelta.y = 0;
		};

		Mouse.prototype.onMouseWheel = function(event) {
			event.preventDefault();

			var state = this.state;

			state.wheelDelta += event.wheelDeltaY;
		};

		Mouse.prototype.resetDelta = function() {
			var state = this.state;

			state.pos2dDelta.x = 0;
			state.pos2dDelta.y = 0;

			state.pos3dDelta.x = 0;
			state.pos3dDelta.y = 0;
			state.pos3dDelta.z = 0;

			state.wheelDelta = 0;
		};

		Mouse.prototype.mouseIn3d = function(pos2d) {
			var camera = this.camera.camera;

			var vector = new THREE.Vector3(
				( pos2d.x / window.innerWidth ) * 2 - 1,
				- ( pos2d.y / window.innerHeight ) * 2 + 1,
				0.5
			);

			this.projector.unprojectVector( vector, camera );

			var dir = vector.sub( camera.position ).normalize();

			var distance = - camera.position.y / dir.y;

			var pos = camera.position.clone().add( dir.multiplyScalar( distance ) );

			return pos;
		};

		// TODO: Tidy this up
		Mouse.prototype.updateCamera = function() {
			var state = this.state;

			state.downPos2d.x = state.pos2d.x;
			state.downPos2d.y = state.pos2d.y;

			state.downPos2dDelta.x = 0;
			state.downPos2dDelta.y = 0;

			state.camera.startTheta = this.camera.theta;
			state.camera.startPhi = this.camera.phi;
		};

		var instance;

		// an emulation of static variables and methods
		var _static = {
			name: "VIZI.Mouse",

			// Method for getting an instance. It returns
			// a singleton instance of a singleton object
			getInstance: function(camera) {
				if ( instance  ===  undefined )  {
					instance = new Mouse(camera);
				}

				return instance;
			}
		};

		return _static;
	}());
}());<commit_msg>Fix mouse wheel on firefox<commit_after>/* globals window, _, VIZI, THREE */
(function() {
	"use strict";

	VIZI.Mouse = (function() {
		var Mouse = function(camera) {
			VIZI.Log("Inititialising mouse manager");

			_.extend(this, VIZI.Mediator);

			// Reference to camera for 3D projection
			this.camera = camera;
			this.projector = new THREE.Projector();

			this.state = {
				buttons: {
					left: false,
					right: false,
					middle: false
				},
				pos2d: new THREE.Vector2(),
				downPos2d: new THREE.Vector2(),
				downPos2dDelta: new THREE.Vector2(),
				pos3d: new THREE.Vector3(),
				downPos3d: new THREE.Vector3(),
				pos2dDelta: new THREE.Vector2(),
				pos3dDelta: new THREE.Vector3(),
				wheelDelta: 0,
				camera: {
					startTheta: this.camera.theta,
					startPhi: this.camera.phi
				}
			};

			this.initDOMEvents();
		};

		Mouse.prototype.initDOMEvents = function() {
			var self = this;

			document.addEventListener("mousedown", function(event) {
				self.onMouseDown(event);
			}, false);

			document.addEventListener("mousemove", function(event) {
				self.onMouseMove(event);
			}, false);

			document.addEventListener("mouseup", function(event) {
				self.onMouseUp(event);
			}, false);

			document.addEventListener("wheel", function(event) {
				self.onMouseWheel(event);
			}, false);
		};

		Mouse.prototype.onMouseDown = function(event) {
			event.preventDefault();

			var state = this.state;

			if (event.button === 0) {
				state.buttons.left = true;
			}

			if (event.button === 1) {
				state.buttons.middle = true;
			}

			if (event.button === 2) {
				state.buttons.right = true;
			}

			state.pos2d.x = event.clientX;
			state.pos2d.y = event.clientY;

			state.downPos2d.x = event.clientX;
			state.downPos2d.y = event.clientY;

			state.pos2dDelta.x = 0;
			state.pos2dDelta.y = 0;

			var pos3d = this.mouseIn3d(state.downPos2d);

			state.pos3d.x = pos3d.x;
			state.pos3d.y = pos3d.y;
			state.pos3d.z = pos3d.z;

			state.downPos3d.x = pos3d.x;
			state.downPos3d.y = pos3d.y;
			state.downPos3d.z = pos3d.z;

			state.pos3dDelta.x = 0;
			state.pos3dDelta.y = 0;
			state.pos3dDelta.z = 0;

			state.camera.startTheta = this.camera.theta;
			state.camera.startPhi = this.camera.phi;
		};

		Mouse.prototype.onMouseMove = function(event) {
			event.preventDefault();

			var state = this.state;

			state.pos2dDelta.x = event.clientX - state.pos2d.x;
			state.pos2dDelta.y = event.clientY - state.pos2d.y;

			state.pos2d.x = event.clientX;
			state.pos2d.y = event.clientY;

			var pos3d = this.mouseIn3d(state.pos2d);

			state.pos3d.x = pos3d.x;
			state.pos3d.y = pos3d.y;
			state.pos3d.z = pos3d.z;

			if (state.buttons.left) {
				state.downPos2dDelta.x = event.clientX - state.downPos2d.x;
				state.downPos2dDelta.y = event.clientY - state.downPos2d.y;

				state.pos3dDelta.x = state.downPos3d.x - pos3d.x;
				state.pos3dDelta.y = state.downPos3d.y - pos3d.y;
				state.pos3dDelta.z = state.downPos3d.z - pos3d.z;
			}
		};

		Mouse.prototype.onMouseUp = function(event) {
			event.preventDefault();

			var state = this.state;

			if (event.button === 0) {
				state.buttons.left = false;
			}

			if (event.button === 1) {
				state.buttons.middle = false;
			}

			if (event.button === 2) {
				state.buttons.right = false;
			}

			// Reset mouse down positions and deltas
			state.downPos2dDelta.x = 0;
			state.downPos2dDelta.y = 0;
		};

		Mouse.prototype.onMouseWheel = function(event) {
			event.preventDefault();

			var state = this.state;

			state.wheelDelta += event.deltaY;
		};

		Mouse.prototype.resetDelta = function() {
			var state = this.state;

			state.pos2dDelta.x = 0;
			state.pos2dDelta.y = 0;

			state.pos3dDelta.x = 0;
			state.pos3dDelta.y = 0;
			state.pos3dDelta.z = 0;

			state.wheelDelta = 0;
		};

		Mouse.prototype.mouseIn3d = function(pos2d) {
			var camera = this.camera.camera;

			var vector = new THREE.Vector3(
				( pos2d.x / window.innerWidth ) * 2 - 1,
				- ( pos2d.y / window.innerHeight ) * 2 + 1,
				0.5
			);

			this.projector.unprojectVector( vector, camera );

			var dir = vector.sub( camera.position ).normalize();

			var distance = - camera.position.y / dir.y;

			var pos = camera.position.clone().add( dir.multiplyScalar( distance ) );

			return pos;
		};

		// TODO: Tidy this up
		Mouse.prototype.updateCamera = function() {
			var state = this.state;

			state.downPos2d.x = state.pos2d.x;
			state.downPos2d.y = state.pos2d.y;

			state.downPos2dDelta.x = 0;
			state.downPos2dDelta.y = 0;

			state.camera.startTheta = this.camera.theta;
			state.camera.startPhi = this.camera.phi;
		};

		var instance;

		// an emulation of static variables and methods
		var _static = {
			name: "VIZI.Mouse",

			// Method for getting an instance. It returns
			// a singleton instance of a singleton object
			getInstance: function(camera) {
				if ( instance  ===  undefined )  {
					instance = new Mouse(camera);
				}

				return instance;
			}
		};

		return _static;
	}());
}());<|endoftext|>


<commit_before>__resources__["/gameMsgHandler.js"] = {meta: {mimetype: "application/javascript"}, data: function(exports, require, module, __filename, __dirname) {

	var pomelo = window.pomelo;
	var app = require('app');
	var AttackResult = require('consts').AttackResult;
	var mainPanel = require('mainPanelView');
	var dialogPanel = require('dialogPanelView');
	var EntityType = require('consts').EntityType;
	var SkillEffect = require('skillEffect');
	var utils = require('utils');
	var dataApi = require('dataApi');
	var clientManager = require('clientManager');

	exports.init = init;

	function init() {
		/**
		 * Handle change area message
		 * @param  data {Object} The message
		 */
		pomelo.on('onChangeArea', function(data) {
			if(!data.success) {
				return;
			}
			clientManager.loadResource({jsonLoad: false}, function() {
				pomelo.areaId = data.target;
				pomelo.request("area.playerHandler.enterScene",{uid:pomelo.uid, playerId: pomelo.playerId, areaId: pomelo.areaId}, function(msg) {
					app.init(msg);
				});
			});
		});

		/**
		 * Handle add entities message
		 * @param data {Object} The message, contains entities to add
		 */
		pomelo.on('onAddEntities', function(data){
			var entities = data;
			var area = app.getCurArea();

			if(!area) {
				console.warn('entity not exist!');
				return;
			}

			for(var key in entities){
				var array = entities[key];

				if(key === 'npc')
					console.warn('add npc');
				for(var i = 0; i < array.length; i++){
					if(!area.getEntity(array[i].entityId)){
						var entity = utils.buildEntity(key, array[i]);
						area.addEntity(entity);
					}else{
						console.warn('add exist entity!');
					}

				}
			}
		});

		/**
		 * Drop items when the task has been completed.
		 *
		 */
		pomelo.on('onDropItems', function(data) {
			var area = app.getCurArea();
			var items = data.dropItems;
			var length = items.length;
			for (var i = 0; i < length; i ++) {
				area.addEntity(items[i]);
			}
		});

		/**
		 * Handle remove entities message
		 * @param data {Object} The message, contains entitiy ids to remove
		 */
		pomelo.on('onRemoveEntities', function(data){
			var entities = data.entities;
			var area = app.getCurArea();
			var player = area.getCurPlayer();
			for(var i = 0; i < entities.length; i++){
				if(entities[i] !== player.entityId) {
					area.removeEntity(entities[i]);
				}
			}
		});

		/**
		 * Handle move  message
		 * @param data {Object} The message, contains move information
		 */

		pomelo.on('onMove', function(data){
			var path = data.path;
			var character = app.getCurArea().getEntity(data.entityId);
			if(!character){
				//console.log('no character exist for move!' + data.entityId);
				return;
			}

			var sprite = character.getSprite();
			var totalDistance = utils.totalDistance(path);
			var needTime = Math.floor(totalDistance / sprite.getSpeed() * 1000 - app.getDelayTime());
			var speed = totalDistance/needTime * 1000;
			sprite.movePath(path, speed);
		});

		pomelo.on('onPathCheckout', function(data) {
			var player = app.getCurArea().getEntity(data.entityId);
			var serverPosition = data.position;
			var clientposition = player.getSprite().getPosition();
			var realDistance = utils.distance(serverPosition.x, serverPosition.y, clientposition.x, clientposition.y);
			var distanceLimit = 100;

			if (realDistance > distanceLimit) {
				player.getSprite().translateTo(serverPosition.x, serverPosition.y);
			}
		});

		/**
		 * Handle player upgrade message
		 * @param data {Object} The message, contains the info for player upgrade
		 */
		pomelo.on('onUpgrade' , function(data) {
			var playerData = data.player;
			var area = app.getCurArea();
			var player = area.getPlayer(playerData.id);
			player.upgrade(playerData);
			player.getSprite().upgrade();
		});

		/**
		 * Handle update task data message
		 * @param data {Object} The message, contains the info for update task
		 */
		pomelo.on('onUpdateTaskData', function(reData) {
			app.getCurPlayer().updateTaskData(reData);
		});

		/**
		 * Handle task complete message
		 * @param data {Object} The message, contains the info for complete task
		 */
		pomelo.on('onTaskCompleted', function(data) {
			var tasks = app.getCurPlayer().curTasks;
			var completeTask = tasks[data.taskId];
			completeTask.setState(1);
			mainPanel.notify('The task has been completed！');
		});

		/**
		 * Handle remove item message
		 * @param data {Object} The message, contains the info for remove item
		 */
		pomelo.on('onRemoveItem', function(data){
			app.getCurArea().removeEntity(data.entityId);
		});

		/**
		 * Handle pick item message
		 * @param data {Object} The message, contains the info for pick item
		 */
		pomelo.on('onPickItem', function(data) {
			var area = app.getCurArea();
			var player = area.getEntity(data.player);
			var item = area.getEntity(data.item);
			//Only add item for current player
			if (player.entityId === area.getCurPlayer().entityId && !!item) {
				player.bag.addItem({id: item.kindId, type: item.type}, data.index);
			}
			area.removeEntity(data.item);
		});

		/**
		 * Handle npc talk message
		 * @param data {Object} The message, contains the info for npc talk
		 */
		pomelo.on('onNPCTalk', function(data){
			var npc = app.getCurArea().getEntity(data.npc);
			var curPlayer = app.getCurPlayer();
			var dir = {
				x1: curPlayer.getPosition().x,
				y1: curPlayer.getPosition().y,
				x2: npc.getPosition().x,
				y2: npc.getPosition().y
			};
			curPlayer.getSprite().stand(dir);
			dialogPanel.open(data);
		});

		/**
		 * Handle checkout task message
		 * @param data {Object} The message, contains the info for check out task
		 */
		pomelo.on('onCheckoutTask', function(data) {
			dialogPanel.open(data);
		});

		/**
		 * the order of attack, whose result contains success, killed and not_in_range
		 * @param {Object} data  contains attacter, target, result ect.
		 */
		pomelo.on('onAttack', function(data){
			var area = app.getCurArea();
			var skillId = data.skillId;
			var attacker = area.getEntity(data.attacker);
			var target = area.getEntity(data.target);

			if(!attacker || !target){
				console.log('attacker or target not exist ! attacker: ' + data.attacker + ', target : ' + data.target);
				return;
			}

			var attackerSprite = attacker.getSprite();
			var targetSprite = target.getSprite();
			var attackerPos = attackerSprite.getPosition();
			var targetPos = targetSprite.getPosition();
			var resultData = data.result;
			var result = resultData.result;
			var skillEffectParams = {
				id: skillId,
				player: attacker,
				position: {x: targetPos.x - attackerPos.x, y: targetPos.y - attackerPos.y}
			};
			if (app.getCurPlayer().entityId == data.attacker && skillId > 1) {
				mainPanel.skillBox[skillId].start();
			}

			var params ={
				attacker: attacker,
				attackerSprite: attackerSprite,
				target: target,
				targetSprite: targetSprite,
				attackerPos: attackerPos,
				targetPos: targetPos,
				resultData: resultData,
				skillEffectParams: skillEffectParams,
				experience: data.exp
			};
			if (result === AttackResult.SUCCESS) {
				successAction(params);
			} else if (result === AttackResult.KILLED) {
				killedAction(params);
			} else if (result === AttackResult.NOT_IN_RANGE) {
				targetSprite.stand({x1: attackerPos.x, x2:attackerPos.y, y1: targetPos.x, y2: targetPos.y});
			}
			uiUpdate();
		});

		/**
		 * when player revives, it works
		 * @param {Object} data
		 */
		pomelo.on('onRevive', function(data) {
			var area = app.getCurArea();
			var curPlayer = app.getCurPlayer();
			if (curPlayer.entityId !== data.entityId) {
				area.addEntity(data.entity);
			}
			var player = area.getEntity(data.entityId);
			player.died = false;
			player.set('hp', data.hp);
			var sprite = player.getSprite();
			sprite.revive(data, function() {
				if (player.entityId === app.getCurPlayer().entityId) {
					area.map.centerTo(data.x, data.y);
					mainPanel.reviveMaskHide();
				}
			});
		});
	}

	/**
	 * the action invokes when the result is success
	 * @param {Object} data
	 */
	var successAction = function(data) {
		new SkillEffect(data.skillEffectParams).createEffectAni();
		data.attackerSprite.translateTo(data.attackerPos.x, data.attackerPos.y);
		var dir = {x1: data.attackerPos.x, y1: data.attackerPos.y, x2: data.targetPos.x, y2: data.targetPos.y};
		data.targetSprite.createNumberNodes(data.resultData.damage);
		data.attackerSprite.attack(dir, 'noKilled', function() {
		});
		data.target.update({damage: data.resultData.damage});
		data.attacker.update({mpUse: data.resultData.mpUse});
		data.targetSprite.reduceBlood();
	};

	/**
	 * the action invokes when the result is killed
	 * @param {Object} data
	 */
	var killedAction = function(data) {
		if (!!data.target.died) {
			return;
		}
		data.target.died = true;
		if (data.target.type === EntityType.MOB) {
			data.target = null;
		}
		new SkillEffect(data.skillEffectParams).createEffectAni();
		var attackerSprite = data.attackerSprite;
		var attackerPos = data.attackerPos;
		attackerSprite.translateTo(attackerPos.x, attackerPos.y);
		data.targetSprite.zeroBlood();
		if (data.attacker.type === EntityType.PLAYER) {
			data.attacker.update({mpUse: data.resultData.mpUse, experience: data.experience });
		}
		var dir = {x1: data.attackerPos.x, y1: data.attackerPos.y, x2: data.targetPos.x, y2: data.targetPos.y};
		data.targetSprite.createNumberNodes(data.resultData.damage);
		data.attackerSprite.attack(dir, 'killed', function() {
		});
		data.targetSprite.died({x1: data.targetPos.x, y1: data.targetPos.y, x2: data.attackerPos.x, y2: data.attackerPos.y}, function(){
		var items = data.resultData.items;

			if (!!items && items.length > 0) {
				for (var i = 0; i < items.length; i ++) {
					var item = utils.buildEntity(items[i].type, items[i]);
					app.getCurArea().addEntity(item);
				}
			 }
		});
	};

	var uiUpdate = function() {
		var player = app.getCurPlayer();
		player.emit('change:hp');
		player.emit('change:maxHp');
		player.emit('change:mp');
		player.emit('change:maxMp');
		player.emit('change:experience');
	};

	function buildItem(data){
		var item;
		switch(data.type){
			case 'item' :
				item = utils.clone(dataApi.item.findById(data.kindId));
			break;
			case 'equipment' :
				item = utils.clone(dataApi.equipment.findById(data.kindId));
			break;
			default :
				return null;
		}

		item.x = data.x;
		item.y = data.y;
		item.entityId = data.entityId;
		item.playerId = data.playerId;
		item.type = data.type;

		return item;
	}
}};
<commit_msg>fix bug for upgrade<commit_after>__resources__["/gameMsgHandler.js"] = {meta: {mimetype: "application/javascript"}, data: function(exports, require, module, __filename, __dirname) {

	var pomelo = window.pomelo;
	var app = require('app');
	var AttackResult = require('consts').AttackResult;
	var mainPanel = require('mainPanelView');
	var dialogPanel = require('dialogPanelView');
	var EntityType = require('consts').EntityType;
	var SkillEffect = require('skillEffect');
	var utils = require('utils');
	var dataApi = require('dataApi');
	var clientManager = require('clientManager');

	exports.init = init;

	function init() {
		/**
		 * Handle change area message
		 * @param  data {Object} The message
		 */
		pomelo.on('onChangeArea', function(data) {
			if(!data.success) {
				return;
			}
			clientManager.loadResource({jsonLoad: false}, function() {
				pomelo.areaId = data.target;
				pomelo.request("area.playerHandler.enterScene",{uid:pomelo.uid, playerId: pomelo.playerId, areaId: pomelo.areaId}, function(msg) {
					app.init(msg);
				});
			});
		});

		/**
		 * Handle add entities message
		 * @param data {Object} The message, contains entities to add
		 */
		pomelo.on('onAddEntities', function(data){
			var entities = data;
			var area = app.getCurArea();

			if(!area) {
				console.warn('entity not exist!');
				return;
			}

			for(var key in entities){
				var array = entities[key];

				for(var i = 0; i < array.length; i++){
					if(!area.getEntity(array[i].entityId)){
						var entity = utils.buildEntity(key, array[i]);
						area.addEntity(entity);
					}else{
						console.warn('add exist entity!');
					}

				}
			}
		});

		/**
		 * Drop items when the task has been completed.
		 *
		 */
		pomelo.on('onDropItems', function(data) {
			var area = app.getCurArea();
			var items = data.dropItems;
			var length = items.length;
			for (var i = 0; i < length; i ++) {
				area.addEntity(items[i]);
			}
		});

		/**
		 * Handle remove entities message
		 * @param data {Object} The message, contains entitiy ids to remove
		 */
		pomelo.on('onRemoveEntities', function(data){
			var entities = data.entities;
			var area = app.getCurArea();
			var player = area.getCurPlayer();
			for(var i = 0; i < entities.length; i++){
				if(entities[i] !== player.entityId) {
					area.removeEntity(entities[i]);
				}
			}
		});

		/**
		 * Handle move  message
		 * @param data {Object} The message, contains move information
		 */

		pomelo.on('onMove', function(data){
			var path = data.path;
			var character = app.getCurArea().getEntity(data.entityId);
			if(!character){
				//console.log('no character exist for move!' + data.entityId);
				return;
			}

			var sprite = character.getSprite();
			var totalDistance = utils.totalDistance(path);
			var needTime = Math.floor(totalDistance / sprite.getSpeed() * 1000 - app.getDelayTime());
			var speed = totalDistance/needTime * 1000;
			sprite.movePath(path, speed);
		});

		pomelo.on('onPathCheckout', function(data) {
			var player = app.getCurArea().getEntity(data.entityId);
			var serverPosition = data.position;
			var clientposition = player.getSprite().getPosition();
			var realDistance = utils.distance(serverPosition.x, serverPosition.y, clientposition.x, clientposition.y);
			var distanceLimit = 100;

			if (realDistance > distanceLimit) {
				player.getSprite().translateTo(serverPosition.x, serverPosition.y);
			}
		});

		/**
		 * Handle player upgrade message
		 * @param data {Object} The message, contains the info for player upgrade
		 */
		pomelo.on('onUpgrade' , function(playerData) {
			var area = app.getCurArea();
			var player = area.getPlayer(playerData.id);
			player.upgrade(playerData);
			player.getSprite().upgrade();
		});

		/**
		 * Handle update task data message
		 * @param data {Object} The message, contains the info for update task
		 */
		pomelo.on('onUpdateTaskData', function(reData) {
			app.getCurPlayer().updateTaskData(reData);
		});

		/**
		 * Handle task complete message
		 * @param data {Object} The message, contains the info for complete task
		 */
		pomelo.on('onTaskCompleted', function(data) {
			var tasks = app.getCurPlayer().curTasks;
			var completeTask = tasks[data.taskId];
			completeTask.setState(1);
			mainPanel.notify('The task has been completed！');
		});

		/**
		 * Handle remove item message
		 * @param data {Object} The message, contains the info for remove item
		 */
		pomelo.on('onRemoveItem', function(data){
			app.getCurArea().removeEntity(data.entityId);
		});

		/**
		 * Handle pick item message
		 * @param data {Object} The message, contains the info for pick item
		 */
		pomelo.on('onPickItem', function(data) {
			var area = app.getCurArea();
			var player = area.getEntity(data.player);
			var item = area.getEntity(data.item);
			//Only add item for current player
			if (player.entityId === area.getCurPlayer().entityId && !!item) {
				player.bag.addItem({id: item.kindId, type: item.type}, data.index);
			}
			area.removeEntity(data.item);
		});

		/**
		 * Handle npc talk message
		 * @param data {Object} The message, contains the info for npc talk
		 */
		pomelo.on('onNPCTalk', function(data){
			var npc = app.getCurArea().getEntity(data.npc);
			var curPlayer = app.getCurPlayer();
			var dir = {
				x1: curPlayer.getPosition().x,
				y1: curPlayer.getPosition().y,
				x2: npc.getPosition().x,
				y2: npc.getPosition().y
			};
			curPlayer.getSprite().stand(dir);
			dialogPanel.open(data);
		});

		/**
		 * Handle checkout task message
		 * @param data {Object} The message, contains the info for check out task
		 */
		pomelo.on('onCheckoutTask', function(data) {
			dialogPanel.open(data);
		});

		/**
		 * the order of attack, whose result contains success, killed and not_in_range
		 * @param {Object} data  contains attacter, target, result ect.
		 */
		pomelo.on('onAttack', function(data){
			var area = app.getCurArea();
			var skillId = data.skillId;
			var attacker = area.getEntity(data.attacker);
			var target = area.getEntity(data.target);

			if(!attacker || !target){
				console.log('attacker or target not exist ! attacker: ' + data.attacker + ', target : ' + data.target);
				return;
			}

			var attackerSprite = attacker.getSprite();
			var targetSprite = target.getSprite();
			var attackerPos = attackerSprite.getPosition();
			var targetPos = targetSprite.getPosition();
			var resultData = data.result;
			var result = resultData.result;
			var skillEffectParams = {
				id: skillId,
				player: attacker,
				position: {x: targetPos.x - attackerPos.x, y: targetPos.y - attackerPos.y}
			};
			if (app.getCurPlayer().entityId == data.attacker && skillId > 1) {
				mainPanel.skillBox[skillId].start();
			}

			var params ={
				attacker: attacker,
				attackerSprite: attackerSprite,
				target: target,
				targetSprite: targetSprite,
				attackerPos: attackerPos,
				targetPos: targetPos,
				resultData: resultData,
				skillEffectParams: skillEffectParams,
				experience: data.exp
			};
			if (result === AttackResult.SUCCESS) {
				successAction(params);
			} else if (result === AttackResult.KILLED) {
				killedAction(params);
			} else if (result === AttackResult.NOT_IN_RANGE) {
				targetSprite.stand({x1: attackerPos.x, x2:attackerPos.y, y1: targetPos.x, y2: targetPos.y});
			}
			uiUpdate();
		});

		/**
		 * when player revives, it works
		 * @param {Object} data
		 */
		pomelo.on('onRevive', function(data) {
			var area = app.getCurArea();
			var curPlayer = app.getCurPlayer();
			if (curPlayer.entityId !== data.entityId) {
				area.addEntity(data.entity);
			}
			var player = area.getEntity(data.entityId);
			player.died = false;
			player.set('hp', data.hp);
			var sprite = player.getSprite();
			sprite.revive(data, function() {
				if (player.entityId === app.getCurPlayer().entityId) {
					area.map.centerTo(data.x, data.y);
					mainPanel.reviveMaskHide();
				}
			});
		});
	}

	/**
	 * the action invokes when the result is success
	 * @param {Object} data
	 */
	var successAction = function(data) {
		new SkillEffect(data.skillEffectParams).createEffectAni();
		data.attackerSprite.translateTo(data.attackerPos.x, data.attackerPos.y);
		var dir = {x1: data.attackerPos.x, y1: data.attackerPos.y, x2: data.targetPos.x, y2: data.targetPos.y};
		data.targetSprite.createNumberNodes(data.resultData.damage);
		data.attackerSprite.attack(dir, 'noKilled', function() {
		});
		data.target.update({damage: data.resultData.damage});
		data.attacker.update({mpUse: data.resultData.mpUse});
		data.targetSprite.reduceBlood();
	};

	/**
	 * the action invokes when the result is killed
	 * @param {Object} data
	 */
	var killedAction = function(data) {
		if (!!data.target.died) {
			return;
		}
		data.target.died = true;
		if (data.target.type === EntityType.MOB) {
			data.target = null;
		}
		new SkillEffect(data.skillEffectParams).createEffectAni();
		var attackerSprite = data.attackerSprite;
		var attackerPos = data.attackerPos;
		attackerSprite.translateTo(attackerPos.x, attackerPos.y);
		data.targetSprite.zeroBlood();
		if (data.attacker.type === EntityType.PLAYER) {
			data.attacker.update({mpUse: data.resultData.mpUse, experience: data.experience });
		}
		var dir = {x1: data.attackerPos.x, y1: data.attackerPos.y, x2: data.targetPos.x, y2: data.targetPos.y};
		data.targetSprite.createNumberNodes(data.resultData.damage);
		data.attackerSprite.attack(dir, 'killed', function() {
		});
		data.targetSprite.died({x1: data.targetPos.x, y1: data.targetPos.y, x2: data.attackerPos.x, y2: data.attackerPos.y}, function(){
		var items = data.resultData.items;

			if (!!items && items.length > 0) {
				for (var i = 0; i < items.length; i ++) {
					var item = utils.buildEntity(items[i].type, items[i]);
					app.getCurArea().addEntity(item);
				}
			 }
		});
	};

	var uiUpdate = function() {
		var player = app.getCurPlayer();
		player.emit('change:hp');
		player.emit('change:maxHp');
		player.emit('change:mp');
		player.emit('change:maxMp');
		player.emit('change:experience');
	};

	function buildItem(data){
		var item;
		switch(data.type){
			case 'item' :
				item = utils.clone(dataApi.item.findById(data.kindId));
			break;
			case 'equipment' :
				item = utils.clone(dataApi.equipment.findById(data.kindId));
			break;
			default :
				return null;
		}

		item.x = data.x;
		item.y = data.y;
		item.entityId = data.entityId;
		item.playerId = data.playerId;
		item.type = data.type;

		return item;
	}
}};
<|endoftext|>


<commit_before>'use strict';
'require view';
'require dom';
'require fs';
'require ui';
'require uci';

return view.extend({
	handleCommand: function(exec, args) {
		var buttons = document.querySelectorAll('.diag-action > .cbi-button');

		for (var i = 0; i < buttons.length; i++)
			buttons[i].setAttribute('disabled', 'true');

		return fs.exec(exec, args).then(function(res) {
			var out = document.querySelector('.command-output');
			    out.style.display = '';

			dom.content(out, [ res.stdout || '', res.stderr || '' ]);
		}).catch(function(err) {
			ui.addNotification(null, E('p', [ err ]))
		}).finally(function() {
			for (var i = 0; i < buttons.length; i++)
				buttons[i].removeAttribute('disabled');
		});
	},

	handlePing: function(ev, cmd) {
		var exec = cmd || 'ping',
		    addr = ev.currentTarget.parentNode.previousSibling.value,
		    args = (exec == 'ping') ? [ '-4', '-c', '5', '-W', '1', addr ] : [ '-6', '-c', '5', addr ];

		return this.handleCommand(exec, args);
	},

	handleTraceroute: function(ev, cmd) {
		var exec = cmd || 'traceroute',
		    addr = ev.currentTarget.parentNode.previousSibling.value,
		    args = (exec == 'traceroute') ? [ '-q', '1', '-w', '1', '-n', addr ] : [ '-q', '1', '-w', '2', '-n', addr ];

		return this.handleCommand(exec, args);
	},

	handleNslookup: function(ev, cmd) {
		var addr = ev.currentTarget.parentNode.previousSibling.value;

		return this.handleCommand('nslookup', [ addr ]);
	},

	load: function() {
		return Promise.all([
			L.resolveDefault(fs.stat('/bin/ping6'), {}),
			L.resolveDefault(fs.stat('/usr/bin/ping6'), {}),
			L.resolveDefault(fs.stat('/bin/traceroute6'), {}),
			L.resolveDefault(fs.stat('/usr/bin/traceroute6'), {}),
			uci.load('luci')
		]);
	},

	render: function(res) {
		var has_ping6 = res[0].path || res[1].path,
		    has_traceroute6 = res[2].path || res[3].path,
			dns_host = uci.get('luci', 'diag', 'dns') || 'openwrt.org',
			ping_host = uci.get('luci', 'diag', 'ping') || 'openwrt.org',
			route_host = uci.get('luci', 'diag', 'route') || 'openwrt.org';

		return E([], [
			E('h2', {}, [ _('Network Utilities') ]),
			E('div', { 'class': 'table' }, [
				E('div', { 'class': 'tr' }, [
					E('div', { 'class': 'td left' }, [
						E('input', {
							'style': 'margin:5px 0',
							'type': 'text',
							'value': ping_host
						}),
						E('span', { 'class': 'diag-action' }, [
							has_ping6 ? new ui.ComboButton('ping', {
								'ping': '%s %s'.format(_('IPv4'), _('Ping')),
								'ping6': '%s %s'.format(_('IPv6'), _('Ping')),
							}, {
								'click': ui.createHandlerFn(this, 'handlePing'),
								'classes': {
									'ping': 'btn cbi-button cbi-button-action',
									'ping6': 'btn cbi-button cbi-button-action'
								}
							}).render() : E('button', {
								'class': 'cbi-button cbi-button-action',
								'click': ui.createHandlerFn(this, 'handlePing')
							}, [ _('Ping') ])
						])
					]),

					E('div', { 'class': 'td left' }, [
						E('input', {
							'style': 'margin:5px 0',
							'type': 'text',
							'value': route_host
						}),
						E('span', { 'class': 'diag-action' }, [
							has_traceroute6 ? new ui.ComboButton('traceroute', {
								'traceroute': '%s %s'.format(_('IPv4'), _('Traceroute')),
								'traceroute6': '%s %s'.format(_('IPv6'), _('Traceroute')),
							}, {
								'click': ui.createHandlerFn(this, 'handleTraceroute'),
								'classes': {
									'traceroute': 'btn cbi-button cbi-button-action',
									'traceroute6': 'btn cbi-button cbi-button-action'
								}
							}).render() : E('button', {
								'class': 'cbi-button cbi-button-action',
								'click': ui.createHandlerFn(this, 'handleTraceroute')
							}, [ _('Traceroute') ])
						])
					]),

					E('div', { 'class': 'td left' }, [
						E('input', {
							'style': 'margin:5px 0',
							'type': 'text',
							'value': dns_host
						}),
						E('span', { 'class': 'diag-action' }, [
							E('button', {
								'class': 'cbi-button cbi-button-action',
								'click': ui.createHandlerFn(this, 'handleNslookup')
							}, [ _('Nslookup') ])
						])
					])
				])
			]),
			E('pre', { 'class': 'command-output', 'style': 'display:none' })
		]);
	},

	handleSaveApply: null,
	handleSave: null,
	handleReset: null
});
<commit_msg>luci-mod-network: diagnostics.js: don't pass IP version flag to ping6<commit_after>'use strict';
'require view';
'require dom';
'require fs';
'require ui';
'require uci';

return view.extend({
	handleCommand: function(exec, args) {
		var buttons = document.querySelectorAll('.diag-action > .cbi-button');

		for (var i = 0; i < buttons.length; i++)
			buttons[i].setAttribute('disabled', 'true');

		return fs.exec(exec, args).then(function(res) {
			var out = document.querySelector('.command-output');
			    out.style.display = '';

			dom.content(out, [ res.stdout || '', res.stderr || '' ]);
		}).catch(function(err) {
			ui.addNotification(null, E('p', [ err ]))
		}).finally(function() {
			for (var i = 0; i < buttons.length; i++)
				buttons[i].removeAttribute('disabled');
		});
	},

	handlePing: function(ev, cmd) {
		var exec = cmd || 'ping',
		    addr = ev.currentTarget.parentNode.previousSibling.value,
		    args = (exec == 'ping') ? [ '-4', '-c', '5', '-W', '1', addr ] : [ '-c', '5', addr ];

		return this.handleCommand(exec, args);
	},

	handleTraceroute: function(ev, cmd) {
		var exec = cmd || 'traceroute',
		    addr = ev.currentTarget.parentNode.previousSibling.value,
		    args = (exec == 'traceroute') ? [ '-q', '1', '-w', '1', '-n', addr ] : [ '-q', '1', '-w', '2', '-n', addr ];

		return this.handleCommand(exec, args);
	},

	handleNslookup: function(ev, cmd) {
		var addr = ev.currentTarget.parentNode.previousSibling.value;

		return this.handleCommand('nslookup', [ addr ]);
	},

	load: function() {
		return Promise.all([
			L.resolveDefault(fs.stat('/bin/ping6'), {}),
			L.resolveDefault(fs.stat('/usr/bin/ping6'), {}),
			L.resolveDefault(fs.stat('/bin/traceroute6'), {}),
			L.resolveDefault(fs.stat('/usr/bin/traceroute6'), {}),
			uci.load('luci')
		]);
	},

	render: function(res) {
		var has_ping6 = res[0].path || res[1].path,
		    has_traceroute6 = res[2].path || res[3].path,
			dns_host = uci.get('luci', 'diag', 'dns') || 'openwrt.org',
			ping_host = uci.get('luci', 'diag', 'ping') || 'openwrt.org',
			route_host = uci.get('luci', 'diag', 'route') || 'openwrt.org';

		return E([], [
			E('h2', {}, [ _('Network Utilities') ]),
			E('div', { 'class': 'table' }, [
				E('div', { 'class': 'tr' }, [
					E('div', { 'class': 'td left' }, [
						E('input', {
							'style': 'margin:5px 0',
							'type': 'text',
							'value': ping_host
						}),
						E('span', { 'class': 'diag-action' }, [
							has_ping6 ? new ui.ComboButton('ping', {
								'ping': '%s %s'.format(_('IPv4'), _('Ping')),
								'ping6': '%s %s'.format(_('IPv6'), _('Ping')),
							}, {
								'click': ui.createHandlerFn(this, 'handlePing'),
								'classes': {
									'ping': 'btn cbi-button cbi-button-action',
									'ping6': 'btn cbi-button cbi-button-action'
								}
							}).render() : E('button', {
								'class': 'cbi-button cbi-button-action',
								'click': ui.createHandlerFn(this, 'handlePing')
							}, [ _('Ping') ])
						])
					]),

					E('div', { 'class': 'td left' }, [
						E('input', {
							'style': 'margin:5px 0',
							'type': 'text',
							'value': route_host
						}),
						E('span', { 'class': 'diag-action' }, [
							has_traceroute6 ? new ui.ComboButton('traceroute', {
								'traceroute': '%s %s'.format(_('IPv4'), _('Traceroute')),
								'traceroute6': '%s %s'.format(_('IPv6'), _('Traceroute')),
							}, {
								'click': ui.createHandlerFn(this, 'handleTraceroute'),
								'classes': {
									'traceroute': 'btn cbi-button cbi-button-action',
									'traceroute6': 'btn cbi-button cbi-button-action'
								}
							}).render() : E('button', {
								'class': 'cbi-button cbi-button-action',
								'click': ui.createHandlerFn(this, 'handleTraceroute')
							}, [ _('Traceroute') ])
						])
					]),

					E('div', { 'class': 'td left' }, [
						E('input', {
							'style': 'margin:5px 0',
							'type': 'text',
							'value': dns_host
						}),
						E('span', { 'class': 'diag-action' }, [
							E('button', {
								'class': 'cbi-button cbi-button-action',
								'click': ui.createHandlerFn(this, 'handleNslookup')
							}, [ _('Nslookup') ])
						])
					])
				])
			]),
			E('pre', { 'class': 'command-output', 'style': 'display:none' })
		]);
	},

	handleSaveApply: null,
	handleSave: null,
	handleReset: null
});
<|endoftext|>