<commit_before>/**
 * @license
 * Copyright 2018 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Methods for dragging a bubble visually.
 * @author fenichel@google.com (Rachel Fenichel)
 */
'use strict';

goog.module('Blockly.BubbleDragger');
goog.module.declareLegacyNamespace();

const BlockDragSurfaceSvg = goog.requireType('Blockly.BlockDragSurfaceSvg');
const ComponentManager = goog.require('Blockly.ComponentManager');
const Coordinate = goog.require('Blockly.utils.Coordinate');
const Events = goog.require('Blockly.Events');
const IBubble = goog.requireType('Blockly.IBubble');
const IDeleteArea = goog.requireType('Blockly.IDeleteArea');
const IDragTarget = goog.requireType('Blockly.IDragTarget');
const utils = goog.require('Blockly.utils');
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
/** @suppress {extraRequire} */
goog.require('Blockly.Bubble');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.CommentMove');

/**
 * Class for a bubble dragger.  It moves things on the bubble canvas around the
 * workspace when they are being dragged by a mouse or touch.  These can be
 * block comments, mutators, warnings, or workspace comments.
 * @param {!IBubble} bubble The item on the bubble canvas to drag.
 * @param {!WorkspaceSvg} workspace The workspace to drag on.
 * @constructor
 */
const BubbleDragger = function(bubble, workspace) {
  /**
   * The item on the bubble canvas that is being dragged.
   * @type {!IBubble}
   * @private
   */
  this.draggingBubble_ = bubble;

  /**
   * The workspace on which the bubble is being dragged.
   * @type {!WorkspaceSvg}
   * @private
   */
  this.workspace_ = workspace;

  /**
   * Which drag target the mouse pointer is over, if any.
   * @type {?IDragTarget}
   * @private
   */
  this.dragTarget_ = null;

  /**
   * Whether the bubble would be deleted if dropped immediately.
   * @type {boolean}
   * @private
   */
  this.wouldDeleteBubble_ = false;

  /**
   * The location of the top left corner of the dragging bubble's body at the
   * beginning of the drag, in workspace coordinates.
   * @type {!Coordinate}
   * @private
   */
  this.startXY_ = this.draggingBubble_.getRelativeToSurfaceXY();

  /**
   * The drag surface to move bubbles to during a drag, or null if none should
   * be used.  Block dragging and bubble dragging use the same surface.
   * @type {BlockDragSurfaceSvg}
   * @private
   */
  this.dragSurface_ =
      utils.is3dSupported() && !!workspace.getBlockDragSurface() ?
      workspace.getBlockDragSurface() :
      null;
};

/**
 * Sever all links from this object.
 * @package
 * @suppress {checkTypes}
 */
BubbleDragger.prototype.dispose = function() {
  this.draggingBubble_ = null;
  this.workspace_ = null;
  this.dragSurface_ = null;
};

/**
 * Start dragging a bubble.  This includes moving it to the drag surface.
 * @package
 */
BubbleDragger.prototype.startBubbleDrag = function() {
  if (!Events.getGroup()) {
    Events.setGroup(true);
  }

  this.workspace_.setResizesEnabled(false);
  this.draggingBubble_.setAutoLayout(false);
  if (this.dragSurface_) {
    this.moveToDragSurface_();
  }

  this.draggingBubble_.setDragging && this.draggingBubble_.setDragging(true);
};

/**
 * Execute a step of bubble dragging, based on the given event.  Update the
 * display accordingly.
 * @param {!Event} e The most recent move event.
 * @param {!Coordinate} currentDragDeltaXY How far the pointer has
 *     moved from the position at the start of the drag, in pixel units.
 * @package
 */
BubbleDragger.prototype.dragBubble = function(e, currentDragDeltaXY) {
  const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
  const newLoc = Coordinate.sum(this.startXY_, delta);
  this.draggingBubble_.moveDuringDrag(this.dragSurface_, newLoc);

  const oldDragTarget = this.dragTarget_;
  this.dragTarget_ = this.workspace_.getDragTarget(e);

  const oldWouldDeleteBubble = this.wouldDeleteBubble_;
  this.wouldDeleteBubble_ = this.shouldDelete_(this.dragTarget_);
  if (oldWouldDeleteBubble != this.wouldDeleteBubble_) {
    // Prevent unnecessary add/remove class calls.
    this.updateCursorDuringBubbleDrag_();
  }

  // Call drag enter/exit/over after wouldDeleteBlock is called in shouldDelete_
  if (this.dragTarget_ !== oldDragTarget) {
    oldDragTarget && oldDragTarget.onDragExit(this.draggingBubble_);
    this.dragTarget_ && this.dragTarget_.onDragEnter(this.draggingBubble_);
  }
  this.dragTarget_ && this.dragTarget_.onDragOver(this.draggingBubble_);
};

/**
 * Whether ending the drag would delete the bubble.
 * @param {?IDragTarget} dragTarget The drag target that the bubblee is
 *     currently over.
 * @return {boolean} Whether dropping the bubble immediately would delete the
 *    block.
 * @private
 */
BubbleDragger.prototype.shouldDelete_ = function(dragTarget) {
  if (dragTarget) {
    const componentManager = this.workspace_.getComponentManager();
    const isDeleteArea = componentManager.hasCapability(dragTarget.id,
        ComponentManager.Capability.DELETE_AREA);
    if (isDeleteArea) {
      return (/** @type {!IDeleteArea} */ (dragTarget))
          .wouldDelete(this.draggingBubble_, false);
    }
  }
  return false;
};

/**
 * Update the cursor (and possibly the trash can lid) to reflect whether the
 * dragging bubble would be deleted if released immediately.
 * @private
 */
BubbleDragger.prototype.updateCursorDuringBubbleDrag_ = function() {
  this.draggingBubble_.setDeleteStyle(this.wouldDeleteBubble_);
};

/**
 * Finish a bubble drag and put the bubble back on the workspace.
 * @param {!Event} e The mouseup/touchend event.
 * @param {!Coordinate} currentDragDeltaXY How far the pointer has
 *     moved from the position at the start of the drag, in pixel units.
 * @package
 */
BubbleDragger.prototype.endBubbleDrag = function(
    e, currentDragDeltaXY) {
  // Make sure internal state is fresh.
  this.dragBubble(e, currentDragDeltaXY);

  const preventMove = this.dragTarget_ &&
      this.dragTarget_.shouldPreventMove(this.draggingBubble_);
  let newLoc;
  if (preventMove) {
    newLoc = this.startXY_;
  } else {
    const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
    newLoc = Coordinate.sum(this.startXY_, delta);
  }
  // Move the bubble to its final location.
  this.draggingBubble_.moveTo(newLoc.x, newLoc.y);

  if (this.dragTarget_) {
    this.dragTarget_.onDrop(this.draggingBubble_);
  }

  if (this.wouldDeleteBubble_) {
    // Fire a move event, so we know where to go back to for an undo.
    this.fireMoveEvent_();
    this.draggingBubble_.dispose(false, true);
  } else {
    // Put everything back onto the bubble canvas.
    if (this.dragSurface_) {
      this.dragSurface_.clearAndHide(this.workspace_.getBubbleCanvas());
    }
    if (this.draggingBubble_.setDragging) {
      this.draggingBubble_.setDragging(false);
    }
    this.fireMoveEvent_();
  }
  this.workspace_.setResizesEnabled(true);

  Events.setGroup(false);
};

/**
 * Fire a move event at the end of a bubble drag.
 * @private
 */
BubbleDragger.prototype.fireMoveEvent_ = function() {
  if (this.draggingBubble_.isComment) {
    const event = new (Events.get(Events.COMMENT_MOVE))(
        /** @type {!Blockly.WorkspaceCommentSvg} */ (this.draggingBubble_));
    event.setOldCoordinate(this.startXY_);
    event.recordNew();
    Events.fire(event);
  }
  // TODO (fenichel): move events for comments.
  return;
};

/**
 * Convert a coordinate object from pixels to workspace units, including a
 * correction for mutator workspaces.
 * This function does not consider differing origins.  It simply scales the
 * input's x and y values.
 * @param {!Coordinate} pixelCoord A coordinate with x and y
 *     values in CSS pixel units.
 * @return {!Coordinate} The input coordinate divided by the
 *     workspace scale.
 * @private
 */
BubbleDragger.prototype.pixelsToWorkspaceUnits_ = function(pixelCoord) {
  const result = new Coordinate(
      pixelCoord.x / this.workspace_.scale,
      pixelCoord.y / this.workspace_.scale);
  if (this.workspace_.isMutator) {
    // If we're in a mutator, its scale is always 1, purely because of some
    // oddities in our rendering optimizations.  The actual scale is the same as
    // the scale on the parent workspace.
    // Fix that for dragging.
    const mainScale = this.workspace_.options.parentWorkspace.scale;
    result.scale(1 / mainScale);
  }
  return result;
};

/**
 * Move the bubble onto the drag surface at the beginning of a drag.  Move the
 * drag surface to preserve the apparent location of the bubble.
 * @private
 */
BubbleDragger.prototype.moveToDragSurface_ = function() {
  this.draggingBubble_.moveTo(0, 0);
  this.dragSurface_.translateSurface(this.startXY_.x, this.startXY_.y);
  // Execute the move on the top-level SVG component.
  this.dragSurface_.setBlocksAndShow(this.draggingBubble_.getSvgRoot());
};

exports = BubbleDragger;
<commit_msg>clang-format core/bubble_dragger.js<commit_after>/**
 * @license
 * Copyright 2018 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Methods for dragging a bubble visually.
 * @author fenichel@google.com (Rachel Fenichel)
 */
'use strict';

goog.module('Blockly.BubbleDragger');
goog.module.declareLegacyNamespace();

const BlockDragSurfaceSvg = goog.requireType('Blockly.BlockDragSurfaceSvg');
const ComponentManager = goog.require('Blockly.ComponentManager');
const Coordinate = goog.require('Blockly.utils.Coordinate');
const Events = goog.require('Blockly.Events');
const IBubble = goog.requireType('Blockly.IBubble');
const IDeleteArea = goog.requireType('Blockly.IDeleteArea');
const IDragTarget = goog.requireType('Blockly.IDragTarget');
const utils = goog.require('Blockly.utils');
const WorkspaceSvg = goog.requireType('Blockly.WorkspaceSvg');
/** @suppress {extraRequire} */
goog.require('Blockly.Bubble');
/** @suppress {extraRequire} */
goog.require('Blockly.constants');
/** @suppress {extraRequire} */
goog.require('Blockly.Events.CommentMove');

/**
 * Class for a bubble dragger.  It moves things on the bubble canvas around the
 * workspace when they are being dragged by a mouse or touch.  These can be
 * block comments, mutators, warnings, or workspace comments.
 * @param {!IBubble} bubble The item on the bubble canvas to drag.
 * @param {!WorkspaceSvg} workspace The workspace to drag on.
 * @constructor
 */
const BubbleDragger = function(bubble, workspace) {
  /**
   * The item on the bubble canvas that is being dragged.
   * @type {!IBubble}
   * @private
   */
  this.draggingBubble_ = bubble;

  /**
   * The workspace on which the bubble is being dragged.
   * @type {!WorkspaceSvg}
   * @private
   */
  this.workspace_ = workspace;

  /**
   * Which drag target the mouse pointer is over, if any.
   * @type {?IDragTarget}
   * @private
   */
  this.dragTarget_ = null;

  /**
   * Whether the bubble would be deleted if dropped immediately.
   * @type {boolean}
   * @private
   */
  this.wouldDeleteBubble_ = false;

  /**
   * The location of the top left corner of the dragging bubble's body at the
   * beginning of the drag, in workspace coordinates.
   * @type {!Coordinate}
   * @private
   */
  this.startXY_ = this.draggingBubble_.getRelativeToSurfaceXY();

  /**
   * The drag surface to move bubbles to during a drag, or null if none should
   * be used.  Block dragging and bubble dragging use the same surface.
   * @type {BlockDragSurfaceSvg}
   * @private
   */
  this.dragSurface_ =
      utils.is3dSupported() && !!workspace.getBlockDragSurface() ?
      workspace.getBlockDragSurface() :
      null;
};

/**
 * Sever all links from this object.
 * @package
 * @suppress {checkTypes}
 */
BubbleDragger.prototype.dispose = function() {
  this.draggingBubble_ = null;
  this.workspace_ = null;
  this.dragSurface_ = null;
};

/**
 * Start dragging a bubble.  This includes moving it to the drag surface.
 * @package
 */
BubbleDragger.prototype.startBubbleDrag = function() {
  if (!Events.getGroup()) {
    Events.setGroup(true);
  }

  this.workspace_.setResizesEnabled(false);
  this.draggingBubble_.setAutoLayout(false);
  if (this.dragSurface_) {
    this.moveToDragSurface_();
  }

  this.draggingBubble_.setDragging && this.draggingBubble_.setDragging(true);
};

/**
 * Execute a step of bubble dragging, based on the given event.  Update the
 * display accordingly.
 * @param {!Event} e The most recent move event.
 * @param {!Coordinate} currentDragDeltaXY How far the pointer has
 *     moved from the position at the start of the drag, in pixel units.
 * @package
 */
BubbleDragger.prototype.dragBubble = function(e, currentDragDeltaXY) {
  const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
  const newLoc = Coordinate.sum(this.startXY_, delta);
  this.draggingBubble_.moveDuringDrag(this.dragSurface_, newLoc);

  const oldDragTarget = this.dragTarget_;
  this.dragTarget_ = this.workspace_.getDragTarget(e);

  const oldWouldDeleteBubble = this.wouldDeleteBubble_;
  this.wouldDeleteBubble_ = this.shouldDelete_(this.dragTarget_);
  if (oldWouldDeleteBubble != this.wouldDeleteBubble_) {
    // Prevent unnecessary add/remove class calls.
    this.updateCursorDuringBubbleDrag_();
  }

  // Call drag enter/exit/over after wouldDeleteBlock is called in shouldDelete_
  if (this.dragTarget_ !== oldDragTarget) {
    oldDragTarget && oldDragTarget.onDragExit(this.draggingBubble_);
    this.dragTarget_ && this.dragTarget_.onDragEnter(this.draggingBubble_);
  }
  this.dragTarget_ && this.dragTarget_.onDragOver(this.draggingBubble_);
};

/**
 * Whether ending the drag would delete the bubble.
 * @param {?IDragTarget} dragTarget The drag target that the bubblee is
 *     currently over.
 * @return {boolean} Whether dropping the bubble immediately would delete the
 *    block.
 * @private
 */
BubbleDragger.prototype.shouldDelete_ = function(dragTarget) {
  if (dragTarget) {
    const componentManager = this.workspace_.getComponentManager();
    const isDeleteArea = componentManager.hasCapability(
        dragTarget.id, ComponentManager.Capability.DELETE_AREA);
    if (isDeleteArea) {
      return (/** @type {!IDeleteArea} */ (dragTarget))
          .wouldDelete(this.draggingBubble_, false);
    }
  }
  return false;
};

/**
 * Update the cursor (and possibly the trash can lid) to reflect whether the
 * dragging bubble would be deleted if released immediately.
 * @private
 */
BubbleDragger.prototype.updateCursorDuringBubbleDrag_ = function() {
  this.draggingBubble_.setDeleteStyle(this.wouldDeleteBubble_);
};

/**
 * Finish a bubble drag and put the bubble back on the workspace.
 * @param {!Event} e The mouseup/touchend event.
 * @param {!Coordinate} currentDragDeltaXY How far the pointer has
 *     moved from the position at the start of the drag, in pixel units.
 * @package
 */
BubbleDragger.prototype.endBubbleDrag = function(e, currentDragDeltaXY) {
  // Make sure internal state is fresh.
  this.dragBubble(e, currentDragDeltaXY);

  const preventMove = this.dragTarget_ &&
      this.dragTarget_.shouldPreventMove(this.draggingBubble_);
  let newLoc;
  if (preventMove) {
    newLoc = this.startXY_;
  } else {
    const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
    newLoc = Coordinate.sum(this.startXY_, delta);
  }
  // Move the bubble to its final location.
  this.draggingBubble_.moveTo(newLoc.x, newLoc.y);

  if (this.dragTarget_) {
    this.dragTarget_.onDrop(this.draggingBubble_);
  }

  if (this.wouldDeleteBubble_) {
    // Fire a move event, so we know where to go back to for an undo.
    this.fireMoveEvent_();
    this.draggingBubble_.dispose(false, true);
  } else {
    // Put everything back onto the bubble canvas.
    if (this.dragSurface_) {
      this.dragSurface_.clearAndHide(this.workspace_.getBubbleCanvas());
    }
    if (this.draggingBubble_.setDragging) {
      this.draggingBubble_.setDragging(false);
    }
    this.fireMoveEvent_();
  }
  this.workspace_.setResizesEnabled(true);

  Events.setGroup(false);
};

/**
 * Fire a move event at the end of a bubble drag.
 * @private
 */
BubbleDragger.prototype.fireMoveEvent_ = function() {
  if (this.draggingBubble_.isComment) {
    const event = new (Events.get(Events.COMMENT_MOVE))(
        /** @type {!Blockly.WorkspaceCommentSvg} */ (this.draggingBubble_));
    event.setOldCoordinate(this.startXY_);
    event.recordNew();
    Events.fire(event);
  }
  // TODO (fenichel): move events for comments.
  return;
};

/**
 * Convert a coordinate object from pixels to workspace units, including a
 * correction for mutator workspaces.
 * This function does not consider differing origins.  It simply scales the
 * input's x and y values.
 * @param {!Coordinate} pixelCoord A coordinate with x and y
 *     values in CSS pixel units.
 * @return {!Coordinate} The input coordinate divided by the
 *     workspace scale.
 * @private
 */
BubbleDragger.prototype.pixelsToWorkspaceUnits_ = function(pixelCoord) {
  const result = new Coordinate(
      pixelCoord.x / this.workspace_.scale,
      pixelCoord.y / this.workspace_.scale);
  if (this.workspace_.isMutator) {
    // If we're in a mutator, its scale is always 1, purely because of some
    // oddities in our rendering optimizations.  The actual scale is the same as
    // the scale on the parent workspace.
    // Fix that for dragging.
    const mainScale = this.workspace_.options.parentWorkspace.scale;
    result.scale(1 / mainScale);
  }
  return result;
};

/**
 * Move the bubble onto the drag surface at the beginning of a drag.  Move the
 * drag surface to preserve the apparent location of the bubble.
 * @private
 */
BubbleDragger.prototype.moveToDragSurface_ = function() {
  this.draggingBubble_.moveTo(0, 0);
  this.dragSurface_.translateSurface(this.startXY_.x, this.startXY_.y);
  // Execute the move on the top-level SVG component.
  this.dragSurface_.setBlocksAndShow(this.draggingBubble_.getSvgRoot());
};

exports = BubbleDragger;
<|endoftext|>


<commit_before>// ORNAMENTS ///////////////////////////////////////////////////////

// Added as part of the Ingress #Helios in 2014, ornaments
// are additional image overlays for portals.
// currently there are 6 known types of ornaments: ap$x$suffix
// - cluster portals (without suffix)
// - volatile portals (_v)
// - meeting points (_start)
// - finish points (_end)
//
// Beacons and Frackers were introduced at the launch of the Ingress
// ingame store on November 1st, 2015
// - Beacons (pe$TAG - $NAME) ie: 'peNIA - NIANTIC'
// - Frackers ('peFRACK')
// (there are 7 different colors for each of them)
//
// Ornament IDs are dynamic. NIANTIC might change them at any time without prior notice.
// New ornamnent IDs found on the map will be recorded and saved to knownOrnaments from
// which the Ornaments dialog will be filled with checked checkboxes.
// To exclude a set of ornaments, even if they have not yet shown up on the map, the user
// can add an entry to excludedOrnaments, which will compared (startsWith) to all known and
// future IDs. example: "ap" to exclude all Ornaments for anomalies (ap1, ap2, ap2_v)

/* global L, dialog, log */

window.ornaments = {

  OVERLAY_SIZE: 60,
  OVERLAY_OPACITY: 0.6,
  // The icon object holds optional definitions for the ornaments an beacons. The object shall
  // be filled from a plugin
  // 'ornamentID' : {
  //   name: 'meaningful name',     // shows up in dialog
  //   layer: 'name for the Layer', // shows up in layerchooser, optional, if not set
  //                                // ornament will be in "Ornaments"
  //   url: 'url',                  // from which the image will be taken, optional,
  //                                // 84x84px is default, if not set, stock images will be
  //                                // used
  //   offset: [dx,dy],             // optional, shift the ornament vertically or horizontally by
  //                                // dx (vertical)and dy )horizontal.
  //                                // [0, 0.5] to place right above the portal.
  //                                // default is [0, 0] to center
  //   opacity: 0..1                // optional, default is 0.6
  // }
  icon:{},
  excludedOrnaments: [],
  knownOrnaments: {},

  setup: function () {
    this._portals = {};
    this.layerGroup = L.layerGroup;
    if (window.map.options.preferCanvas && L.Browser.canvas && !window.DISABLE_CANVASICONLAYER) {
      this.layerGroup = L.canvasIconLayer;
      L.CanvasIconLayer.mergeOptions({ padding: L.Canvas.prototype.options.padding });
    }
    this.load();

    this.layers = {};
    this.layers['Ornaments'] = window.ornaments.layerGroup();
    this.layers['Excluded ornaments'] = window.ornaments.layerGroup(); // to keep excluded ornaments in an own layer

    window.layerChooser.addOverlay(this.layers['Ornaments'], 'Ornaments');
    window.layerChooser.addOverlay(this.layers['Excluded ornaments'], 'Excluded ornaments', {default: false});

    $('', {
      text:'Ornaments Opt',
      id: 'ornaments-toolbox-link',
      title: 'Edit ornament exclusions',
      accesskey: 'o',
      click: window.ornaments.ornamentsOpt})
      .appendTo('#toolbox');
  },
  createLayer: function (layerID) {
    window.ornaments.layers[layerID] = window.ornaments.layerGroup();
    window.layerChooser.addOverlay(window.ornaments.layers[layerID], layerID);
  },

  addPortal: function (portal) {
    this.removePortal(portal);
    var ornaments = portal.options.data.ornaments;
    if (ornaments && ornaments.length) {
      this._portals[portal.options.guid] = ornaments.map(function (ornament) {
        var layer = this.layers['Ornaments'];
        var opacity = this.OVERLAY_OPACITY;
        var size = this.OVERLAY_SIZE;
        var anchor = [size / 2, size / 2];
        var iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/' + ornament + '.png';

        if (!this.knownOrnaments[ornament]) {
          this.knownOrnaments[ornament]=false;
        }

        if (ornament in this.icon) {
          if (this.icon[ornament].layer) {
            if (this.layers[this.icon[ornament].layer] === undefined){
              log.log ('Add missing layer: ',this.icon[ornament].layer);
              window.ornaments.createLayer(window.ornaments.icon[ornament].layer);
            }
            layer =  this.layers[window.ornaments.icon[ornament].layer];
          }
          if (window.ornaments.icon[ornament].url) {
            iconUrl = window.ornaments.icon[ornament].url;
            if (this.icon[ornament].offset) {
              var offset = this.icon[ornament].offset;
              anchor = [size * offset[0] + anchor[0], size * offset[1] + anchor[1]];
            }
            if (this.icon[ornament].opacity) {
              opacity = this.icon[ornament].opacity;
            }
          }
        }

        var exclude = false;
        if (this.excludedOrnaments && this.excludedOrnaments !== ['']) {
          exclude = this.excludedOrnaments.some( function(pattern) {
            return ornament.startsWith(pattern);
          });
        }
        exclude = exclude | this.knownOrnaments[ornament];
        if (exclude){
          layer = this.layers['Excluded ornaments'];
        }

        return L.marker(portal.getLatLng(), {
          icon: L.icon({
            iconUrl: iconUrl,
            iconSize: [size, size],
            iconAnchor: anchor, // https://github.com/IITC-CE/Leaflet.Canvas-Markers/issues/4
            className: 'no-pointer-events'
          }),
          interactive: false,
          keyboard: false,
          opacity: opacity,
          layer: layer
        }).addTo(layer);

      }, this);
    }
  },

  removePortal: function (portal) {
    var guid = portal.options.guid;
    if (this._portals[guid]) {
      this._portals[guid].forEach(function (marker) {
        marker.options.layer.removeLayer(marker);
      });
      delete this._portals[guid];
    }
  },
  initOrnaments: function () {
    this.knownOrnaments = {};
    this.save();
  },

  load: function () {
    var dataStr;
    try {
      dataStr = localStorage.getItem('excludedOrnaments');
      if (!dataStr) { return; }
      this.excludedOrnaments = JSON.parse(dataStr);
    } catch (e) {
      log.warn('ornaments: failed to load excludedOrnaments from localStorage: '+e);
    }
    try {
      dataStr = localStorage.getItem('knownOrnaments');
      if (!dataStr) {
        this.initOrnaments ();
        return;
      }
      this.knownOrnaments = JSON.parse(dataStr);
    } catch (e) {
      log.warn('ornaments: failed to load data from localStorage: '+e);
    }

  },

  save: function () {
    localStorage['excludedOrnaments'] = JSON.stringify(this.excludedOrnaments);
    localStorage['knownOrnaments'] = JSON.stringify(this.knownOrnaments);
  },

  // reload: addPortal also calls removePortal
  reload: function () {
    for (var guid in window.ornaments._portals) {
      window.ornaments.addPortal(window.portals[guid]);
    }
  },

  processInput: function () {
    window.ornaments.excludedOrnaments = $('#ornaments_E').val().split(/[\s,]+/);
    window.ornaments.excludedOrnaments = window.ornaments.excludedOrnaments.filter(function (ornamentCode) { return ornamentCode !== ''; });
    // process the input from the checkboxes
    for (var ornamentCode in window.ornaments.knownOrnaments) {
      var input = $('#chk_orn_'+ornamentCode);
      window.ornaments.knownOrnaments[ornamentCode] = input.is(':checked');
    }
  },

  ornamentsList: function() {
    var text ='';
    var sortedIDs = Object.keys(window.ornaments.knownOrnaments).sort();

    sortedIDs.forEach ( function (ornamentCode) {
      var hidden = window.ornaments.excludedOrnaments.some( function(code) {
        return ornamentCode.startsWith(code);
      });

      var name = (window.ornaments.icon[ornamentCode] ? window.ornaments.icon[ornamentCode].name +' ('+ornamentCode+')' : ornamentCode);
      var checked = (window.ornaments.knownOrnaments[ornamentCode]||hidden) ?  'checked ' : '';
      text += '<input id="chk_orn_' + ornamentCode + '" type="checkbox" ' + checked;
      text += ' onchange="window.ornaments.processInput();window.ornaments.save();window.ornaments.reload()"';
      text += (hidden ? 'disabled':'');
      text += '>'+ name + '
';
    });
    return text;
  },

  replaceOL: function () {
    document.getElementById('ornamentsList').innerHTML = window.ornaments.ornamentsList();
  },

  onChangeHandler: function () {
    window.ornaments.processInput();
    window.ornaments.replaceOL();
    window.ornaments.save();
    window.ornaments.reload();
  },

  ornamentsOpt: function () {
    var excludedIDs = window.ornaments.excludedOrnaments.join(',');
    var html = '
'
             + 'Hide Ornaments from IITC that start with:
'
             + '<input type="text" value="' + excludedIDs + '" id="ornaments_E"'
             + ' onchange="window.ornaments.onChangeHandler()"></input>
'
             + '(separator: space or comma allowed)
'
             + 'known Ornaments, check to hide:
'
             + '
 ' + window.ornaments.ornamentsList() + '
'
             + '
';

    dialog({
      html:html,
      id:'ornamentsOpt',
      title:'Ornament excludes',
      buttons: {
        RESET : function () {
          window.ornaments.initOrnaments();
          window.ornaments.reload();
          $(this).dialog('close');
        },
        OK : function() {
          // process the input from the input
          window.ornaments.processInput();
          window.ornaments.save();
          window.ornaments.reload();
          $(this).dialog('close');

        }
      }
    });
  }
};
<commit_msg>image size following portal marker scale<commit_after>// ORNAMENTS ///////////////////////////////////////////////////////

// Added as part of the Ingress #Helios in 2014, ornaments
// are additional image overlays for portals.
// currently there are 6 known types of ornaments: ap$x$suffix
// - cluster portals (without suffix)
// - volatile portals (_v)
// - meeting points (_start)
// - finish points (_end)
//
// Beacons and Frackers were introduced at the launch of the Ingress
// ingame store on November 1st, 2015
// - Beacons (pe$TAG - $NAME) ie: 'peNIA - NIANTIC'
// - Frackers ('peFRACK')
// (there are 7 different colors for each of them)
//
// Ornament IDs are dynamic. NIANTIC might change them at any time without prior notice.
// New ornamnent IDs found on the map will be recorded and saved to knownOrnaments from
// which the Ornaments dialog will be filled with checked checkboxes.
// To exclude a set of ornaments, even if they have not yet shown up on the map, the user
// can add an entry to excludedOrnaments, which will compared (startsWith) to all known and
// future IDs. example: "ap" to exclude all Ornaments for anomalies (ap1, ap2, ap2_v)

/* global L, dialog, log */

window.ornaments = {

  OVERLAY_SIZE: 60,
  OVERLAY_OPACITY: 0.6,
  // The icon object holds optional definitions for the ornaments an beacons. The object shall
  // be filled from a plugin
  // 'ornamentID' : {
  //   name: 'meaningful name',     // shows up in dialog
  //   layer: 'name for the Layer', // shows up in layerchooser, optional, if not set
  //                                // ornament will be in "Ornaments"
  //   url: 'url',                  // from which the image will be taken, optional,
  //                                // 84x84px is default, if not set, stock images will be
  //                                // used
  //   offset: [dx,dy],             // optional, shift the ornament vertically or horizontally by
  //                                // dx (vertical)and dy )horizontal.
  //                                // [0, 0.5] to place right above the portal.
  //                                // default is [0, 0] to center
  //   opacity: 0..1                // optional, default is 0.6
  // }
  icon:{},
  excludedOrnaments: [],
  knownOrnaments: {},

  setup: function () {
    this._portals = {};
    this.layerGroup = L.layerGroup;
    if (window.map.options.preferCanvas && L.Browser.canvas && !window.DISABLE_CANVASICONLAYER) {
      this.layerGroup = L.canvasIconLayer;
      L.CanvasIconLayer.mergeOptions({ padding: L.Canvas.prototype.options.padding });
    }
    this.load();

    this.layers = {};
    this.layers['Ornaments'] = window.ornaments.layerGroup();
    this.layers['Excluded ornaments'] = window.ornaments.layerGroup(); // to keep excluded ornaments in an own layer

    window.layerChooser.addOverlay(this.layers['Ornaments'], 'Ornaments');
    window.layerChooser.addOverlay(this.layers['Excluded ornaments'], 'Excluded ornaments', {default: false});

    $('', {
      text:'Ornaments Opt',
      id: 'ornaments-toolbox-link',
      title: 'Edit ornament exclusions',
      accesskey: 'o',
      click: window.ornaments.ornamentsOpt})
      .appendTo('#toolbox');
  },
  createLayer: function (layerID) {
    window.ornaments.layers[layerID] = window.ornaments.layerGroup();
    window.layerChooser.addOverlay(window.ornaments.layers[layerID], layerID);
  },

  addPortal: function (portal) {
    this.removePortal(portal);
    var ornaments = portal.options.data.ornaments;
    if (ornaments && ornaments.length) {
      this._portals[portal.options.guid] = ornaments.map(function (ornament) {
        var layer = this.layers['Ornaments'];
        var opacity = this.OVERLAY_OPACITY;
        var size = this.OVERLAY_SIZE * window.portalMarkerScale();
        var anchor = [size / 2, size / 2];
        var iconUrl = '//commondatastorage.googleapis.com/ingress.com/img/map_icons/marker_images/' + ornament + '.png';

        if (!this.knownOrnaments[ornament]) {
          this.knownOrnaments[ornament]=false;
        }

        if (ornament in this.icon) {
          if (this.icon[ornament].layer) {
            if (this.layers[this.icon[ornament].layer] === undefined){
              log.log ('Add missing layer: ',this.icon[ornament].layer);
              window.ornaments.createLayer(window.ornaments.icon[ornament].layer);
            }
            layer =  this.layers[window.ornaments.icon[ornament].layer];
          }
          if (window.ornaments.icon[ornament].url) {
            iconUrl = window.ornaments.icon[ornament].url;
            if (this.icon[ornament].offset) {
              var offset = this.icon[ornament].offset;
              anchor = [size * offset[0] + anchor[0], size * offset[1] + anchor[1]];
            }
            if (this.icon[ornament].opacity) {
              opacity = this.icon[ornament].opacity;
            }
          }
        }

        var exclude = false;
        if (this.excludedOrnaments && this.excludedOrnaments !== ['']) {
          exclude = this.excludedOrnaments.some( function(pattern) {
            return ornament.startsWith(pattern);
          });
        }
        exclude = exclude | this.knownOrnaments[ornament];
        if (exclude){
          layer = this.layers['Excluded ornaments'];
        }

        return L.marker(portal.getLatLng(), {
          icon: L.icon({
            iconUrl: iconUrl,
            iconSize: [size, size],
            iconAnchor: anchor, // https://github.com/IITC-CE/Leaflet.Canvas-Markers/issues/4
            className: 'no-pointer-events'
          }),
          interactive: false,
          keyboard: false,
          opacity: opacity,
          layer: layer
        }).addTo(layer);

      }, this);
    }
  },

  removePortal: function (portal) {
    var guid = portal.options.guid;
    if (this._portals[guid]) {
      this._portals[guid].forEach(function (marker) {
        marker.options.layer.removeLayer(marker);
      });
      delete this._portals[guid];
    }
  },
  initOrnaments: function () {
    this.knownOrnaments = {};
    this.save();
  },

  load: function () {
    var dataStr;
    try {
      dataStr = localStorage.getItem('excludedOrnaments');
      if (!dataStr) { return; }
      this.excludedOrnaments = JSON.parse(dataStr);
    } catch (e) {
      log.warn('ornaments: failed to load excludedOrnaments from localStorage: '+e);
    }
    try {
      dataStr = localStorage.getItem('knownOrnaments');
      if (!dataStr) {
        this.initOrnaments ();
        return;
      }
      this.knownOrnaments = JSON.parse(dataStr);
    } catch (e) {
      log.warn('ornaments: failed to load data from localStorage: '+e);
    }

  },

  save: function () {
    localStorage['excludedOrnaments'] = JSON.stringify(this.excludedOrnaments);
    localStorage['knownOrnaments'] = JSON.stringify(this.knownOrnaments);
  },

  // reload: addPortal also calls removePortal
  reload: function () {
    for (var guid in window.ornaments._portals) {
      window.ornaments.addPortal(window.portals[guid]);
    }
  },

  processInput: function () {
    window.ornaments.excludedOrnaments = $('#ornaments_E').val().split(/[\s,]+/);
    window.ornaments.excludedOrnaments = window.ornaments.excludedOrnaments.filter(function (ornamentCode) { return ornamentCode !== ''; });
    // process the input from the checkboxes
    for (var ornamentCode in window.ornaments.knownOrnaments) {
      var input = $('#chk_orn_'+ornamentCode);
      window.ornaments.knownOrnaments[ornamentCode] = input.is(':checked');
    }
  },

  ornamentsList: function() {
    var text ='';
    var sortedIDs = Object.keys(window.ornaments.knownOrnaments).sort();

    sortedIDs.forEach ( function (ornamentCode) {
      var hidden = window.ornaments.excludedOrnaments.some( function(code) {
        return ornamentCode.startsWith(code);
      });

      var name = (window.ornaments.icon[ornamentCode] ? window.ornaments.icon[ornamentCode].name +' ('+ornamentCode+')' : ornamentCode);
      var checked = (window.ornaments.knownOrnaments[ornamentCode]||hidden) ?  'checked ' : '';
      text += '<input id="chk_orn_' + ornamentCode + '" type="checkbox" ' + checked;
      text += ' onchange="window.ornaments.processInput();window.ornaments.save();window.ornaments.reload()"';
      text += (hidden ? 'disabled':'');
      text += '>'+ name + '
';
    });
    return text;
  },

  replaceOL: function () {
    document.getElementById('ornamentsList').innerHTML = window.ornaments.ornamentsList();
  },

  onChangeHandler: function () {
    window.ornaments.processInput();
    window.ornaments.replaceOL();
    window.ornaments.save();
    window.ornaments.reload();
  },

  ornamentsOpt: function () {
    var excludedIDs = window.ornaments.excludedOrnaments.join(',');
    var html = '
'
             + 'Hide Ornaments from IITC that start with:
'
             + '<input type="text" value="' + excludedIDs + '" id="ornaments_E"'
             + ' onchange="window.ornaments.onChangeHandler()"></input>
'
             + '(separator: space or comma allowed)
'
             + 'known Ornaments, check to hide:
'
             + '
 ' + window.ornaments.ornamentsList() + '
'
             + '
';

    dialog({
      html:html,
      id:'ornamentsOpt',
      title:'Ornament excludes',
      buttons: {
        RESET : function () {
          window.ornaments.initOrnaments();
          window.ornaments.reload();
          $(this).dialog('close');
        },
        OK : function() {
          // process the input from the input
          window.ornaments.processInput();
          window.ornaments.save();
          window.ornaments.reload();
          $(this).dialog('close');

        }
      }
    });
  }
};
<|endoftext|>


<commit_before>'use strict'

function FPSControls(camera) {
    this.__init__(camera)
}

FPSControls.prototype = {
    camera            : null,
    pitch             : null,
    yaw               : null,
    enabled           : null,

    // Movement.
    up                : null,
    down              : null,
    left              : null,
    right             : null,

    velocity          : null,
    acceleration      : null,

    flying_on         : null,
    left_right        : null,

    walking_direction : null,

    // Camera view.
    max_upward_view   : null,
    max_downward_view : null,
    direction_vector  : null,

    // Mouse movement variables.

    // Used to make mouse movement feel smoother.
    mouse_movement_x_buffer: null,
    mouse_movement_y_buffer: null,

    // Constants.
    half_pie          : null,
    max_view_angle    : null,
    diagonal_penalty  : null,
    ground_normal     : null,

    __init__: function(camera) {
        // Constants.
        this.half_pie         = Math.PI / 2
        this.max_view_angle   = this.half_pie * 0.9
        this.diagonal_penalty = Math.sqrt(.5)
        this.ground_normal    = new THREE.Vector3(0, 1, 0)

        this.camera = camera
        this.camera.rotation.set(0, 0, 0)

        this.pitch = new THREE.Object3D()
        this.pitch.add(camera)

        this.yaw = new THREE.Object3D()
        this.yaw.position.y = 10
        this.yaw.add(this.pitch)

        this.enabled = false
        this.flying_on = true

        this.velocity = new THREE.Vector3()

        this.mouse_movement_x_buffer = new SmoothStep(this.yaw.rotation.y, 2.0)
        this.mouse_movement_y_buffer = new SmoothStep(this.pitch.rotation.x, 2.0)

        document.addEventListener('keypress', this.on_key_press.bind(this), false)
        document.addEventListener('mousemove', this.on_mouse_move.bind(this), false)
        document.addEventListener('keydown', this.on_key_down.bind(this), false)
        document.addEventListener('keyup', this.on_key_up.bind(this), false)
    },

    toggle_flying: function() {
        this.flying_on = !this.flying_on
        if (this.flying_on) {
            this.velocity.y = 0
        }
    },

    fly_left: function(delta) {
        this.velocity.x += 200 * delta * this.left_right.x
        this.velocity.y += 200 * delta * this.left_right.y
        this.velocity.z += 200 * delta * this.left_right.z
    },

    fly_right: function(delta) {
        this.velocity.x -= 200 * delta * this.left_right.x
        this.velocity.y -= 200 * delta * this.left_right.y
        this.velocity.z -= 200 * delta * this.left_right.z
    },

    fly_forward: function(delta) {
        this.velocity.x += 200 * delta * this.direction_vector.x
        this.velocity.y += 200 * delta * this.direction_vector.y
        this.velocity.z += 200 * delta * this.direction_vector.z
    },

    fly_backward: function(delta) {
        this.velocity.x -= 200 * delta * this.direction_vector.x
        this.velocity.y -= 200 * delta * this.direction_vector.y
        this.velocity.z -= 200 * delta * this.direction_vector.z
    },

    move_forward: function(delta) {
        this.velocity.x += 200 * delta * this.direction_vector.x
        this.velocity.z += 200 * delta * this.direction_vector.z
    },

    move_backward: function(delta) {
        this.velocity.x -= 200 * delta * this.direction_vector.x
        this.velocity.z -= 200 * delta * this.direction_vector.z
    },

    move_left: function(delta) {
        this.velocity.x += 200 * delta * this.left_right.x
        this.velocity.z += 200 * delta * this.left_right.z
    },

    move_right: function(delta) {
        this.velocity.x -= 200 * delta * this.left_right.x
        this.velocity.z -= 200 * delta * this.left_right.z
    },

    physics: function(delta) {
        if (this.enabled) {

            this.mouse_movement_x_buffer.update(delta)
            this.mouse_movement_y_buffer.update(delta)

            if (this.flying_on) {
                // Flying code.
                if (this.space) {
                    this.velocity.y += 200.0 * delta
                }
                if (this.shift) {
                    this.velocity.y -= 200.0 * delta
                }

                if ((this.up ^ this.down) & (this.left ^ this.right)) {
                    if (this.up) {
                        this.fly_forward(delta * this.diagonal_penalty)
                    } else {
                        this.fly_backward(delta * this.diagonal_penalty)
                    }
                    if (this.left) {
                        this.fly_left(delta * this.diagonal_penalty)
                    } else {
                        this.fly_right(delta * this.diagonal_penalty)
                    }
                } else if (this.up ^ this.down) {
                    if (this.up) {
                        this.fly_forward(delta)
                    } else {
                        this.fly_backward(delta)
                    }
                } else if (this.left ^ this.right) {
                    if (this.left) {
                        this.fly_left(delta)
                    } else {
                        this.fly_right(delta)
                    }
                }
                this.velocity.y *= (1 - delta * 15)
                this.yaw.position.y += this.velocity.y
            } else {
                // Walking code.
                if ((this.up ^ this.down) & (this.left ^ this.right)) {
                    if (this.up) {
                        this.move_forward(delta * this.diagonal_penalty)
                    } else {
                        this.move_backward(delta * this.diagonal_penalty)
                    }
                    if (this.left) {
                        this.move_left(delta * this.diagonal_penalty)
                    } else {
                        this.move_right(delta * this.diagonal_penalty)
                    }
                } else if (this.up ^ this.down) {
                    if (this.up) {
                        this.fly_forward(delta)
                    } else {
                        this.fly_backward(delta)
                    }
                } else if (this.left ^ this.right) {
                    if (this.left) {
                        this.move_left(delta)
                    } else {
                        this.move_right(delta)
                    }
                }
                if (this.velocity.y != 0) {
                    this.velocity.y -= 9.8 * delta * 10
                    this.velocity.y *= (1 - delta * 15)
                    this.yaw.position.y += this.velocity.y
                    if (this.yaw.position.y <= 10) {
                        this.yaw.position.y = 10
                        this.velocity.y = 0
                    }
                }
            }
            // Both flying and walking treat the following lines the same.
            this.velocity.x *= (1 - delta * 15)
            this.velocity.z *= (1 - delta * 15)
            this.yaw.position.x += this.velocity.x
            this.yaw.position.z += this.velocity.z
        }
    },

    get_position: function() {
        return this.yaw.position
    },

    get_velocity: function() {
        return this.velocity
    },

    enable: function() {
        this.enabled = true
    },

    disable: function() {
        this.enabled = false
    },

    on_key_press: function(event) {
        switch(event.which) {
        case 102: // f
            this.toggle_flying()
            break
        }
    },

    on_key_down: function(event) {
        switch(event.keyCode) {
        case 38: // up
        case 87: // w
            this.up = true
            break
        case 37: // left
        case 65: // a
            this.left = true
            break
        case 40: // down
        case 83: // s
            this.down = true
            break
        case 39: // right
        case 68: // d
            this.right = true
            break
        case 32: // space
            this.space = true
            break
        case 16: // shift
            this.shift = true
            break
        }
    },

    on_key_up: function(event) {
        switch(event.keyCode) {
        case 38: // up
        case 87: // w
            this.up = false
            break
        case 37: // left
        case 65: // a
            this.left = false
            break
        case 40: // down
        case 83: // s
            this.down = false
            break
        case 39: // right
        case 68: // d
            this.right = false
            break
        case 32: // space
            this.space = false
            break
        case 16: // shift
            this.shift = false
            break
        }
    },

    on_mouse_move: function(event) {
        if (this.enabled) {
            var movement_x = event.movementX || event.mozMovementX || event.webkitMovementX || 0
            var movement_y = event.movementY || event.mozMovementY || event.webkitMovementY || 0

            // TODO : Smooth step this portion out.
            //this.yaw.rotation.y   -= movement_x * 0.002
            //this.pitch.rotation.x -= movement_y * 0.002
            this.yaw.rotation.y = this.mouse_movement_x_buffer.get_current_value()
            this.pitch.rotation.x = this.mouse_movement_y_buffer.get_current_value()

            this.mouse_movement_x_buffer.add_force(movement_x * -0.002 * .2 * .2 * .5)
            this.mouse_movement_y_buffer.add_force(movement_y * -0.002 * .2 * .2 * .5)

            //this.mouse_movement_x_buffer.

            this.pitch.rotation.x = Math.max(-this.max_view_angle, Math.min(this.max_view_angle, this.pitch.rotation.x))

            this.direction_vector = this.get_direction()
            this.direction_vector.normalize()

            this.left_right = new THREE.Vector3(0, 1, 0)
            this.left_right.cross(this.direction_vector)
            this.left_right.normalize()

            if (!this.flying_on) {
                this.walking_direction = new THREE.Vector3(this.direction_vector.x, this.direction_vector.y, this.direction_vector.z)
                this.walking_direction = this.walking_direction.projectOnPlane(this.ground_normal)
                this.walking_direction.normalize()
            }
        }
    },

    get_object: function() {
        return this.yaw
    },

    get_direction: function() {
        var direction = new THREE.Vector3(0, 0, -1)
        var rotation  = new THREE.Euler(this.pitch.rotation.x, this.yaw.rotation.y, 0, 'YXZ')
        return direction.applyEuler(rotation)
    }

}<commit_msg>Mouse smooth step movement testing 09<commit_after>'use strict'

function FPSControls(camera) {
    this.__init__(camera)
}

FPSControls.prototype = {
    camera            : null,
    pitch             : null,
    yaw               : null,
    enabled           : null,

    // Movement.
    up                : null,
    down              : null,
    left              : null,
    right             : null,

    velocity          : null,
    acceleration      : null,

    flying_on         : null,
    left_right        : null,

    walking_direction : null,

    // Camera view.
    max_upward_view   : null,
    max_downward_view : null,
    direction_vector  : null,

    // Mouse movement variables.

    // Used to make mouse movement feel smoother.
    mouse_movement_x_buffer: null,
    mouse_movement_y_buffer: null,

    // Constants.
    half_pie          : null,
    max_view_angle    : null,
    diagonal_penalty  : null,
    ground_normal     : null,

    __init__: function(camera) {
        // Constants.
        this.half_pie         = Math.PI / 2
        this.max_view_angle   = this.half_pie * 0.9
        this.diagonal_penalty = Math.sqrt(.5)
        this.ground_normal    = new THREE.Vector3(0, 1, 0)

        this.camera = camera
        this.camera.rotation.set(0, 0, 0)

        this.pitch = new THREE.Object3D()
        this.pitch.add(camera)

        this.yaw = new THREE.Object3D()
        this.yaw.position.y = 10
        this.yaw.add(this.pitch)

        this.enabled = false
        this.flying_on = true

        this.velocity = new THREE.Vector3()

        this.mouse_movement_x_buffer = new SmoothStep(this.yaw.rotation.y, 1.0)
        this.mouse_movement_y_buffer = new SmoothStep(this.pitch.rotation.x, 1.0)

        document.addEventListener('keypress', this.on_key_press.bind(this), false)
        document.addEventListener('mousemove', this.on_mouse_move.bind(this), false)
        document.addEventListener('keydown', this.on_key_down.bind(this), false)
        document.addEventListener('keyup', this.on_key_up.bind(this), false)
    },

    toggle_flying: function() {
        this.flying_on = !this.flying_on
        if (this.flying_on) {
            this.velocity.y = 0
        }
    },

    fly_left: function(delta) {
        this.velocity.x += 200 * delta * this.left_right.x
        this.velocity.y += 200 * delta * this.left_right.y
        this.velocity.z += 200 * delta * this.left_right.z
    },

    fly_right: function(delta) {
        this.velocity.x -= 200 * delta * this.left_right.x
        this.velocity.y -= 200 * delta * this.left_right.y
        this.velocity.z -= 200 * delta * this.left_right.z
    },

    fly_forward: function(delta) {
        this.velocity.x += 200 * delta * this.direction_vector.x
        this.velocity.y += 200 * delta * this.direction_vector.y
        this.velocity.z += 200 * delta * this.direction_vector.z
    },

    fly_backward: function(delta) {
        this.velocity.x -= 200 * delta * this.direction_vector.x
        this.velocity.y -= 200 * delta * this.direction_vector.y
        this.velocity.z -= 200 * delta * this.direction_vector.z
    },

    move_forward: function(delta) {
        this.velocity.x += 200 * delta * this.direction_vector.x
        this.velocity.z += 200 * delta * this.direction_vector.z
    },

    move_backward: function(delta) {
        this.velocity.x -= 200 * delta * this.direction_vector.x
        this.velocity.z -= 200 * delta * this.direction_vector.z
    },

    move_left: function(delta) {
        this.velocity.x += 200 * delta * this.left_right.x
        this.velocity.z += 200 * delta * this.left_right.z
    },

    move_right: function(delta) {
        this.velocity.x -= 200 * delta * this.left_right.x
        this.velocity.z -= 200 * delta * this.left_right.z
    },

    physics: function(delta) {
        if (this.enabled) {

            this.mouse_movement_x_buffer.update(delta)
            this.mouse_movement_y_buffer.update(delta)

            if (this.flying_on) {
                // Flying code.
                if (this.space) {
                    this.velocity.y += 200.0 * delta
                }
                if (this.shift) {
                    this.velocity.y -= 200.0 * delta
                }

                if ((this.up ^ this.down) & (this.left ^ this.right)) {
                    if (this.up) {
                        this.fly_forward(delta * this.diagonal_penalty)
                    } else {
                        this.fly_backward(delta * this.diagonal_penalty)
                    }
                    if (this.left) {
                        this.fly_left(delta * this.diagonal_penalty)
                    } else {
                        this.fly_right(delta * this.diagonal_penalty)
                    }
                } else if (this.up ^ this.down) {
                    if (this.up) {
                        this.fly_forward(delta)
                    } else {
                        this.fly_backward(delta)
                    }
                } else if (this.left ^ this.right) {
                    if (this.left) {
                        this.fly_left(delta)
                    } else {
                        this.fly_right(delta)
                    }
                }
                this.velocity.y *= (1 - delta * 15)
                this.yaw.position.y += this.velocity.y
            } else {
                // Walking code.
                if ((this.up ^ this.down) & (this.left ^ this.right)) {
                    if (this.up) {
                        this.move_forward(delta * this.diagonal_penalty)
                    } else {
                        this.move_backward(delta * this.diagonal_penalty)
                    }
                    if (this.left) {
                        this.move_left(delta * this.diagonal_penalty)
                    } else {
                        this.move_right(delta * this.diagonal_penalty)
                    }
                } else if (this.up ^ this.down) {
                    if (this.up) {
                        this.fly_forward(delta)
                    } else {
                        this.fly_backward(delta)
                    }
                } else if (this.left ^ this.right) {
                    if (this.left) {
                        this.move_left(delta)
                    } else {
                        this.move_right(delta)
                    }
                }
                if (this.velocity.y != 0) {
                    this.velocity.y -= 9.8 * delta * 10
                    this.velocity.y *= (1 - delta * 15)
                    this.yaw.position.y += this.velocity.y
                    if (this.yaw.position.y <= 10) {
                        this.yaw.position.y = 10
                        this.velocity.y = 0
                    }
                }
            }
            // Both flying and walking treat the following lines the same.
            this.velocity.x *= (1 - delta * 15)
            this.velocity.z *= (1 - delta * 15)
            this.yaw.position.x += this.velocity.x
            this.yaw.position.z += this.velocity.z
        }
    },

    get_position: function() {
        return this.yaw.position
    },

    get_velocity: function() {
        return this.velocity
    },

    enable: function() {
        this.enabled = true
    },

    disable: function() {
        this.enabled = false
    },

    on_key_press: function(event) {
        switch(event.which) {
        case 102: // f
            this.toggle_flying()
            break
        }
    },

    on_key_down: function(event) {
        switch(event.keyCode) {
        case 38: // up
        case 87: // w
            this.up = true
            break
        case 37: // left
        case 65: // a
            this.left = true
            break
        case 40: // down
        case 83: // s
            this.down = true
            break
        case 39: // right
        case 68: // d
            this.right = true
            break
        case 32: // space
            this.space = true
            break
        case 16: // shift
            this.shift = true
            break
        }
    },

    on_key_up: function(event) {
        switch(event.keyCode) {
        case 38: // up
        case 87: // w
            this.up = false
            break
        case 37: // left
        case 65: // a
            this.left = false
            break
        case 40: // down
        case 83: // s
            this.down = false
            break
        case 39: // right
        case 68: // d
            this.right = false
            break
        case 32: // space
            this.space = false
            break
        case 16: // shift
            this.shift = false
            break
        }
    },

    on_mouse_move: function(event) {
        if (this.enabled) {
            var movement_x = event.movementX || event.mozMovementX || event.webkitMovementX || 0
            var movement_y = event.movementY || event.mozMovementY || event.webkitMovementY || 0

            // TODO : Smooth step this portion out.
            //this.yaw.rotation.y   -= movement_x * 0.002
            //this.pitch.rotation.x -= movement_y * 0.002
            this.yaw.rotation.y = this.mouse_movement_x_buffer.get_current_value()
            this.pitch.rotation.x = this.mouse_movement_y_buffer.get_current_value()

            this.mouse_movement_x_buffer.add_force(movement_x * -0.002 * .2 * .2 * .5)
            this.mouse_movement_y_buffer.add_force(movement_y * -0.002 * .2 * .2 * .5)

            //this.mouse_movement_x_buffer.

            this.pitch.rotation.x = Math.max(-this.max_view_angle, Math.min(this.max_view_angle, this.pitch.rotation.x))

            this.direction_vector = this.get_direction()
            this.direction_vector.normalize()

            this.left_right = new THREE.Vector3(0, 1, 0)
            this.left_right.cross(this.direction_vector)
            this.left_right.normalize()

            if (!this.flying_on) {
                this.walking_direction = new THREE.Vector3(this.direction_vector.x, this.direction_vector.y, this.direction_vector.z)
                this.walking_direction = this.walking_direction.projectOnPlane(this.ground_normal)
                this.walking_direction.normalize()
            }
        }
    },

    get_object: function() {
        return this.yaw
    },

    get_direction: function() {
        var direction = new THREE.Vector3(0, 0, -1)
        var rotation  = new THREE.Euler(this.pitch.rotation.x, this.yaw.rotation.y, 0, 'YXZ')
        return direction.applyEuler(rotation)
    }

}<|endoftext|>


<commit_before>// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith

module.exports = `(function () {
  var serializeXML = function (node, output) {
    var nodeType = node.nodeType;
    if (nodeType === 3) {
      output.push(node.textContent.replace(/&/, '&').replace(/</, '<').replace('>', '>'));
    } else if (nodeType === 1) {
      output.push('<', node.tagName);
      if (node.hasAttributes()) {
        [].forEach.call(node.attributes, function(attrNode){
          output.push(' ', attrNode.item.name, '=\\'', attrNode.item.value, '\\'');
        })
      }
      if (node.hasChildNodes()) {
        output.push('>');
        [].forEach.call(node.childNodes, function(childNode){
          serializeXML(childNode, output);
        })
        output.push('</', node.tagName, '>');
      } else {
        output.push('/>');
      }
    } else if (nodeType == 8) {
      output.push('<!--', node.nodeValue, '-->');
    }
  }

  Object.defineProperty(SVGElement.prototype, 'innerHTML', {
    get: function () {
      var output = [];
      var childNode = this.firstChild;
      while (childNode) {
        serializeXML(childNode, output);
        childNode = childNode.nextSibling;
      }
      return output.join('');
    },
    set: function (markupText) {
      while (this.firstChild) {
        this.removeChild(this.firstChild);
      }

      try {
        var dXML = new DOMParser();
        dXML.async = false;

        var sXML = '<svg xmlns=\\'http://www.w3.org/2000/svg\\' xmlns:xlink=\\'http://www.w3.org/1999/xlink\\'>' + markupText + '</svg>';
        var svgDocElement = dXML.parseFromString(sXML, 'text/xml').documentElement;

        var childNode = svgDocElement.firstChild;
        while (childNode) {
          this.appendChild(this.ownerDocument.importNode(childNode, true));
          childNode = childNode.nextSibling;
        }
      } catch (e) {};
    }
  });

  Object.defineProperty(SVGElement.prototype, 'innerSVG', {
    get: function () {
      return this.innerHTML;
    },
    set: function (markup) {
      this.innerHTML = markup;
    }
  });

})();`;
<commit_msg>fixes innerHTML polyfill for server side use<commit_after>// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith

module.exports = `if (typeof window !== "undefined") {
  (function () {
    var serializeXML = function (node, output) {
      var nodeType = node.nodeType;
      if (nodeType === 3) {
        output.push(node.textContent.replace(/&/, '&').replace(/</, '<').replace('>', '>'));
      } else if (nodeType === 1) {
        output.push('<', node.tagName);
        if (node.hasAttributes()) {
          [].forEach.call(node.attributes, function(attrNode){
            output.push(' ', attrNode.item.name, '=\\'', attrNode.item.value, '\\'');
          })
        }
        if (node.hasChildNodes()) {
          output.push('>');
          [].forEach.call(node.childNodes, function(childNode){
            serializeXML(childNode, output);
          })
          output.push('</', node.tagName, '>');
        } else {
          output.push('/>');
        }
      } else if (nodeType == 8) {
        output.push('<!--', node.nodeValue, '-->');
      }
    }

    Object.defineProperty(SVGElement.prototype, 'innerHTML', {
      get: function () {
        var output = [];
        var childNode = this.firstChild;
        while (childNode) {
          serializeXML(childNode, output);
          childNode = childNode.nextSibling;
        }
        return output.join('');
      },
      set: function (markupText) {
        while (this.firstChild) {
          this.removeChild(this.firstChild);
        }

        try {
          var dXML = new DOMParser();
          dXML.async = false;

          var sXML = '<svg xmlns=\\'http://www.w3.org/2000/svg\\' xmlns:xlink=\\'http://www.w3.org/1999/xlink\\'>' + markupText + '</svg>';
          var svgDocElement = dXML.parseFromString(sXML, 'text/xml').documentElement;

          var childNode = svgDocElement.firstChild;
          while (childNode) {
            this.appendChild(this.ownerDocument.importNode(childNode, true));
            childNode = childNode.nextSibling;
          }
        } catch (e) {};
      }
    });

    Object.defineProperty(SVGElement.prototype, 'innerSVG', {
      get: function () {
        return this.innerHTML;
      },
      set: function (markup) {
        this.innerHTML = markup;
      }
    });

  })();
}`;
<|endoftext|>


<commit_before>'use strict'

var SerialPort = require('serialport')
var EventEmitter = require('events').EventEmitter
var util = require('util')
var _ = require('lodash/fp')

var puloonData = require('./puloon_data')

var PAUSE_BETWEEN_DISPENSES = 200

var SLIT = 0.785

var ACK = 0x06
var NAK = 0x15
var EOT = 0x04
var SOH = 0x01
var ID = 0x30
var STX = 0x02
var COMMANDS = {
  0x44: {name: 'reset', responseParameters: 0},
  0x52: {name: 'dispense', responseParameters: 22},
  0x50: {name: 'status', responseParameters: 18},
  0x5f: {name: 'billLengths', responseParameters: 8},
  0x5e: {name: 'setBillLengths', responseParameters: 0},
  0x67: {name: 'getSerialNumber', responseParameters: 1}
}

var PuloonRs232 = function (device) {
  this.serial = null
  this.device = device
  this.buffer = Buffer.from([])
  this.setState('idle')
  this.response = null
  this.serialNumber = null
  this.responseCallback = null
  this.retrySentCommand = null
}
util.inherits(PuloonRs232, EventEmitter)

PuloonRs232.factory = function factory (device) {
  return new PuloonRs232(device)
}

module.exports = PuloonRs232

PuloonRs232.prototype.open = function open (callback) {
  console.log('INFO Puloon device: ' + this.device)

  var options = {baudRate: 9600, parity: 'even', dataBits: 8, stopBits: 1}
  var serial = new SerialPort(this.device, options)

  this.serial = serial

  var self = this
  serial.on('error', function (err) { self.emit('error', err) })
  serial.on('open', function () {
    console.log('INFO puloon connected')
    serial.on('readable', function () { self._process(serial.read()) })
    serial.on('close', function () { self.emit('disconnected') })
    self.emit('connected')
    callback()
  })
}

function findSohIndex (buffer) {
  for (var i = 0; i < buffer.length; i++) {
    if (buffer[i] === SOH) return i
  }
  return -1
}

function parseFrame (buffer) {
  var sohIndex = findSohIndex(buffer)
  if (sohIndex === -1) throw new Error('no SOH')

  // Start frame at SOH
  var frame = buffer.slice(sohIndex)

  // Need to at least pull the command code
  if (frame.length < 4) return null

  if (frame[1] !== ID || frame[2] !== STX) throw new Error('invalid frame')
  var commandCode = frame[3]
  var command = COMMANDS[commandCode]
  if (!command) {
    throw new Error('unsupported command: 0x' + commandCode.toString(16))
  }
  if (frame.length < command.responseParameters + 7) return null

  var rawErrorCode = frame[4] - 0x20
  var errorCode = rawErrorCode === 0 ? null : rawErrorCode
  var res = {
    code: commandCode,
    name: command.name,
    err: errorCode
  }

  // TODO break this out

  if (command.name === 'dispense') {
    res.bills = [
      {dispensed: frame[6] - 0x20, rejected: frame[7] - 0x20},
      {dispensed: frame[9] - 0x20, rejected: frame[10] - 0x20}
    ]
  }

  if (command.name === 'getSerialNumber') res.serialNumber = frame[5] - 0x20

  return res
}

// Works on both buffers and arrays
function computeBcc (frame) {
  var bcc = 0x00
  for (var i = 0; i < frame.length; i++) {
    bcc = frame[i] ^ bcc
  }
  return bcc
}

function logBufferFrame (data) {
  return JSON.stringify(Buffer.from(data).toJSON().data)
}

function buildFrame (commandCode, parameters) {
  var frame = [0x04, 0x30, 0x02, commandCode]
  frame = frame.concat(parameters, 0x03)
  var bcc = computeBcc(frame)
  frame = frame.concat(bcc)
  var buffer = Buffer.from(frame)
  console.log(`PULOON | Sending frame data: ${logBufferFrame(Buffer.from(buffer))}`)
  return Buffer.from(buffer)
}

PuloonRs232.prototype._processRemaining = function _processRemaining (data, offset) {
  var remaining = data.slice(offset)
  if (remaining.length > 0) {
    var self = this
    process.nextTick(function () { self._process(remaining) })
  }
}

PuloonRs232.prototype._process = function _process (data) {
  console.log(`PULOON | Processing response: ${logBufferFrame(data)}`);
  if (data.length === 0) return

  // Not sure what this is, but it happens
  if ((this.state === 'idle' || this.state === 'waitAck') && data[0] === 0xff) return // Should handle no SOH exception

  if (this.state === 'waitAck') {
    if (data[0] === ACK) {
      clearTimeout(this.retrySentCommand)
      this.setState('waitResponse')
      this._processRemaining(data, 1)
      return
    }
    if (data[0] === NAK) throw new Error('NAK')
  }

  if (this.state === 'waitEOT') {
    if (data[0] === EOT) {
      this.setState('idle')
      this.emit('response', this.response)
      if (this.responseCallback) this.responseCallback(null, this.response)
      this.response = null
      this._processRemaining(data, 1)
      return
    }
  }

  this.buffer = Buffer.concat([this.buffer, data])
  var response = parseFrame(this.buffer)
  if (response === null) return
  this.response = response
  this.buffer = Buffer.from([])

  this.serial.write([ACK])
  this.setState('waitEOT')
}

PuloonRs232.prototype.setState = function setState (state) {
  console.log(`PULOON | STATE -> ${state}`);
  this.state = state
}

PuloonRs232.prototype._send = function _send (command, name, cb) {
  this.setState('waitAck')
  this.responseCallback = cb || null
  this.serial.write(command)
  this.retrySentCommand = setTimeout(this.serial.write(command), 5000)
}

PuloonRs232.prototype.close = function close (cb) {
  var serial = this.serial

  // Workaround for: https://github.com/voodootikigod/node-serialport/issues/241
  setTimeout(function () { serial.close(cb) }, 100)
}

PuloonRs232.prototype._singleDispense = function _singleDispense (notes, delay) {
  this.serialNumber += 1
  var dispenseParams = [0x20 + notes[0], 0x20 + notes[1],
    0x20, 0x20, 0x20, 0x20, 0x20 + this.serialNumber]
  var self = this
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(`PULOON | CMD -> dispense`);
      self._send(buildFrame(0x52, dispenseParams), 'dispense', function (err, res) {
        if (err) return reject(err)
        if (res.err) {
          var dispenseErr = new Error(res.err)
          dispenseErr.result = res
          console.log('dispense error1', dispenseErr)
          return reject(dispenseErr)
        }
        resolve(res)
      })
    }, delay ? PAUSE_BETWEEN_DISPENSES : 0)
  })
}

function max (numArray) {
  return Math.max.apply(null, numArray)
}

PuloonRs232.prototype.dispense = function dispense (notes) {
  var self = this
  var currentNotes = notes
  var remainingNotes = notes
  var noteSequence = []
  var results = []

  return new Promise(resolve => {
    var current = Promise.resolve()

    while (true) {
      currentNotes = remainingNotes.map(function (n) { return Math.min(20, n) })
      remainingNotes = _.zipWith(_.subtract, remainingNotes, currentNotes)

      if (max(currentNotes) === 0) break
      noteSequence.push(currentNotes)
    }

    noteSequence.forEach((notes, idx) => {
      current = current.then(() => {
        return self._singleDispense(notes, idx > 0)
          .then(function (res) {
            results.push(res)
            return results
          })
      })
    })

    resolve(current)
  })
    .then(function (res) {
      return aggregateDispensed(res)
    })
    .catch(function (e) {
      results.push(e.result)
      var res = aggregateDispensed(results)
      res.err = e.message
      res.statusCode = 570
      console.log('dispense error2', res)
      return res
    })
}

function addBillRecs (a, b) {
  return {
    dispensed: a.dispensed + b.dispensed,
    rejected: a.rejected + b.rejected
  }
}

function aggregateDispensed (arr) {
  return arr.reduce(function (acc, r) {
    return {
      bills: _.zipWith(addBillRecs, acc.bills, r.bills),
      code: [].concat(acc.code, r.code),
      name: [].concat(acc.name, r.name)
    }
  })
}

PuloonRs232.prototype.reset = function reset (cassettes, fiatCode, cb) {
  // Note: Puloon sends two identical responses for reset command,
  // one before motor reset, and one after.
  console.log(`PULOON | CMD -> reset`);
  var responseCount = 0
  var self = this
  this._send(buildFrame(0x44, []), 'reset', function () {
    responseCount += 1
    if (responseCount < 2) return
    self._getSerialNumber(function (err, serialNumber) {
      if (err) { return cb(err) }
      self.serialNumber = serialNumber
      self._setBillLengths(cassettes, fiatCode, cb)
    })
  })
}

PuloonRs232.prototype._getSerialNumber = function _getSerialNumber (cb) {
  console.log(`PULOON | CMD -> getSerialNumber`);
  this._send(buildFrame(0x67, []), 'getSerialNumber', function (err, res) {
    cb(err, res.serialNumber)
  })
}

PuloonRs232.prototype.updateSerialNumber = function updateSerialNumber (cb) {
  var self = this
  console.log(`PULOON | CMD -> updateSerialNumber`);
  this._getSerialNumber(function (err, serialNumber) {
    if (err) { return cb(err) }
    self.serialNumber = serialNumber
    cb()
  })
}

function billLengthData (cassettes, fiatCode) {
  var numCassettes = cassettes.length
  if (numCassettes > 4) throw new Error('Too many cassettes')
  var billLengths = puloonData.billLengths[fiatCode]
  if (!billLengths) throw new Error('Unsupported fiatCode: ' + fiatCode)
  var data = [0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30]
  for (var i = 0; i < numCassettes; i++) {
    var cassette = cassettes[i]
    var billLength = billLengths[cassette.denomination]
    if (!billLength) {
      throw new Error('Unsupported denomination: ' + cassette.denomination + ' for fiatCode: ' + fiatCode)
    }
    var adjustedBillLength = Math.floor(billLength / SLIT)
    var index = i * 2
    data[index] = (Math.floor(adjustedBillLength / 16) + 0x30)
    data[index + 1] = ((adjustedBillLength % 16) + 0x30)
  }

  return data
}

PuloonRs232.prototype._setBillLengths = function _setBillLengths (cassettes, fiatCode, cb) {
  var data = billLengthData(cassettes, fiatCode)
  console.log(`PULOON | CMD -> setBillLengths`);
  this._send(buildFrame(0x5e, data), 'setBillLengths', function (err) {
    cb(err)
  })
}
<commit_msg>Fix nosoh bug (#157)<commit_after>'use strict'

var SerialPort = require('serialport')
var EventEmitter = require('events').EventEmitter
var util = require('util')
var _ = require('lodash/fp')

var puloonData = require('./puloon_data')

var PAUSE_BETWEEN_DISPENSES = 200

var SLIT = 0.785

var ACK = 0x06
var NAK = 0x15
var EOT = 0x04
var SOH = 0x01
var ID = 0x30
var STX = 0x02
var COMMANDS = {
  0x44: {name: 'reset', responseParameters: 0},
  0x52: {name: 'dispense', responseParameters: 22},
  0x50: {name: 'status', responseParameters: 18},
  0x5f: {name: 'billLengths', responseParameters: 8},
  0x5e: {name: 'setBillLengths', responseParameters: 0},
  0x67: {name: 'getSerialNumber', responseParameters: 1}
}

var PuloonRs232 = function (device) {
  this.serial = null
  this.device = device
  this.buffer = Buffer.from([])
  this.setState('idle')
  this.response = null
  this.serialNumber = null
  this.responseCallback = null
  this.retrySentCommand = null
}
util.inherits(PuloonRs232, EventEmitter)

PuloonRs232.factory = function factory (device) {
  return new PuloonRs232(device)
}

module.exports = PuloonRs232

PuloonRs232.prototype.open = function open (callback) {
  console.log('INFO Puloon device: ' + this.device)

  var options = {baudRate: 9600, parity: 'even', dataBits: 8, stopBits: 1}
  var serial = new SerialPort(this.device, options)

  this.serial = serial

  var self = this
  serial.on('error', function (err) { self.emit('error', err) })
  serial.on('open', function () {
    console.log('INFO puloon connected')
    serial.on('readable', function () { self._process(serial.read()) })
    serial.on('close', function () { self.emit('disconnected') })
    self.emit('connected')
    callback()
  })
}

function findSohIndex (buffer) {
  for (var i = 0; i < buffer.length; i++) {
    if (buffer[i] === SOH) return i
  }
  return -1
}

function parseFrame (buffer) {
  var sohIndex = findSohIndex(buffer)
  if (sohIndex === -1) throw new Error('no SOH')

  // Start frame at SOH
  var frame = buffer.slice(sohIndex)

  // Need to at least pull the command code
  if (frame.length < 4) return null

  if (frame[1] !== ID || frame[2] !== STX) throw new Error('invalid frame')
  var commandCode = frame[3]
  var command = COMMANDS[commandCode]
  if (!command) {
    throw new Error('unsupported command: 0x' + commandCode.toString(16))
  }
  if (frame.length < command.responseParameters + 7) return null

  var rawErrorCode = frame[4] - 0x20
  var errorCode = rawErrorCode === 0 ? null : rawErrorCode
  var res = {
    code: commandCode,
    name: command.name,
    err: errorCode
  }

  // TODO break this out

  if (command.name === 'dispense') {
    res.bills = [
      {dispensed: frame[6] - 0x20, rejected: frame[7] - 0x20},
      {dispensed: frame[9] - 0x20, rejected: frame[10] - 0x20}
    ]
  }

  if (command.name === 'getSerialNumber') res.serialNumber = frame[5] - 0x20

  return res
}

// Works on both buffers and arrays
function computeBcc (frame) {
  var bcc = 0x00
  for (var i = 0; i < frame.length; i++) {
    bcc = frame[i] ^ bcc
  }
  return bcc
}

function logBufferFrame (data) {
  return JSON.stringify(Buffer.from(data).toJSON().data)
}

function buildFrame (commandCode, parameters) {
  var frame = [0x04, 0x30, 0x02, commandCode]
  frame = frame.concat(parameters, 0x03)
  var bcc = computeBcc(frame)
  frame = frame.concat(bcc)
  var buffer = Buffer.from(frame)
  console.log(`PULOON | Sending frame data: ${logBufferFrame(Buffer.from(buffer))}`)
  return Buffer.from(buffer)
}

PuloonRs232.prototype._processRemaining = function _processRemaining (data, offset) {
  var remaining = data.slice(offset)
  if (remaining.length > 0) {
    var self = this
    process.nextTick(function () { self._process(remaining) })
  }
}

PuloonRs232.prototype._process = function _process (data) {
  console.log(`PULOON | Processing response: ${logBufferFrame(data)}`);
  if (data.length === 0) return

  // Not sure what this is, but it happens
  if ((this.state === 'idle' || this.state === 'waitAck') && data[0] === 0xff) return // Should handle no SOH exception

  if (this.state === 'waitAck') {
    if (data[0] === ACK) {
      clearTimeout(this.retrySentCommand)
      this.setState('waitResponse')
      this._processRemaining(data, 1)
      return
    }
    if (data[0] === NAK) throw new Error('NAK')
  }

  if (this.state === 'waitEOT') {
    if (data[0] === EOT) {
      this.setState('idle')
      this.emit('response', this.response)
      if (this.responseCallback) this.responseCallback(null, this.response)
      this.response = null
      this._processRemaining(data, 1)
      return
    }
  }

  this.buffer = Buffer.concat([this.buffer, data])
  var response = parseFrame(this.buffer)
  if (response === null) return
  this.response = response
  this.buffer = Buffer.from([])

  this.serial.write([ACK])
  this.setState('waitEOT')
}

PuloonRs232.prototype.setState = function setState (state) {
  console.log(`PULOON | STATE -> ${state}`);
  this.state = state
}

PuloonRs232.prototype._send = function _send (command, name, cb) {
  this.setState('waitAck')
  this.responseCallback = cb || null
  this.serial.write(command)
  this.retrySentCommand = setTimeout(() => this.serial.write(command), 5000)
}

PuloonRs232.prototype.close = function close (cb) {
  var serial = this.serial

  // Workaround for: https://github.com/voodootikigod/node-serialport/issues/241
  setTimeout(function () { serial.close(cb) }, 100)
}

PuloonRs232.prototype._singleDispense = function _singleDispense (notes, delay) {
  this.serialNumber += 1
  var dispenseParams = [0x20 + notes[0], 0x20 + notes[1],
    0x20, 0x20, 0x20, 0x20, 0x20 + this.serialNumber]
  var self = this
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(`PULOON | CMD -> dispense`);
      self._send(buildFrame(0x52, dispenseParams), 'dispense', function (err, res) {
        if (err) return reject(err)
        if (res.err) {
          var dispenseErr = new Error(res.err)
          dispenseErr.result = res
          console.log('dispense error1', dispenseErr)
          return reject(dispenseErr)
        }
        resolve(res)
      })
    }, delay ? PAUSE_BETWEEN_DISPENSES : 0)
  })
}

function max (numArray) {
  return Math.max.apply(null, numArray)
}

PuloonRs232.prototype.dispense = function dispense (notes) {
  var self = this
  var currentNotes = notes
  var remainingNotes = notes
  var noteSequence = []
  var results = []

  return new Promise(resolve => {
    var current = Promise.resolve()

    while (true) {
      currentNotes = remainingNotes.map(function (n) { return Math.min(20, n) })
      remainingNotes = _.zipWith(_.subtract, remainingNotes, currentNotes)

      if (max(currentNotes) === 0) break
      noteSequence.push(currentNotes)
    }

    noteSequence.forEach((notes, idx) => {
      current = current.then(() => {
        return self._singleDispense(notes, idx > 0)
          .then(function (res) {
            results.push(res)
            return results
          })
      })
    })

    resolve(current)
  })
    .then(function (res) {
      return aggregateDispensed(res)
    })
    .catch(function (e) {
      results.push(e.result)
      var res = aggregateDispensed(results)
      res.err = e.message
      res.statusCode = 570
      console.log('dispense error2', res)
      return res
    })
}

function addBillRecs (a, b) {
  return {
    dispensed: a.dispensed + b.dispensed,
    rejected: a.rejected + b.rejected
  }
}

function aggregateDispensed (arr) {
  return arr.reduce(function (acc, r) {
    return {
      bills: _.zipWith(addBillRecs, acc.bills, r.bills),
      code: [].concat(acc.code, r.code),
      name: [].concat(acc.name, r.name)
    }
  })
}

PuloonRs232.prototype.reset = function reset (cassettes, fiatCode, cb) {
  // Note: Puloon sends two identical responses for reset command,
  // one before motor reset, and one after.
  console.log(`PULOON | CMD -> reset`);
  var responseCount = 0
  var self = this
  this._send(buildFrame(0x44, []), 'reset', function () {
    responseCount += 1
    if (responseCount < 2) return
    self._getSerialNumber(function (err, serialNumber) {
      if (err) { return cb(err) }
      self.serialNumber = serialNumber
      self._setBillLengths(cassettes, fiatCode, cb)
    })
  })
}

PuloonRs232.prototype._getSerialNumber = function _getSerialNumber (cb) {
  console.log(`PULOON | CMD -> getSerialNumber`);
  this._send(buildFrame(0x67, []), 'getSerialNumber', function (err, res) {
    cb(err, res.serialNumber)
  })
}

PuloonRs232.prototype.updateSerialNumber = function updateSerialNumber (cb) {
  var self = this
  console.log(`PULOON | CMD -> updateSerialNumber`);
  this._getSerialNumber(function (err, serialNumber) {
    if (err) { return cb(err) }
    self.serialNumber = serialNumber
    cb()
  })
}

function billLengthData (cassettes, fiatCode) {
  var numCassettes = cassettes.length
  if (numCassettes > 4) throw new Error('Too many cassettes')
  var billLengths = puloonData.billLengths[fiatCode]
  if (!billLengths) throw new Error('Unsupported fiatCode: ' + fiatCode)
  var data = [0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30]
  for (var i = 0; i < numCassettes; i++) {
    var cassette = cassettes[i]
    var billLength = billLengths[cassette.denomination]
    if (!billLength) {
      throw new Error('Unsupported denomination: ' + cassette.denomination + ' for fiatCode: ' + fiatCode)
    }
    var adjustedBillLength = Math.floor(billLength / SLIT)
    var index = i * 2
    data[index] = (Math.floor(adjustedBillLength / 16) + 0x30)
    data[index + 1] = ((adjustedBillLength % 16) + 0x30)
  }

  return data
}

PuloonRs232.prototype._setBillLengths = function _setBillLengths (cassettes, fiatCode, cb) {
  var data = billLengthData(cassettes, fiatCode)
  console.log(`PULOON | CMD -> setBillLengths`);
  this._send(buildFrame(0x5e, data), 'setBillLengths', function (err) {
    cb(err)
  })
}
<|endoftext|>


<commit_before>// var path = require('path');

// var home_dir = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE,
// 		config_path = home_dir + '/.conf/kestrel-config.json';

// var config = require(config_path),
// 		repo_name = path.basename(path.resolve('./'));

module.exports = {
  "bucket_environment": prompt("Unschedule from `staging` or `prod`?", "staging"),
  "trigger": prompt("Enter the sync trigger")
 }<commit_msg>remove comments<commit_after>module.exports = {
  "bucket_environment": prompt("Unschedule from `staging` or `prod`?", "staging"),
  "trigger": prompt("Enter the sync trigger")
 }<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');
const Promise = require('bluebird');
const logger = require('../logger');
const cf = require('../cf');
const catalog = require('../models/catalog');
const config = require('../config');
const CONST = require('../constants');

class QuotaManager {
  constructor(quotaAPIClient) {
    this.quotaAPIClient = quotaAPIClient;
  }

  checkQuota(orgId, planId) {
    return Promise.try(() => {
      if (!_.get(config.quota, 'enabled')) {
        logger.debug('Quota check is not enabled');
        return CONST.QUOTA_API_RESPONSE_CODES.VALID_QUOTA;
      } else {
        return this.isOrgWhitelisted(orgId)
          .then(isWhitelisted => {
            if (isWhitelisted) {
              logger.debug('Org whitelisted, Quota check skipped');
              return CONST.QUOTA_API_RESPONSE_CODES.VALID_QUOTA;
            } else {
              logger.debug(`Org id is ${orgId}`);
              logger.debug(`Plan id is ${planId}`);
              let planName = _.find(catalog.plans, ['id', planId]).name;
              let serviceName = _.find(catalog.plans, ['id', planId]).service.name;
              let skipQuotaCheck = _.find(catalog.plans, ['id', planId]).metadata.skip_quota_check;
              logger.debug(`Plan Name is ${planName}`);
              logger.debug(`Service Name is ${serviceName}`);
              logger.debug(`Skip Quota check: ${skipQuotaCheck}`);
              if (skipQuotaCheck && skipQuotaCheck === true) {
                return CONST.QUOTA_API_RESPONSE_CODES.VALID_QUOTA;
              } else {
                const planIdsWithSameSKU = this.getAllPlanIdsWithSameSKU(planName, serviceName, catalog);
                return this.quotaAPIClient.getQuota(orgId, serviceName, planName)
                  .then(quota => {
                    // Special cases:
                    // When obtained quota = 0, send message to customer – Not entitled to create service instance
                    // When obtained quota = -1, assume that the org is whitelisted and hence allow the creation
                    if (quota === 0) {
                      return CONST.QUOTA_API_RESPONSE_CODES.NOT_ENTITLED;
                    } else if (quota === -1) {
                      return CONST.QUOTA_API_RESPONSE_CODES.VALID_QUOTA;
                    } else {
                      return this.getAllPlanGuidsFromPlanIDs(planIdsWithSameSKU)
                        .tap(planGuids => logger.debug('planguids are ', planGuids))
                        .then(planGuids => cf.cloudController.getServiceInstancesInOrgWithPlansGuids(orgId, planGuids))
                        .tap(instances => logger.debug(`Number of instances are ${_.size(instances)} & Quota number for current org space and service_plan is ${quota}`))
                        .then(instances => _.size(instances) >= quota ? CONST.QUOTA_API_RESPONSE_CODES.INVALID_QUOTA : CONST.QUOTA_API_RESPONSE_CODES.VALID_QUOTA);
                    }
                  });
              }
            }
          });
      }
    });
  }

  getAllPlanIdsWithSameSKU(planName, serviceName, serviceCatalog) {
    return Promise.try(() => {
      const planManagerName = _.find(catalog.plans, ['name', planName]).manager.name;
      const firstIdx = planName.indexOf('-'); // assumption here is that service plan names are versioned, and the format is like <version>-{...}-<tshirt-size>
      const skuName = planName.substring(planName.indexOf('-', firstIdx)); // and skuName will be only -{...}-<tshirt-size>

      logger.debug(`SKUName is ${skuName}`);
      const planIdsWithSameSKU = [];
      const service = _.find(serviceCatalog.services, ['name', serviceName]);
      _.each(service.plans, (plan) => {
        if (plan.name.endsWith(skuName) && plan.manager.name === planManagerName) {
          planIdsWithSameSKU.push(plan.id);
          logger.debug(`Found a plan with name as ${plan.name} which contains the skuName ${skuName}`);
        }
      });
      logger.debug('sameskuplanids are ', planIdsWithSameSKU);
      return planIdsWithSameSKU;
    });
  }

  isOrgWhitelisted(orgId) {
    return cf.cloudController.getOrganization(orgId)
      .tap(org => {
        logger.debug('current org details are ', org);
        logger.debug('current org name is ', org.entity.name);
        logger.debug('Whitelisted orgs are ', config.quota.whitelist);
      })
      .then(org => _.includes(config.quota.whitelist, org.entity.name));
  }

  getAllPlanGuidsFromPlanIDs(planIds) {
    return Promise.map(planIds, planId => this.getPlanGuidFromPlanID(planId));
  }

  getPlanGuidFromPlanID(planId) {
    return cf.cloudController.getServicePlans(`unique_id:${planId}`)
      .tap(plans => logger.debug(`planguid for uniqueid ${planId} is ${_.head(plans).metadata.guid}`))
      .then(plans => _.head(plans).metadata.guid);
  }
}

module.exports = QuotaManager;<commit_msg>Fixes #142: modified code for skip quota check (#145)<commit_after>'use strict';

const _ = require('lodash');
const Promise = require('bluebird');
const logger = require('../logger');
const cf = require('../cf');
const catalog = require('../models/catalog');
const config = require('../config');
const CONST = require('../constants');

class QuotaManager {
  constructor(quotaAPIClient) {
    this.quotaAPIClient = quotaAPIClient;
  }

  checkQuota(orgId, planId) {
    return Promise.try(() => {
      if (!_.get(config.quota, 'enabled')) {
        logger.debug('Quota check is not enabled');
        return CONST.QUOTA_API_RESPONSE_CODES.VALID_QUOTA;
      } else {
        return this.isOrgWhitelisted(orgId)
          .then(isWhitelisted => {
            if (isWhitelisted) {
              logger.debug('Org whitelisted, Quota check skipped');
              return CONST.QUOTA_API_RESPONSE_CODES.VALID_QUOTA;
            } else {
              logger.debug(`Org id is ${orgId}`);
              logger.debug(`Plan id is ${planId}`);
              let planName = _.find(catalog.plans, ['id', planId]).name;
              let serviceName = _.find(catalog.plans, ['id', planId]).service.name;
              let skipQuotaCheck = _.find(catalog.plans, ['id', planId]).metadata ? _.find(catalog.plans, ['id', planId]).metadata.skip_quota_check : undefined;
              logger.debug(`Plan Name is ${planName}`);
              logger.debug(`Service Name is ${serviceName}`);
              logger.debug(`Skip Quota check: ${skipQuotaCheck}`);
              if (skipQuotaCheck && skipQuotaCheck === true) {
                return CONST.QUOTA_API_RESPONSE_CODES.VALID_QUOTA;
              } else {
                const planIdsWithSameSKU = this.getAllPlanIdsWithSameSKU(planName, serviceName, catalog);
                return this.quotaAPIClient.getQuota(orgId, serviceName, planName)
                  .then(quota => {
                    // Special cases:
                    // When obtained quota = 0, send message to customer – Not entitled to create service instance
                    // When obtained quota = -1, assume that the org is whitelisted and hence allow the creation
                    if (quota === 0) {
                      return CONST.QUOTA_API_RESPONSE_CODES.NOT_ENTITLED;
                    } else if (quota === -1) {
                      return CONST.QUOTA_API_RESPONSE_CODES.VALID_QUOTA;
                    } else {
                      return this.getAllPlanGuidsFromPlanIDs(planIdsWithSameSKU)
                        .tap(planGuids => logger.debug('planguids are ', planGuids))
                        .then(planGuids => cf.cloudController.getServiceInstancesInOrgWithPlansGuids(orgId, planGuids))
                        .tap(instances => logger.debug(`Number of instances are ${_.size(instances)} & Quota number for current org space and service_plan is ${quota}`))
                        .then(instances => _.size(instances) >= quota ? CONST.QUOTA_API_RESPONSE_CODES.INVALID_QUOTA : CONST.QUOTA_API_RESPONSE_CODES.VALID_QUOTA);
                    }
                  });
              }
            }
          });
      }
    });
  }

  getAllPlanIdsWithSameSKU(planName, serviceName, serviceCatalog) {
    return Promise.try(() => {
      const planManagerName = _.find(catalog.plans, ['name', planName]).manager.name;
      const firstIdx = planName.indexOf('-'); // assumption here is that service plan names are versioned, and the format is like <version>-{...}-<tshirt-size>
      const skuName = planName.substring(planName.indexOf('-', firstIdx)); // and skuName will be only -{...}-<tshirt-size>

      logger.debug(`SKUName is ${skuName}`);
      const planIdsWithSameSKU = [];
      const service = _.find(serviceCatalog.services, ['name', serviceName]);
      _.each(service.plans, (plan) => {
        if (plan.name.endsWith(skuName) && plan.manager.name === planManagerName) {
          planIdsWithSameSKU.push(plan.id);
          logger.debug(`Found a plan with name as ${plan.name} which contains the skuName ${skuName}`);
        }
      });
      logger.debug('sameskuplanids are ', planIdsWithSameSKU);
      return planIdsWithSameSKU;
    });
  }

  isOrgWhitelisted(orgId) {
    return cf.cloudController.getOrganization(orgId)
      .tap(org => {
        logger.debug('current org details are ', org);
        logger.debug('current org name is ', org.entity.name);
        logger.debug('Whitelisted orgs are ', config.quota.whitelist);
      })
      .then(org => _.includes(config.quota.whitelist, org.entity.name));
  }

  getAllPlanGuidsFromPlanIDs(planIds) {
    return Promise.map(planIds, planId => this.getPlanGuidFromPlanID(planId));
  }

  getPlanGuidFromPlanID(planId) {
    return cf.cloudController.getServicePlans(`unique_id:${planId}`)
      .tap(plans => logger.debug(`planguid for uniqueid ${planId} is ${_.head(plans).metadata.guid}`))
      .then(plans => _.head(plans).metadata.guid);
  }
}

module.exports = QuotaManager;<|endoftext|>