<commit_before>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2015, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

var _ = require('lodash');
var sundial = require('sundial');
var util = require('util');

var annotate = require('../../eventAnnotations');
var common = require('../../commonFunctions');

var debug = require('bows')('AnimasDriver');

/**
 * Creates a new "simulator" for Animas pump data.
 *
 * Once all input activities are collected, the simulator will have accumulated events that the Tidepool Platform
 * will understand into a local "events" array.  You can retrieve the events by calling `getEvents()`
 *
 */
exports.make = function(config){
  if (config == null) {
    config = {};
  }
  var settings = config.settings || null;
  var events = [];

  var currBasal = null;
  var currBolus = null;
  var currStatus = null;
  var currTimestamp = null;
  var currSMBG = null;
  var suspendingAlarm = null;

  function setCurrBasal(basal) {
    currBasal = basal;
  }

  function setCurrBolus(bolus) {
    currBolus = bolus;
  }

  function setCurrStatus(status) {
    currStatus = status;
  }

  function setCurrSMBG(smbg) {
    currSMBG = smbg;
  }

  function setSuspendingAlarm(alarm) {
    suspendingAlarm = alarm;
  }

  function ensureTimestamp(e){
    if (currTimestamp > e.time) {
      throw new Error(
        util.format('Timestamps must be in order.  Current timestamp was[%s], but got[%j]', currTimestamp, e)
      );
    }
    currTimestamp = e.time;
    return e;
  }

  function simpleSimulate(e) {
    ensureTimestamp(e);
    events.push(e);
  }

  return {
    alarm: function(event) {
      var type = event.alarmType;
      if(type === 'occlusion' || type === 'auto_off' || type === 'no_insulin' || type === 'no_power') {
        // alarm will be added later (with fabricated status event) when basal is resumed
        setSuspendingAlarm(event);
      } else{
        simpleSimulate(event);
      }
    },
    basal: function(event){

      ensureTimestamp(event);

      if (currBasal != null) {
        if(!currBasal.isAssigned('duration')) {
          // calculate current basal's duration
          var duration = Date.parse(event.time) - Date.parse(currBasal.time);
          currBasal.with_duration(duration);
        }
        currBasal = common.truncateDuration(currBasal, 'animas');
      }

      if (event.rate === 0 ) {
        if(currStatus !== null) {
          // categorize the basal we're processing as a suspend basal if its rate
          // is zero and its start falls within the suspend event we have on deck
          var resumed = Date.parse(currStatus.time) + currStatus.duration;
          if (resumed >= Date.parse(event.time)) {
            //this basal is suspended
            event.deliveryType = 'suspend';
            delete event.rate;
          }
        }
      } else {
        // new basal is non-zero:
        // Are we resuming from a suspend event we created from an alarm?
        // If so, we should embed a suspend/resume event in the alarm and
        // mark the current basal as suspended
        if (suspendingAlarm != null) {
          if ((currBasal != null) && (currBasal.rate === 0)) {

            console.log('Embedding a suspend/resume event in an alarm:', suspendingAlarm);

            var status = {
              time: currBasal.time,
              deviceTime: currBasal.deviceTime,
              timezoneOffset: currBasal.timezoneOffset,
              conversionOffset: currBasal.conversionOffset,
              deviceId: currBasal.deviceId,
              duration: currBasal.duration,
              type: 'deviceEvent',
              subType: 'status',
              status: 'suspended',
              reason: {suspended: 'automatic', resumed: 'manual'},
              payload: {cause: suspendingAlarm.alarmType}
            };
            annotate.annotateEvent(status, 'animas/status/fabricated-from-alarm');

            //this basal is suspended
            currBasal.deliveryType = 'suspend';
            delete currBasal.rate;
            annotate.annotateEvent(currBasal, 'animas/basal/marked-suspended-from-alarm');

            suspendingAlarm.status = status;
            setCurrStatus(null);
          }

          events.push(suspendingAlarm);
          setSuspendingAlarm(null); //reset alarm as not to re-use
        }
      }

      if(currBasal !== null && (currBasal.time !== event.time)) {
        if (currBasal.index < event.index) {
          annotate.annotateEvent(event,'animas/out-of-sequence');
        }
        currBasal = currBasal.done();
        events.push(currBasal);
      }

      setCurrBasal(event);
    },
    finalBasal: function() {
      if(currBasal != null && currStatus != null && currBasal.rate !== 0 && currStatus.status === 'suspended') {
        //basal rate has not yet changed to zero after final suspend event
        currBasal.with_duration(Date.parse(currStatus.time) - Date.parse(currBasal.time));
        currBasal = common.truncateDuration(currBasal, 'animas');
        currBasal = currBasal.done();
        events.push(currBasal);
      }
    },
    bolus: function(event) {
      delete event.syncCounter; //wizard events already synced up
      delete event.requiredAmount;
      delete event.jsDate;
      simpleSimulate(event);
      setCurrBolus(event);
    },
    prime: function(event) {
      simpleSimulate(event);
    },
    pumpSettings: function(event) {
      simpleSimulate(event);
    },
    smbg: function(event) {

      if(currSMBG != null && (event.subType === 'manual') && (currSMBG.subType === 'linked')) {
        var linkedBG = currSMBG.value;
        if(event.units === 'mmol/L') {
          // linked value is always in mg/dL, so we have to convert to
          // mmol/L with one significant digit if we want to compare to
          // Animas mmol/L manual BG value
          linkedBG = common.convertBackToMmol(currSMBG.value);
        }
        if (linkedBG === event.value) {
          console.log('Duplicate SMBG value (',event.value,')',currSMBG.subType,currSMBG.time,'/',event.subType,event.time);
          var duration = Date.parse(event.time) - Date.parse(currSMBG.time);
          if (duration < (15 * sundial.MIN_TO_MSEC)) {
            console.log('Dropping duplicate manual value');
            return;
          }
        }
      }
      simpleSimulate(event);
      setCurrSMBG(event);
    },
    suspend: function(event) {
      ensureTimestamp(event);
      if (currStatus != null && currStatus.index < event.index) {
        annotate.annotateEvent(event,'animas/out-of-sequence');
      }

      if (currBasal != null && currBasal.rate === 0) {
        //this basal is suspended
        currBasal.deliveryType = 'suspend';
        delete currBasal.rate;
      }
      events.push(event);
      setCurrStatus(event);
    },
    wizard: function(event) {
      simpleSimulate(event);
    },
    cbg: function(event) {
      simpleSimulate(event);
    },
    calibration: function(event) {
      simpleSimulate(event);
    },
    getEvents: function() {
      function filterOutInvalidData() {
        return _.filter(events, function(event) {
          // we include the index on all objects to be able to sort accurately in
          // pump-event order despite date & time settings changes, but it's not
          // part of our data model, so we delete before uploading
          delete event.index;

          //filter out zero boluses
          if (event.type === 'bolus') {
            if (event.normal === 0 && !event.expectedNormal) {
              return false;
            }
            else {
              return true;
            }
          }
          else if (event.type === 'wizard') {
            var bolus = event.bolus || null;
            if (bolus != null) {
              if (bolus.normal === 0 && !bolus.expectedNormal) {
                return false;
              }
              else {
                return true;
              }
            }
          }

          // Filter out dates 2008 and earlier, as this is year 0 for Vibe.
          // We are doing this because we expect no pump to have true 2008 dates,
          // so anything generated in 2008 or earlier is really just because
          // someone didn't immediately set the date upon powering up the pump
          // for a while. Thus, we are dropping these events because we don't
          // know the actual, real time for them.
          if(parseInt(event.time.substring(0,4)) <= 2008) {
            debug('Dropping event in 2008 or earlier: ', event);
            return false;
          }

          return true;
        });
      }
      // because we have to wait for the *next* basal to determine the duration of a current
      // basal, basal events get added to `events` out of order wrt other events
      // (although within their own type all the basals are always in order)
      // end result: we have to sort events again before we try to upload them
      var orderedEvents = _.sortBy(filterOutInvalidData(), function(e) { return e.time; });

      return orderedEvents;
    }
  };
};
<commit_msg>handle final basal in the future due to wrong device time<commit_after>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2015, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

var _ = require('lodash');
var sundial = require('sundial');
var util = require('util');

var annotate = require('../../eventAnnotations');
var common = require('../../commonFunctions');

var debug = require('bows')('AnimasDriver');

/**
 * Creates a new "simulator" for Animas pump data.
 *
 * Once all input activities are collected, the simulator will have accumulated events that the Tidepool Platform
 * will understand into a local "events" array.  You can retrieve the events by calling `getEvents()`
 *
 */
exports.make = function(config){
  if (config == null) {
    config = {};
  }
  var settings = config.settings || null;
  var events = [];

  var currBasal = null;
  var currBolus = null;
  var currStatus = null;
  var currTimestamp = null;
  var currSMBG = null;
  var suspendingAlarm = null;

  function setCurrBasal(basal) {
    currBasal = basal;
  }

  function setCurrBolus(bolus) {
    currBolus = bolus;
  }

  function setCurrStatus(status) {
    currStatus = status;
  }

  function setCurrSMBG(smbg) {
    currSMBG = smbg;
  }

  function setSuspendingAlarm(alarm) {
    suspendingAlarm = alarm;
  }

  function ensureTimestamp(e){
    if (currTimestamp > e.time) {
      throw new Error(
        util.format('Timestamps must be in order.  Current timestamp was[%s], but got[%j]', currTimestamp, e)
      );
    }
    currTimestamp = e.time;
    return e;
  }

  function simpleSimulate(e) {
    ensureTimestamp(e);
    events.push(e);
  }

  return {
    alarm: function(event) {
      var type = event.alarmType;
      if(type === 'occlusion' || type === 'auto_off' || type === 'no_insulin' || type === 'no_power') {
        // alarm will be added later (with fabricated status event) when basal is resumed
        setSuspendingAlarm(event);
      } else{
        simpleSimulate(event);
      }
    },
    basal: function(event){

      ensureTimestamp(event);

      if (currBasal != null) {
        if(!currBasal.isAssigned('duration')) {
          // calculate current basal's duration
          var duration = Date.parse(event.time) - Date.parse(currBasal.time);
          currBasal.with_duration(duration);
        }
        currBasal = common.truncateDuration(currBasal, 'animas');
      }

      if (event.rate === 0 ) {
        if(currStatus !== null) {
          // categorize the basal we're processing as a suspend basal if its rate
          // is zero and its start falls within the suspend event we have on deck
          var resumed = Date.parse(currStatus.time) + currStatus.duration;
          if (resumed >= Date.parse(event.time)) {
            //this basal is suspended
            event.deliveryType = 'suspend';
            delete event.rate;
          }
        }
      } else {
        // new basal is non-zero:
        // Are we resuming from a suspend event we created from an alarm?
        // If so, we should embed a suspend/resume event in the alarm and
        // mark the current basal as suspended
        if (suspendingAlarm != null) {
          if ((currBasal != null) && (currBasal.rate === 0)) {

            console.log('Embedding a suspend/resume event in an alarm:', suspendingAlarm);

            var status = {
              time: currBasal.time,
              deviceTime: currBasal.deviceTime,
              timezoneOffset: currBasal.timezoneOffset,
              conversionOffset: currBasal.conversionOffset,
              deviceId: currBasal.deviceId,
              duration: currBasal.duration,
              type: 'deviceEvent',
              subType: 'status',
              status: 'suspended',
              reason: {suspended: 'automatic', resumed: 'manual'},
              payload: {cause: suspendingAlarm.alarmType}
            };
            annotate.annotateEvent(status, 'animas/status/fabricated-from-alarm');

            //this basal is suspended
            currBasal.deliveryType = 'suspend';
            delete currBasal.rate;
            annotate.annotateEvent(currBasal, 'animas/basal/marked-suspended-from-alarm');

            suspendingAlarm.status = status;
            setCurrStatus(null);
          }

          events.push(suspendingAlarm);
          setSuspendingAlarm(null); //reset alarm as not to re-use
        }
      }

      if(currBasal !== null && (currBasal.time !== event.time)) {
        if (currBasal.index < event.index) {
          annotate.annotateEvent(event,'animas/out-of-sequence');
        }
        currBasal = currBasal.done();
        events.push(currBasal);
      }

      setCurrBasal(event);
    },
    finalBasal: function() {
      if(currBasal != null && currStatus != null && currBasal.rate !== 0 && currStatus.status === 'suspended') {
        //basal rate has not yet changed to zero after final suspend event
        currBasal.with_duration(Date.parse(currStatus.time) - Date.parse(currBasal.time));
        currBasal = common.truncateDuration(currBasal, 'animas');

        if (currBasal.duration < 0) {
          // if the device time was set wrong and there are event dates in the future,
          // the final basal duration cannot be calculated
          currBasal.duration = 0;
          annotate.annotateEvent(currBasal, 'basal/unknown-duration');
        }

        currBasal = currBasal.done();
        events.push(currBasal);
      }
    },
    bolus: function(event) {
      delete event.syncCounter; //wizard events already synced up
      delete event.requiredAmount;
      delete event.jsDate;
      simpleSimulate(event);
      setCurrBolus(event);
    },
    prime: function(event) {
      simpleSimulate(event);
    },
    pumpSettings: function(event) {
      simpleSimulate(event);
    },
    smbg: function(event) {

      if(currSMBG != null && (event.subType === 'manual') && (currSMBG.subType === 'linked')) {
        var linkedBG = currSMBG.value;
        if(event.units === 'mmol/L') {
          // linked value is always in mg/dL, so we have to convert to
          // mmol/L with one significant digit if we want to compare to
          // Animas mmol/L manual BG value
          linkedBG = common.convertBackToMmol(currSMBG.value);
        }
        if (linkedBG === event.value) {
          console.log('Duplicate SMBG value (',event.value,')',currSMBG.subType,currSMBG.time,'/',event.subType,event.time);
          var duration = Date.parse(event.time) - Date.parse(currSMBG.time);
          if (duration < (15 * sundial.MIN_TO_MSEC)) {
            console.log('Dropping duplicate manual value');
            return;
          }
        }
      }
      simpleSimulate(event);
      setCurrSMBG(event);
    },
    suspend: function(event) {
      ensureTimestamp(event);
      if (currStatus != null && currStatus.index < event.index) {
        annotate.annotateEvent(event,'animas/out-of-sequence');
      }

      if (currBasal != null && currBasal.rate === 0) {
        //this basal is suspended
        currBasal.deliveryType = 'suspend';
        delete currBasal.rate;
      }
      events.push(event);
      setCurrStatus(event);
    },
    wizard: function(event) {
      simpleSimulate(event);
    },
    cbg: function(event) {
      simpleSimulate(event);
    },
    calibration: function(event) {
      simpleSimulate(event);
    },
    getEvents: function() {
      function filterOutInvalidData() {
        return _.filter(events, function(event) {
          // we include the index on all objects to be able to sort accurately in
          // pump-event order despite date & time settings changes, but it's not
          // part of our data model, so we delete before uploading
          delete event.index;

          //filter out zero boluses
          if (event.type === 'bolus') {
            if (event.normal === 0 && !event.expectedNormal) {
              return false;
            }
            else {
              return true;
            }
          }
          else if (event.type === 'wizard') {
            var bolus = event.bolus || null;
            if (bolus != null) {
              if (bolus.normal === 0 && !bolus.expectedNormal) {
                return false;
              }
              else {
                return true;
              }
            }
          }

          // Filter out dates 2008 and earlier, as this is year 0 for Vibe.
          // We are doing this because we expect no pump to have true 2008 dates,
          // so anything generated in 2008 or earlier is really just because
          // someone didn't immediately set the date upon powering up the pump
          // for a while. Thus, we are dropping these events because we don't
          // know the actual, real time for them.
          if(parseInt(event.time.substring(0,4)) <= 2008) {
            debug('Dropping event in 2008 or earlier: ', event);
            return false;
          }

          return true;
        });
      }
      // because we have to wait for the *next* basal to determine the duration of a current
      // basal, basal events get added to `events` out of order wrt other events
      // (although within their own type all the basals are always in order)
      // end result: we have to sort events again before we try to upload them
      var orderedEvents = _.sortBy(filterOutInvalidData(), function(e) { return e.time; });

      return orderedEvents;
    }
  };
};
<|endoftext|>


<commit_before>require('proof')(17, prove)

function prove (assert) {
    var Monotonic = require('..')

    assert(Monotonic.toString(Monotonic.toWords('88888888/888888888')), '88888888/888888888', 'parse')
    assert(Monotonic.asString.compare('0', '0'), 0, 'compare equal')
    assert(Monotonic.asString.compare('100000000', 'ffffffff') > 0, 'compare longer than')
    assert(Monotonic.asString.compare('fffffffe', 'ffffffff') < 0, 'compare less than')
    assert(Monotonic.asString.compare('ffffffff', 'fffffffe') > 0, 'compare less than')
    assert(Monotonic.asString.compare('ffffffff', 'fffffffe') > 0, 'compare less than')
    assert(Monotonic.asString.increment('0/ffffffff', 1), '0/100000000', 'increment roll over')
    assert(Monotonic.asString.increment('0/fffffffe', 1), '0/ffffffff', 'increment roll over')
    assert(Monotonic.asString.increment('0/fffffffe', 0), '1/0', 'increment path roll over')
    assert(!Monotonic.asString.isBoundary('0/fffffffe', 0), 'is not boundary')
    assert(Monotonic.asString.isBoundary('fffffffe/0', 0), 'is boundary')
    assert(Monotonic.asString.compareIndex('0', '0', 0), 0, 'compare index')

    assert(Monotonic.Part.asString.difference('100000000', 'ffffffff'), 1, 'differnce')
    assert(Monotonic.asString.difference('100000000', 'fffffffe', 0), 2, 'difference 2')
    assert(Monotonic.asString.difference('fffffffe', '100000000', 0), -2, 'difference -2')

    assert(Monotonic.Part.asString.compare('0', '0'), 0, 'part difference')
    assert(Monotonic.Part.asString.increment('0'), '1', 'part increment')
}
<commit_msg>Use Proof Redux.<commit_after>require('proof/redux')(17, prove)

function prove (assert) {
    var Monotonic = require('..')

    assert(Monotonic.toString(Monotonic.toWords('88888888/888888888')), '88888888/888888888', 'parse')
    assert(Monotonic.asString.compare('0', '0'), 0, 'compare equal')
    assert(Monotonic.asString.compare('100000000', 'ffffffff') > 0, 'compare longer than')
    assert(Monotonic.asString.compare('fffffffe', 'ffffffff') < 0, 'compare less than')
    assert(Monotonic.asString.compare('ffffffff', 'fffffffe') > 0, 'compare less than')
    assert(Monotonic.asString.compare('ffffffff', 'fffffffe') > 0, 'compare less than')
    assert(Monotonic.asString.increment('0/ffffffff', 1), '0/100000000', 'increment roll over')
    assert(Monotonic.asString.increment('0/fffffffe', 1), '0/ffffffff', 'increment roll over')
    assert(Monotonic.asString.increment('0/fffffffe', 0), '1/0', 'increment path roll over')
    assert(!Monotonic.asString.isBoundary('0/fffffffe', 0), 'is not boundary')
    assert(Monotonic.asString.isBoundary('fffffffe/0', 0), 'is boundary')
    assert(Monotonic.asString.compareIndex('0', '0', 0), 0, 'compare index')

    assert(Monotonic.Part.asString.difference('100000000', 'ffffffff'), 1, 'differnce')
    assert(Monotonic.asString.difference('100000000', 'fffffffe', 0), 2, 'difference 2')
    assert(Monotonic.asString.difference('fffffffe', '100000000', 0), -2, 'difference -2')

    assert(Monotonic.Part.asString.compare('0', '0'), 0, 'part difference')
    assert(Monotonic.Part.asString.increment('0'), '1', 'part increment')
}
<|endoftext|>


<commit_before>if (typeof HotPotatoe === "undefined") {
    HotPotatoe = {};
}

/**
 * Player
 *
 * @param {HotPotatoe.Game} game
 * @param {string} id - A unique player ID
 * @param {boolean} [isCurrentPlayer=false] - Flag to indicate if this player is controlled by current user
 * @constructor
 */
HotPotatoe.Player = function (game, id, isCurrentPlayer) {
    this.parent = game;
    this.phaser = game.phaser;
    this.id = id;
    this.isCurrentPlayer = false;
    this.force = 15;
    this.maxVelocity = 200;
    this.isHotPotatoe = false;

    if (typeof isCurrentPlayer !== 'undefined') {
        this.isCurrentPlayer = isCurrentPlayer;
    }
};

/**
 * Pre-load player assets
 */
HotPotatoe.Player.prototype.preload = function() {
    this.assetId = this.parent.assetIds.players;
    if (this.isCurrentPlayer) {
        this.assetId = this.parent.assetIds.currentPlayer;
    }
};

/**
 * Create player
 */
HotPotatoe.Player.prototype.create = function() {
    var x = Math.floor((Math.random() * 700) + 1);
    var y = Math.floor((Math.random() * 500) + 1);
    this.sprite = this.phaser.add.sprite(x, y, this.assetId);
    if (Meteor.isClient) {
        //On client we hide players until their positions get synced
        this.sprite.visible = false;
    }
    this.sprite.playerId = this.id;

    if (Meteor.isServer) {
        // We need to enable physics on the player
        this.phaser.physics.p2.enable(this.sprite);
        this.sprite.body.setCircle(15);

        // Adding a collision event for player that is hot potato
        if(this.isHotPotatoe) {
            this.jumpingHotPotatoSignalBinding = this.sprite.body.onBeginContact.add(this.jumpingHotPotatoCallback, this);
        }
    } else {
        //We need to center anchor on client because
        //when we set a p2 physics body on this sprite on server
        //the anchor is centered implicitly
        this.sprite.anchor.setTo(0.5, 0.5);

        this.notifyText = this.phaser.add.text(0, 0, 'You are the Hot Potatoe !', {fill: 'white'});
        this.notifyText.visible = false;

        // Display player name
        var currentRoom =  GameRooms.currentRoom();
        var _player =_.findWhere(currentRoom.players, {id: this.id});
        this.playerNameText = this.phaser.add.text(x, y + 23 , _player.name.substring(0, 15), {
            font: "14px Arial",
            fill: "#ffffff",
            align: "center"
        });
        this.playerNameText.anchor.setTo(0.5, 0.5);
        this.playerNameText.visible = false;//will be displayed when player positions are synced
    }

    // If this player is controlled by the user
    // Camera should follow him
    if (this.isCurrentPlayer) {
        this.phaser.camera.follow(this.sprite);
    }
};

/**
 * Update player
 */
HotPotatoe.Player.prototype.update = function() {
    //Handle player input and update sprite sync
    if (Meteor.isServer) {
        if (UserActions[this.id].cursors.left.isDown) {
            this.sprite.body.data.force[0] += this.force;
        } else if (UserActions[this.id].cursors.right.isDown) {
            this.sprite.body.data.force[0] -= this.force;
        }
        if (UserActions[this.id].cursors.up.isDown) {
            this.sprite.body.data.force[1] += this.force;
        } else if (UserActions[this.id].cursors.down.isDown) {
            this.sprite.body.data.force[1] -= this.force;
        }
        // Apply max velocity
        if (this.sprite.body.velocity.x < -this.maxVelocity) {
            this.sprite.body.velocity.x = -this.maxVelocity;
        }
        if (this.sprite.body.velocity.x > this.maxVelocity) {
            this.sprite.body.velocity.x = this.maxVelocity;
        }
        if (this.sprite.body.velocity.y < -this.maxVelocity) {
            this.sprite.body.velocity.y = -this.maxVelocity;
        }
        if (this.sprite.body.velocity.y > this.maxVelocity) {
            this.sprite.body.velocity.y = this.maxVelocity;
        }
    }

    if (Meteor.isClient) {
        this.notifyText.visible = this.isHotPotatoe && this.isCurrentPlayer;
        this.playerNameText.x = this.sprite.x;
        this.playerNameText.y = this.sprite.y + 23;
        if (this.sprite.visible) {
            this.playerNameText.visible = true;
        }
    }
};

/**
 * Defines the hot potatoe flag attribute state,
 * and change sprite texture
 * @param {boolean} hotpotatoe
 */
HotPotatoe.Player.prototype.setHotPotatoe = function(hotpotatoe) {
    this.isHotPotatoe = hotpotatoe;
    if (this.sprite) {
        var newassetId = this.parent.assetIds.players;
        if (hotpotatoe) {
            newassetId = this.parent.assetIds.hotpotatoe;
        } else if (this.isCurrentPlayer) {
            newassetId = this.parent.assetIds.currentPlayer;
        }

        // change sprite texture
        if (this.assetId != newassetId) {
            this.assetId = newassetId;
            this.sprite.loadTexture(newassetId, 0);
        }

        // When a player is defined hot potato, a collision event is added after a delay
        // to make possible jumping the hot potato from player to player
        if (hotpotatoe && Meteor.isServer) {
            this.phaser.time.events.repeat(600, 1, function() {
                this.jumpingHotPotatoSignalBinding = this.sprite.body.onBeginContact.add(this.jumpingHotPotatoCallback, this);
            }, this);
        }
    }

};

/**
 * Callback which is called when a player collides with an object
 *
 * @param {Phaser.Physics.P2.Body} objectCollided
 */
HotPotatoe.Player.prototype.jumpingHotPotatoCallback = function(objectCollided) {
    if (objectCollided && objectCollided.sprite) {
        var _playerCollided = _.findWhere(this.parent.players, {id: objectCollided.sprite.playerId});
        _playerCollided.setHotPotatoe(true);
        this.setHotPotatoe(false);
        this.jumpingHotPotatoSignalBinding.detach();
    }
};<commit_msg>first experimentation of phaser tweens object with yoyo properties for hot potato animation<commit_after>if (typeof HotPotatoe === "undefined") {
    HotPotatoe = {};
}

/**
 * Player
 *
 * @param {HotPotatoe.Game} game
 * @param {string} id - A unique player ID
 * @param {boolean} [isCurrentPlayer=false] - Flag to indicate if this player is controlled by current user
 * @constructor
 */
HotPotatoe.Player = function (game, id, isCurrentPlayer) {
    this.parent = game;
    this.phaser = game.phaser;
    this.id = id;
    this.isCurrentPlayer = false;
    this.force = 15;
    this.maxVelocity = 200;
    this.isHotPotatoe = false;

    if (typeof isCurrentPlayer !== 'undefined') {
        this.isCurrentPlayer = isCurrentPlayer;
    }
};

/**
 * Pre-load player assets
 */
HotPotatoe.Player.prototype.preload = function() {
    this.assetId = this.parent.assetIds.players;
    if (this.isCurrentPlayer) {
        this.assetId = this.parent.assetIds.currentPlayer;
    }
};

/**
 * Create player
 */
HotPotatoe.Player.prototype.create = function() {
    var x = Math.floor((Math.random() * 700) + 1);
    var y = Math.floor((Math.random() * 500) + 1);
    this.sprite = this.phaser.add.sprite(x, y, this.assetId);
    if (Meteor.isClient) {
        //On client we hide players until their positions get synced
        this.sprite.visible = false;

        this.halo = this.phaser.add.graphics(x, y);
        this.halo.lineStyle(1, 0xffffff);
        this.halo.drawCircle(0, 0, 20);
        this.halo.visible = false;
        // with phaser 2.2, repeat value = -1 for unlimited repeat
        this.phaser.add.tween(this.halo.scale).to( { x: 1.5, y: 1.5 }, 600, Phaser.Easing.Linear.None, true, 0, 100, true);
    }
    this.sprite.playerId = this.id;

    if (Meteor.isServer) {
        // We need to enable physics on the player
        this.phaser.physics.p2.enable(this.sprite);
        this.sprite.body.setCircle(15);

        // Adding a collision event for player that is hot potato
        if(this.isHotPotatoe) {
            this.jumpingHotPotatoSignalBinding = this.sprite.body.onBeginContact.add(this.jumpingHotPotatoCallback, this);
        }
    } else {
        //We need to center anchor on client because
        //when we set a p2 physics body on this sprite on server
        //the anchor is centered implicitly
        this.sprite.anchor.setTo(0.5, 0.5);

        this.notifyText = this.phaser.add.text(0, 0, 'You are the Hot Potatoe !', {fill: 'white'});
        this.notifyText.visible = false;

        // Display player name
        var currentRoom =  GameRooms.currentRoom();
        var _player =_.findWhere(currentRoom.players, {id: this.id});
        this.playerNameText = this.phaser.add.text(x, y + 23 , _player.name.substring(0, 15), {
            font: "14px Arial",
            fill: "#ffffff",
            align: "center"
        });
        this.playerNameText.anchor.setTo(0.5, 0.5);
        this.playerNameText.visible = false;//will be displayed when player positions are synced
    }

    // If this player is controlled by the user
    // Camera should follow him
    if (this.isCurrentPlayer) {
        this.phaser.camera.follow(this.sprite);
    }
};

/**
 * Update player
 */
HotPotatoe.Player.prototype.update = function() {
    //Handle player input and update sprite sync
    if (Meteor.isServer) {
        if (UserActions[this.id].cursors.left.isDown) {
            this.sprite.body.data.force[0] += this.force;
        } else if (UserActions[this.id].cursors.right.isDown) {
            this.sprite.body.data.force[0] -= this.force;
        }
        if (UserActions[this.id].cursors.up.isDown) {
            this.sprite.body.data.force[1] += this.force;
        } else if (UserActions[this.id].cursors.down.isDown) {
            this.sprite.body.data.force[1] -= this.force;
        }
        // Apply max velocity
        if (this.sprite.body.velocity.x < -this.maxVelocity) {
            this.sprite.body.velocity.x = -this.maxVelocity;
        }
        if (this.sprite.body.velocity.x > this.maxVelocity) {
            this.sprite.body.velocity.x = this.maxVelocity;
        }
        if (this.sprite.body.velocity.y < -this.maxVelocity) {
            this.sprite.body.velocity.y = -this.maxVelocity;
        }
        if (this.sprite.body.velocity.y > this.maxVelocity) {
            this.sprite.body.velocity.y = this.maxVelocity;
        }
    }

    if (Meteor.isClient) {
        this.notifyText.visible = this.isHotPotatoe && this.isCurrentPlayer;
        this.playerNameText.x = this.sprite.x;
        this.playerNameText.y = this.sprite.y + 23;
        if (this.sprite.visible) {
            this.playerNameText.visible = true;
        }
        this.halo.visible = this.isHotPotatoe && this.sprite.visible;
        this.halo.x = this.sprite.x;
        this.halo.y = this.sprite.y;
    }
};

/**
 * Defines the hot potatoe flag attribute state,
 * and change sprite texture
 * @param {boolean} hotpotatoe
 */
HotPotatoe.Player.prototype.setHotPotatoe = function(hotpotatoe) {
    this.isHotPotatoe = hotpotatoe;
    if (this.sprite) {
        var newassetId = this.parent.assetIds.players;
        if (hotpotatoe) {
            newassetId = this.parent.assetIds.hotpotatoe;
        } else if (this.isCurrentPlayer) {
            newassetId = this.parent.assetIds.currentPlayer;
        }

        // change sprite texture
        if (this.assetId != newassetId) {
            this.assetId = newassetId;
            this.sprite.loadTexture(newassetId, 0);
        }

        // When a player is defined hot potato, a collision event is added after a delay
        // to make possible jumping the hot potato from player to player
        if (hotpotatoe && Meteor.isServer) {
            this.phaser.time.events.repeat(600, 1, function() {
                this.jumpingHotPotatoSignalBinding = this.sprite.body.onBeginContact.add(this.jumpingHotPotatoCallback, this);
            }, this);
        }
    }

};

/**
 * Callback which is called when a player collides with an object
 *
 * @param {Phaser.Physics.P2.Body} objectCollided
 */
HotPotatoe.Player.prototype.jumpingHotPotatoCallback = function(objectCollided) {
    if (objectCollided && objectCollided.sprite) {
        var _playerCollided = _.findWhere(this.parent.players, {id: objectCollided.sprite.playerId});
        _playerCollided.setHotPotatoe(true);
        this.setHotPotatoe(false);
        this.jumpingHotPotatoSignalBinding.detach();
    }
};<|endoftext|>


<commit_before>var util             = require('util'),
    AbstractGeocoder = require('./abstractgeocoder');

/**
 * Constructor
 */
var OpenStreetMapGeocoder = function OpenStreetMapGeocoder(httpAdapter, options) {
    this.options = ['language','email','apiKey', 'osmServer'];

    OpenStreetMapGeocoder.super_.call(this, httpAdapter, options);
    var osmServer = (options && options.osmServer) || 'http://nominatim.openstreetmap.org';
    OpenStreetMapGeocoder.prototype._endpoint = osmServer + '/search';
    OpenStreetMapGeocoder.prototype._endpoint_reverse = osmServer + '/reverse';
};

util.inherits(OpenStreetMapGeocoder, AbstractGeocoder);

OpenStreetMapGeocoder.prototype._endpoint = 'http://nominatim.openstreetmap.org/search';

OpenStreetMapGeocoder.prototype._endpoint_reverse = 'http://nominatim.openstreetmap.org/reverse';

/**
* Geocode
* @param <string|object>   value    Value to geocode (Address or parameters, as specified at https://wiki.openstreetmap.org/wiki/Nominatim#Parameters)
* @param <function> callback Callback method
*/
OpenStreetMapGeocoder.prototype._geocode = function(value, callback) {
    var _this = this;

    var params = this._getCommonParams();
    params.addressdetails = 1;
    if (typeof value == 'string') {
      params.q = value;
    } else {
      for (var k in value) {
        var v = value[k];
        params[k] = v;
      }
    }
    this._forceParams(params);

    this.httpAdapter.get(this._endpoint , params, function(err, result) {
        if (err) {
            return callback(err);
        } else {

            var results = [];

            if(result.error) {
              return callback(new Error(result.error));
            }

            if (result instanceof Array) {
              for (var i = 0; i < result.length; i++) {
                results.push(_this._formatResult(result[i]));
              }
            } else {
              results.push(_this._formatResult(result));
            }

            results.raw = result;
            callback(false, results);
        }

    });

};

OpenStreetMapGeocoder.prototype._formatResult = function(result) {

    var countryCode = result.address.country_code;
    if (countryCode) {
        countryCode = countryCode.toUpperCase();
    }

    var latitude = result.lat;
    if (latitude) {
      latitude = parseFloat(latitude);
    }

    var longitude = result.lon;
    if (longitude) {
      longitude = parseFloat(longitude);
    }

    return {
        'latitude' : latitude,
        'longitude' : longitude,
        'formattedAddress': result.display_name,
        'country' : result.address.country,
        'city' : result.address.city || result.address.town || result.address.village || result.address.hamlet,
        'state': result.address.state,
        'zipcode' : result.address.postcode,
        'streetName': result.address.road || result.address.cycleway,
        'streetNumber' : result.address.house_number,
        'countryCode' : countryCode,
        'neighbourhood': result.address.neighbourhood || ''
    };
};

/**
* Reverse geocoding
* @param {lat:<number>,lon:<number>, ...}  lat: Latitude, lon: Longitude, ... see https://wiki.openstreetmap.org/wiki/Nominatim#Parameters_2
* @param <function> callback Callback method
*/
OpenStreetMapGeocoder.prototype._reverse = function(query, callback) {

    var _this = this;

    var params = this._getCommonParams();
    for (var k in query) {
      var v = query[k];
      params[k] = v;
    }
    this._forceParams(params);

    this.httpAdapter.get(this._endpoint_reverse , params, function(err, result) {
        if (err) {
            return callback(err);
        } else {
          if(result.error) {
            return callback(new Error(result.error));
          }

          var results = [];
          if (result instanceof Array) {
            for (var i = 0; i < result.length; i++) {
              results.push(_this._formatResult(result[i]));
            }
          } else {
            results.push(_this._formatResult(result));
          }

          results.raw = result;
          callback(false, results);
        }
    });
};

/**
* Prepare common params
*
* @return <Object> common params
*/
OpenStreetMapGeocoder.prototype._getCommonParams = function(){
    var params = {};

    for (var k in this.options) {
      var v = this.options[k];
      if (!v) {
        continue;
      }
      if (k === 'language') {
        k = 'accept-language';
      }
      params[k] = v;
    }

    return params;
};

OpenStreetMapGeocoder.prototype._forceParams = function(params){
    params.format = 'json';
    params.addressdetails = 1;
};

module.exports = OpenStreetMapGeocoder;
<commit_msg>Add SSL to API endpoint (nominatim.openstreetmap.org)<commit_after>var util             = require('util'),
    AbstractGeocoder = require('./abstractgeocoder');

/**
 * Constructor
 */
var OpenStreetMapGeocoder = function OpenStreetMapGeocoder(httpAdapter, options) {
    this.options = ['language','email','apiKey', 'osmServer'];

    OpenStreetMapGeocoder.super_.call(this, httpAdapter, options);
    var osmServer = (options && options.osmServer) || 'http://nominatim.openstreetmap.org';
    OpenStreetMapGeocoder.prototype._endpoint = osmServer + '/search';
    OpenStreetMapGeocoder.prototype._endpoint_reverse = osmServer + '/reverse';
};

util.inherits(OpenStreetMapGeocoder, AbstractGeocoder);

OpenStreetMapGeocoder.prototype._endpoint = 'https://nominatim.openstreetmap.org/search';

OpenStreetMapGeocoder.prototype._endpoint_reverse = 'https://nominatim.openstreetmap.org/reverse';

/**
* Geocode
* @param <string|object>   value    Value to geocode (Address or parameters, as specified at https://wiki.openstreetmap.org/wiki/Nominatim#Parameters)
* @param <function> callback Callback method
*/
OpenStreetMapGeocoder.prototype._geocode = function(value, callback) {
    var _this = this;

    var params = this._getCommonParams();
    params.addressdetails = 1;
    if (typeof value == 'string') {
      params.q = value;
    } else {
      for (var k in value) {
        var v = value[k];
        params[k] = v;
      }
    }
    this._forceParams(params);

    this.httpAdapter.get(this._endpoint , params, function(err, result) {
        if (err) {
            return callback(err);
        } else {

            var results = [];

            if(result.error) {
              return callback(new Error(result.error));
            }

            if (result instanceof Array) {
              for (var i = 0; i < result.length; i++) {
                results.push(_this._formatResult(result[i]));
              }
            } else {
              results.push(_this._formatResult(result));
            }

            results.raw = result;
            callback(false, results);
        }

    });

};

OpenStreetMapGeocoder.prototype._formatResult = function(result) {

    var countryCode = result.address.country_code;
    if (countryCode) {
        countryCode = countryCode.toUpperCase();
    }

    var latitude = result.lat;
    if (latitude) {
      latitude = parseFloat(latitude);
    }

    var longitude = result.lon;
    if (longitude) {
      longitude = parseFloat(longitude);
    }

    return {
        'latitude' : latitude,
        'longitude' : longitude,
        'formattedAddress': result.display_name,
        'country' : result.address.country,
        'city' : result.address.city || result.address.town || result.address.village || result.address.hamlet,
        'state': result.address.state,
        'zipcode' : result.address.postcode,
        'streetName': result.address.road || result.address.cycleway,
        'streetNumber' : result.address.house_number,
        'countryCode' : countryCode,
        'neighbourhood': result.address.neighbourhood || ''
    };
};

/**
* Reverse geocoding
* @param {lat:<number>,lon:<number>, ...}  lat: Latitude, lon: Longitude, ... see https://wiki.openstreetmap.org/wiki/Nominatim#Parameters_2
* @param <function> callback Callback method
*/
OpenStreetMapGeocoder.prototype._reverse = function(query, callback) {

    var _this = this;

    var params = this._getCommonParams();
    for (var k in query) {
      var v = query[k];
      params[k] = v;
    }
    this._forceParams(params);

    this.httpAdapter.get(this._endpoint_reverse , params, function(err, result) {
        if (err) {
            return callback(err);
        } else {
          if(result.error) {
            return callback(new Error(result.error));
          }

          var results = [];
          if (result instanceof Array) {
            for (var i = 0; i < result.length; i++) {
              results.push(_this._formatResult(result[i]));
            }
          } else {
            results.push(_this._formatResult(result));
          }

          results.raw = result;
          callback(false, results);
        }
    });
};

/**
* Prepare common params
*
* @return <Object> common params
*/
OpenStreetMapGeocoder.prototype._getCommonParams = function(){
    var params = {};

    for (var k in this.options) {
      var v = this.options[k];
      if (!v) {
        continue;
      }
      if (k === 'language') {
        k = 'accept-language';
      }
      params[k] = v;
    }

    return params;
};

OpenStreetMapGeocoder.prototype._forceParams = function(params){
    params.format = 'json';
    params.addressdetails = 1;
};

module.exports = OpenStreetMapGeocoder;
<|endoftext|>


<commit_before>module.exports = function (sails) {

	/**
	 * Module dependencies.
	 */

	var	util					= require( 'sails-util'),
			SocketServer	= require('socket.io'),
			RedisStore		= require('socket.io/lib/stores/redis'),
			Redis					= require('socket.io/node_modules/redis'),
			Socket				= {
				authorization	: require('./authorization')(sails),
				connection		: require('./connection')(sails)
			};

	/**
	 * loadSocketIO()
	 * @param {Function} cb
	 *
	 * Prepare the nascent ws:// server (but don't listen for connections yet)
	 */

	return function loadSocketIO (cb) {
		sails.log.verbose('Configuring socket (ws://) server...');

		var socketConfig = sails.config.sockets;

		// Socket.io server (WebSockets+polyfill to support Flash sockets, AJAX long polling, etc.)
		var io = sails.io = sails.ws =
		SocketServer.listen(sails.hooks.http.server, {
			logger: {
				info: function (){}
			}
		});

		// If logger option not set, use the default Sails logger config
		if (!socketConfig.logger) {
			var logLevels = {
				'silent': 0,
				'error': 1,
				'warn': 2,
				'debug': 4, // Socket.io flips these around (and it considers debug more verbose than `info`)
				'info': 3,	// Socket.io flips these around
				'verbose': 4	// Socket.io has no concept of `verbose`
			};
			io.set('log level', logLevels[sails.config.log.level] || logLevels['info']);
			io.set('logger', {
				error: sails.log.error,
				warn: sails.log.warn,
				info: sails.log.verbose,
				debug: sails.log.verbose // socket.io considers `debug` the most verbose config, so we'll use verbose to represent it
			});
		}

		// Process the Config File
		util.each(socketConfig, function(value, propertyName) {

			// Handle `Memory` adapter value
			if (propertyName === 'adapter' && value === 'memory') return;

			// Setup custom socket.io MQ (pubsub) store(s)
			if (propertyName === 'adapter' && value === 'redis') {
				var host = socketConfig.host || '127.0.0.1';
				var port = socketConfig.port || 6379;

				var pub = createRedisConnection(port, host, 'pub');
				var sub = createRedisConnection(port, host, 'sub');
				var client = createRedisConnection(port, host, 'client');

				var storeConfig = {
					redisPub: pub,
					redisSub: sub,
					redisClient: client
				};

				// Add a pointer to the redis, required with Auth
				if(socketConfig.pass) {
					storeConfig.redis = Redis;
				}

				io.set('store', new RedisStore(storeConfig));
				return;
			}

			// Configure logic to be run before allowing sockets to connect
			if (propertyName === 'authorization') {

				// Custom logic
				if (util.isFunction(value)) {
					io.set('authorization', value);
					return;
				}

				// `authorization: true` means go ahead and use the default behavior
				if (value === true) {
					io.set('authorization', Socket.authorization);
					return;
				}

				// Otherwise skip the authorization step
				io.set('authorization', false);

				return;
			}

			// If value is explicitly undefined, do nothing
			if (util.isUndefined(value)) return;

			// In the general case, pass the configuration straight down to socket.io
			io.set(propertyName, value);

		});

		// For later:
		// io.configure('development', function() {});
		// io.configure('production', function() {});

		// Link Socket.io requests to a controller/action
		// When a socket.io client connects, listen for the actions in the routing table
		// Authorization has already passed at this point!
		io.sockets.on('connection', Socket.connection);

		cb && cb();
	};

	/**
	 * Creates a new Redis Connection if specified.
	 *
	 * Can be used to connect to remote server with authentication if
	 * `pass` is declared in the socketConfig file.
	 */

	function createRedisConnection(port, host, id) {

		var socketConfig = sails.config.sockets;

		// Create a new client using the port, host and other options
		var client = Redis.createClient(port, host, socketConfig);

		// If a password is needed use client.auth to set it
		if(socketConfig.pass) {
			client.auth(socketConfig.pass, function(err) {
		        if (err) throw err;
		    });
		}

		// If a db is set select it on the client
		if (socketConfig.db) {
			client.select(socketConfig.db);
		}

		// If Redis connection ends, catch the error and retry
		// until it comes back

		client.on('ready', function() {
			sails.log.debug('RedisClient::Events[ready]: [OK] Redis "' + id + '" is up. Connections: ', client.connections);
		});

		client.on('end', function() {
		  sails.log.debug('RedisClient::Events[end]: "' + id + '" , Connected:', client.connected);
		});

		client.on('error', function (err) {
			sails.log.error('RedisClient::Events[error]: "' + id + '" , ' + err);
		    if (/ECONNREFUSED/g.test(err)) {
		        sails.log.error('Waiting for "' + id + '" redis client to come back online. Connections:', client.connections);
		    }
		});

		return client;
	}

};
<commit_msg>Fix funky spaces/tabs mixing<commit_after>module.exports = function (sails) {

	/**
	 * Module dependencies.
	 */

	var	util         = require( 'sails-util'),
		SocketServer = require('socket.io'),
		RedisStore   = require('socket.io/lib/stores/redis'),
		Redis        = require('socket.io/node_modules/redis'),
		Socket       = {
			authorization	: require('./authorization')(sails),
			connection		: require('./connection')(sails)
		};

	/**
	 * loadSocketIO()
	 * @param {Function} cb
	 *
	 * Prepare the nascent ws:// server (but don't listen for connections yet)
	 */

	return function loadSocketIO (cb) {
		sails.log.verbose('Configuring socket (ws://) server...');

		var socketConfig = sails.config.sockets;

		// Socket.io server (WebSockets+polyfill to support Flash sockets, AJAX long polling, etc.)
		var io = sails.io = sails.ws =
		SocketServer.listen(sails.hooks.http.server, {
			logger: {
				info: function (){}
			}
		});

		// If logger option not set, use the default Sails logger config
		if (!socketConfig.logger) {
			var logLevels = {
				'silent': 0,
				'error': 1,
				'warn': 2,
				'debug': 4, // Socket.io flips these around (and it considers debug more verbose than `info`)
				'info': 3,	// Socket.io flips these around
				'verbose': 4	// Socket.io has no concept of `verbose`
			};
			io.set('log level', logLevels[sails.config.log.level] || logLevels['info']);
			io.set('logger', {
				error: sails.log.error,
				warn: sails.log.warn,
				info: sails.log.verbose,
				debug: sails.log.verbose // socket.io considers `debug` the most verbose config, so we'll use verbose to represent it
			});
		}

		// Process the Config File
		util.each(socketConfig, function(value, propertyName) {

			// Handle `Memory` adapter value
			if (propertyName === 'adapter' && value === 'memory') return;

			// Setup custom socket.io MQ (pubsub) store(s)
			if (propertyName === 'adapter' && value === 'redis') {
				var host = socketConfig.host || '127.0.0.1';
				var port = socketConfig.port || 6379;

				var pub = createRedisConnection(port, host, 'pub');
				var sub = createRedisConnection(port, host, 'sub');
				var client = createRedisConnection(port, host, 'client');

				var storeConfig = {
					redisPub: pub,
					redisSub: sub,
					redisClient: client
				};

				// Add a pointer to the redis, required with Auth
				if(socketConfig.pass) {
					storeConfig.redis = Redis;
				}

				io.set('store', new RedisStore(storeConfig));
				return;
			}

			// Configure logic to be run before allowing sockets to connect
			if (propertyName === 'authorization') {

				// Custom logic
				if (util.isFunction(value)) {
					io.set('authorization', value);
					return;
				}

				// `authorization: true` means go ahead and use the default behavior
				if (value === true) {
					io.set('authorization', Socket.authorization);
					return;
				}

				// Otherwise skip the authorization step
				io.set('authorization', false);

				return;
			}

			// If value is explicitly undefined, do nothing
			if (util.isUndefined(value)) return;

			// In the general case, pass the configuration straight down to socket.io
			io.set(propertyName, value);

		});

		// For later:
		// io.configure('development', function() {});
		// io.configure('production', function() {});

		// Link Socket.io requests to a controller/action
		// When a socket.io client connects, listen for the actions in the routing table
		// Authorization has already passed at this point!
		io.sockets.on('connection', Socket.connection);

		cb && cb();
	};

	/**
	 * Creates a new Redis Connection if specified.
	 *
	 * Can be used to connect to remote server with authentication if
	 * `pass` is declared in the socketConfig file.
	 */

	function createRedisConnection(port, host, id) {

		var socketConfig = sails.config.sockets;

		// Create a new client using the port, host and other options
		var client = Redis.createClient(port, host, socketConfig);

		// If a password is needed use client.auth to set it
		if(socketConfig.pass) {
			client.auth(socketConfig.pass, function(err) {
		        if (err) throw err;
		    });
		}

		// If a db is set select it on the client
		if (socketConfig.db) {
			client.select(socketConfig.db);
		}

		// If Redis connection ends, catch the error and retry
		// until it comes back

		client.on('ready', function() {
			sails.log.debug('RedisClient::Events[ready]: [OK] Redis "' + id + '" is up. Connections: ', client.connections);
		});

		client.on('end', function() {
		  sails.log.debug('RedisClient::Events[end]: "' + id + '" , Connected:', client.connected);
		});

		client.on('error', function (err) {
			sails.log.error('RedisClient::Events[error]: "' + id + '" , ' + err);
			if (/ECONNREFUSED/g.test(err)) {
				 sails.log.error('Waiting for "' + id + '" redis client to come back online. Connections:', client.connections);
			}
		});

		return client;
	}

};
<|endoftext|>


<commit_before>/*jshint maxdepth:10 */
'use strict';

var db = require('../../db'),
    _ = require('lodash'),
    Q = require('q'),
    maybeMongodId = /^[0-9a-fA-F]{24}$/;

/**
 * Module used for pre-loading a content object and keeping it in the context to be evaluated later.
 * This exists so that we don't have to make mulitple calls to the database if we are going to be
 * performing some form of validation.
 * @param kontx
 * @param next
 */
module.exports = function setTempContent(kontx, next){

    var content = kontx._content;

    // If we have an array, next must be called array.length times before getReferences(content.fields, promises, next, kontx);actually moving on to the next middleware
    if (content.results && _.isArray(content.results)) {
        if (content.results.length) {
            next = _.after(content.results.length, next);
            _.each(content.results, function(result) {
                getReferences(result.fields, [], next, kontx);
            });
        } else {
            // if there are no results, there is nothing to do
            next();
        }
    } else {
        getReferences(content.fields, [], next, kontx);
    }

};

function getReferences(fields, promises, next, kontx) {

    _.each(fields, function(value, key) {
        getArrayReferences(fields, value, key, promises, next, kontx);
        getDirectReferences(fields, value, key, promises, next, kontx);
        getObjectReferences(fields, value, key, promises, next, kontx);
    });

    tryToSendResponse(promises, next);
}

function getObjectReferences(fields, value, key, promises, next, kontx) {
    _.each(value, function(v, k) {
        if (_.isString(v)) {
            getDirectReferences(value, v, k, promises, next, kontx);
        } else {
            getReferences(v, promises, next, kontx);
        }
    });
}

function getDirectReferences(fields, value, key, promises, next, kontx) {
    var query;

    if (!_.isString(value)) {
        return;
    }

    if (maybeMongodId.test(value)) {

        query = db.content.getById(value);
        promises.push(query);

        query.then(function(response) {
            fields[key] = response.fields;
            getReferences(response.fields, promises, next, kontx);
        })
            .catch(function(e) {console.log(e); });
    }
}

function getArrayReferences(parent, array, key, promises, next, kontx) {
    if (!_.isArray(array)) {
        return;
    }

    _.each(array, function(item, index) {
        var query;
        if (maybeMongodId.test(item)) {

            query = db.content.getById(item);
            promises.push(query);

            query.then(function(response) {
                parent[key][index] = response.fields;
                getReferences(response.fields, promises, next, kontx);
            })
                .catch(function(e) {console.log(e); });
        }
    });
}

function tryToSendResponse(promises, next) {
    var count = 0;
    _.each(promises, function(promise) {
        if (promise.isFulfilled()) {
            ++count;
        }
    });
    if (_.every(promises, function(promise) { return promise.isFulfilled(); })) {
        next();
    }
}
<commit_msg>Updating comments<commit_after>/*jshint maxdepth:10 */
'use strict';

var db = require('../../db'),
    _ = require('lodash'),
    Q = require('q'),
    maybeMongodId = /^[0-9a-fA-F]{24}$/;

/**
 * Module used for populating content references.
 * This is basically a deep recursive mongoose populate call on all paths.
 * @param kontx
 * @param next
 */
module.exports = function (kontx, next){

    var content = kontx._content;

    // If we have an array, next must be called array.length times before getReferences(content.fields, promises, next, kontx);actually moving on to the next middleware
    if (content.results && _.isArray(content.results)) {
        if (content.results.length) {
            next = _.after(content.results.length, next);
            _.each(content.results, function(result) {
                getReferences(result.fields, [], next, kontx);
            });
        } else {
            // if there are no results, there is nothing to do
            next();
        }
    } else {
        getReferences(content.fields, [], next, kontx);
    }

};

function getReferences(fields, promises, next, kontx) {

    _.each(fields, function(value, key) {
        getArrayReferences(fields, value, key, promises, next, kontx);
        getDirectReferences(fields, value, key, promises, next, kontx);
        getObjectReferences(fields, value, key, promises, next, kontx);
    });

    tryToSendResponse(promises, next);
}

function getObjectReferences(fields, value, key, promises, next, kontx) {
    _.each(value, function(v, k) {
        if (_.isString(v)) {
            getDirectReferences(value, v, k, promises, next, kontx);
        } else {
            getReferences(v, promises, next, kontx);
        }
    });
}

function getDirectReferences(fields, value, key, promises, next, kontx) {
    var query;

    if (!_.isString(value)) {
        return;
    }

    if (maybeMongodId.test(value)) {

        query = db.content.getById(value);
        promises.push(query);

        query.then(function(response) {
            fields[key] = response.fields;
            getReferences(response.fields, promises, next, kontx);
        })
            .catch(function(e) {console.log(e); });
    }
}

function getArrayReferences(parent, array, key, promises, next, kontx) {
    if (!_.isArray(array)) {
        return;
    }

    _.each(array, function(item, index) {
        var query;
        if (maybeMongodId.test(item)) {

            query = db.content.getById(item);
            promises.push(query);

            query.then(function(response) {
                parent[key][index] = response.fields;
                getReferences(response.fields, promises, next, kontx);
            })
                .catch(function(e) {console.log(e); });
        }
    });
}

function tryToSendResponse(promises, next) {
    var count = 0;
    _.each(promises, function(promise) {
        if (promise.isFulfilled()) {
            ++count;
        }
    });
    if (_.every(promises, function(promise) { return promise.isFulfilled(); })) {
        next();
    }
}
<|endoftext|>


<commit_before>import {nullCommit} from '../commit';
import BranchSet from '../branch-set';
import RemoteSet from '../remote-set';
import {nullOperationStates} from '../operation-states';
import MultiFilePatch from '../patch/multi-file-patch';

/**
 * Map of registered subclasses to allow states to transition to one another without circular dependencies.
 * Subclasses of State should call `State.register` to add themselves here.
 */
const stateConstructors = new Map();

/**
 * Base class for Repository states. Implements default "null" behavior.
 */
export default class State {
  constructor(repository) {
    this.repository = repository;
  }

  static register(Subclass) {
    stateConstructors.set(Subclass.name, Subclass);
  }

  // This state has just been entered. Perform any asynchronous initialization that needs to occur.
  start() {
    return Promise.resolve();
  }

  // State probe predicates ////////////////////////////////////////////////////////////////////////////////////////////
  // Allow external callers to identify which state a Repository is in if necessary.

  isLoadingGuess() {
    return false;
  }

  isAbsentGuess() {
    return false;
  }

  isAbsent() {
    return false;
  }

  isLoading() {
    return false;
  }

  isEmpty() {
    return false;
  }

  isPresent() {
    return false;
  }

  isTooLarge() {
    return false;
  }

  isDestroyed() {
    return false;
  }

  // Behavior probe predicates /////////////////////////////////////////////////////////////////////////////////////////
  // Determine specific rendering behavior based on the current state.

  isUndetermined() {
    return false;
  }

  showGitTabInit() {
    return false;
  }

  showGitTabInitInProgress() {
    return false;
  }

  showGitTabLoading() {
    return false;
  }

  showStatusBarTiles() {
    return false;
  }

  hasDirectory() {
    return true;
  }

  // Lifecycle actions /////////////////////////////////////////////////////////////////////////////////////////////////
  // These generally default to rejecting a Promise with an error.

  init() {
    return unsupportedOperationPromise(this, 'init');
  }

  clone(remoteUrl) {
    return unsupportedOperationPromise(this, 'clone');
  }

  destroy() {
    return this.transitionTo('Destroyed');
  }

  refresh() {
    // No-op
  }

  observeFilesystemChange(events) {
    this.repository.refresh();
  }

  updateCommitMessageAfterFileSystemChange() {
    this.repository.refresh();
  }

  // Git operations ////////////////////////////////////////////////////////////////////////////////////////////////////
  // These default to rejecting a Promise with an error stating that the operation is not supported in the current
  // state.

  // Staging and unstaging

  stageFiles(paths) {
    return unsupportedOperationPromise(this, 'stageFiles');
  }

  unstageFiles(paths) {
    return unsupportedOperationPromise(this, 'unstageFiles');
  }

  stageFilesFromParentCommit(paths) {
    return unsupportedOperationPromise(this, 'stageFilesFromParentCommit');
  }

  applyPatchToIndex(patch) {
    return unsupportedOperationPromise(this, 'applyPatchToIndex');
  }

  applyPatchToWorkdir(patch) {
    return unsupportedOperationPromise(this, 'applyPatchToWorkdir');
  }

  // Committing

  commit(message, options) {
    return unsupportedOperationPromise(this, 'commit');
  }

  // Merging

  merge(branchName) {
    return unsupportedOperationPromise(this, 'merge');
  }

  abortMerge() {
    return unsupportedOperationPromise(this, 'abortMerge');
  }

  checkoutSide(side, paths) {
    return unsupportedOperationPromise(this, 'checkoutSide');
  }

  mergeFile(oursPath, commonBasePath, theirsPath, resultPath) {
    return unsupportedOperationPromise(this, 'mergeFile');
  }

  writeMergeConflictToIndex(filePath, commonBaseSha, oursSha, theirsSha) {
    return unsupportedOperationPromise(this, 'writeMergeConflictToIndex');
  }

  // Checkout

  checkout(revision, options = {}) {
    return unsupportedOperationPromise(this, 'checkout');
  }

  checkoutPathsAtRevision(paths, revision = 'HEAD') {
    return unsupportedOperationPromise(this, 'checkoutPathsAtRevision');
  }

  // Reset

  undoLastCommit() {
    return unsupportedOperationPromise(this, 'undoLastCommit');
  }

  // Remote interactions

  fetch(branchName) {
    return unsupportedOperationPromise(this, 'fetch');
  }

  pull(branchName) {
    return unsupportedOperationPromise(this, 'pull');
  }

  push(branchName) {
    return unsupportedOperationPromise(this, 'push');
  }

  // Configuration

  setConfig(option, value, {replaceAll} = {}) {
    return unsupportedOperationPromise(this, 'setConfig');
  }

  unsetConfig(option) {
    return unsupportedOperationPromise(this, 'unsetConfig');
  }

  // Direct blob interactions

  createBlob({filePath, stdin} = {}) {
    return unsupportedOperationPromise(this, 'createBlob');
  }

  expandBlobToFile(absFilePath, sha) {
    return unsupportedOperationPromise(this, 'expandBlobToFile');
  }

  // Discard history

  createDiscardHistoryBlob() {
    return unsupportedOperationPromise(this, 'createDiscardHistoryBlob');
  }

  updateDiscardHistory() {
    return unsupportedOperationPromise(this, 'updateDiscardHistory');
  }

  storeBeforeAndAfterBlobs(filePaths, isSafe, destructiveAction, partialDiscardFilePath = null) {
    return unsupportedOperationPromise(this, 'storeBeforeAndAfterBlobs');
  }

  restoreLastDiscardInTempFiles(isSafe, partialDiscardFilePath = null) {
    return unsupportedOperationPromise(this, 'restoreLastDiscardInTempFiles');
  }

  popDiscardHistory(partialDiscardFilePath = null) {
    return unsupportedOperationPromise(this, 'popDiscardHistory');
  }

  clearDiscardHistory(partialDiscardFilePath = null) {
    return unsupportedOperationPromise(this, 'clearDiscardHistory');
  }

  discardWorkDirChangesForPaths(paths) {
    return unsupportedOperationPromise(this, 'discardWorkDirChangesForPaths');
  }

  // Accessors /////////////////////////////////////////////////////////////////////////////////////////////////////////
  // When possible, these default to "empty" results when invoked in states that don't have information available, or
  // fail in a way that's consistent with the requested information not being found.

  // Index queries

  getStatusBundle() {
    return Promise.resolve({
      stagedFiles: {},
      unstagedFiles: {},
      mergeConflictFiles: {},
      branch: {
        oid: null,
        head: null,
        upstream: null,
        aheadBehind: {ahead: null, behind: null},
      },
    });
  }

  getStatusesForChangedFiles() {
    return Promise.resolve({
      stagedFiles: [],
      unstagedFiles: [],
      mergeConflictFiles: [],
    });
  }

  getFilePatchForPath(filePath, options = {}) {
    return Promise.resolve(new MultiFilePatch({}));
  }

  getStagedChangesPatch() {
    return Promise.resolve(new MultiFilePatch({}));
  }

  readFileFromIndex(filePath) {
    return Promise.reject(new Error(`fatal: Path ${filePath} does not exist (neither on disk nor in the index).`));
  }

  // Commit access

  getLastCommit() {
    return Promise.resolve(nullCommit);
  }

  getRecentCommits() {
    return Promise.resolve([]);
  }

  // Author information

  getAuthors() {
    return Promise.resolve([]);
  }

  // Branches

  getBranches() {
    return Promise.resolve(new BranchSet());
  }

  getHeadDescription() {
    return Promise.resolve('(no repository)');
  }

  // Merging and rebasing status

  isMerging() {
    return Promise.resolve(false);
  }

  isRebasing() {
    return Promise.resolve(false);
  }

  // Remotes

  getRemotes() {
    return Promise.resolve(new RemoteSet([]));
  }

  addRemote() {
    return unsupportedOperationPromise(this, 'addRemote');
  }

  getAheadCount(branchName) {
    return Promise.resolve(null);
  }

  getBehindCount(branchName) {
    return Promise.resolve(null);
  }

  getConfig(option, {local} = {}) {
    return Promise.resolve(null);
  }

  // Direct blob access

  getBlobContents(sha) {
    return Promise.reject(new Error(`fatal: Not a valid object name ${sha}`));
  }

  // Discard history

  hasDiscardHistory(partialDiscardFilePath = null) {
    return false;
  }

  getDiscardHistory(partialDiscardFilePath = null) {
    return [];
  }

  getLastHistorySnapshots(partialDiscardFilePath = null) {
    return null;
  }

  // Atom repo state

  getOperationStates() {
    return nullOperationStates;
  }

  setCommitMessage(message) {
    return unsupportedOperationPromise(this, 'setCommitMessage');
  }

  getCommitMessage() {
    return '';
  }

  fetchCommitMessageTemplate() {
    return unsupportedOperationPromise(this, 'fetchCommitMessageTemplate');
  }

  // Cache

  getCache() {
    return null;
  }

  // Internal //////////////////////////////////////////////////////////////////////////////////////////////////////////
  // Non-delegated methods that provide subclasses with convenient access to Repository properties.

  git() {
    return this.repository.git;
  }

  workdir() {
    return this.repository.getWorkingDirectoryPath();
  }

  // Call methods on the active Repository state, even if the state has transitioned beneath you.
  // Use this to perform operations within `start()` methods to guard against interrupted state transitions.
  current() {
    return this.repository.state;
  }

  // pipeline
  executePipelineAction(...args) {
    return this.repository.executePipelineAction(...args);
  }

  // Return a Promise that will resolve once the state transitions from Loading.
  getLoadPromise() {
    return this.repository.getLoadPromise();
  }

  getRemoteForBranch(branchName) {
    return this.repository.getRemoteForBranch(branchName);
  }

  saveDiscardHistory() {
    return this.repository.saveDiscardHistory();
  }

  // Initiate a transition to another state.
  transitionTo(stateName, ...payload) {
    const StateConstructor = stateConstructors.get(stateName);
    if (StateConstructor === undefined) {
      throw new Error(`Attempt to transition to unrecognized state ${stateName}`);
    }
    return this.repository.transition(this, StateConstructor, ...payload);
  }

  // Event broadcast

  didDestroy() {
    return this.repository.emitter.emit('did-destroy');
  }

  didUpdate() {
    return this.repository.emitter.emit('did-update');
  }

  // Direct git access
  // Non-delegated git operations for internal use within states.

  directResolveDotGitDir() {
    return Promise.resolve(null);
  }

  directGetConfig(key, options = {}) {
    return Promise.resolve(null);
  }

  directGetBlobContents() {
    return Promise.reject(new Error('Not a valid object name'));
  }

  directInit() {
    return Promise.resolve();
  }

  directClone(remoteUrl, options) {
    return Promise.resolve();
  }

  // Deferred operations
  // Direct raw git operations to the current state, even if the state has been changed. Use these methods within
  // start() methods.

  resolveDotGitDir() {
    return this.current().directResolveDotGitDir();
  }

  doInit(workdir) {
    return this.current().directInit();
  }

  doClone(remoteUrl, options) {
    return this.current().directClone(remoteUrl, options);
  }

  // Parse a DiscardHistory payload from the SHA recorded in config.
  async loadHistoryPayload() {
    const historySha = await this.current().directGetConfig('atomGithub.historySha');
    if (!historySha) {
      return {};
    }

    let blob;
    try {
      blob = await this.current().directGetBlobContents(historySha);
    } catch (e) {
      if (/Not a valid object name/.test(e.stdErr)) {
        return {};
      }

      throw e;
    }

    try {
      return JSON.parse(blob);
    } catch (e) {
      return {};
    }
  }

  // Debugging assistance.

  toString() {
    return this.constructor.name;
  }
}

function unsupportedOperationPromise(self, opName) {
  return Promise.reject(new Error(`${opName} is not available in ${self} state`));
}
<commit_msg>istanbul ignore test-only code<commit_after>import {nullCommit} from '../commit';
import BranchSet from '../branch-set';
import RemoteSet from '../remote-set';
import {nullOperationStates} from '../operation-states';
import MultiFilePatch from '../patch/multi-file-patch';

/**
 * Map of registered subclasses to allow states to transition to one another without circular dependencies.
 * Subclasses of State should call `State.register` to add themselves here.
 */
const stateConstructors = new Map();

/**
 * Base class for Repository states. Implements default "null" behavior.
 */
export default class State {
  constructor(repository) {
    this.repository = repository;
  }

  static register(Subclass) {
    stateConstructors.set(Subclass.name, Subclass);
  }

  // This state has just been entered. Perform any asynchronous initialization that needs to occur.
  start() {
    return Promise.resolve();
  }

  // State probe predicates ////////////////////////////////////////////////////////////////////////////////////////////
  // Allow external callers to identify which state a Repository is in if necessary.

  isLoadingGuess() {
    return false;
  }

  isAbsentGuess() {
    return false;
  }

  isAbsent() {
    return false;
  }

  isLoading() {
    return false;
  }

  isEmpty() {
    return false;
  }

  isPresent() {
    return false;
  }

  isTooLarge() {
    return false;
  }

  isDestroyed() {
    return false;
  }

  // Behavior probe predicates /////////////////////////////////////////////////////////////////////////////////////////
  // Determine specific rendering behavior based on the current state.

  isUndetermined() {
    return false;
  }

  showGitTabInit() {
    return false;
  }

  showGitTabInitInProgress() {
    return false;
  }

  showGitTabLoading() {
    return false;
  }

  showStatusBarTiles() {
    return false;
  }

  hasDirectory() {
    return true;
  }

  // Lifecycle actions /////////////////////////////////////////////////////////////////////////////////////////////////
  // These generally default to rejecting a Promise with an error.

  init() {
    return unsupportedOperationPromise(this, 'init');
  }

  clone(remoteUrl) {
    return unsupportedOperationPromise(this, 'clone');
  }

  destroy() {
    return this.transitionTo('Destroyed');
  }

  refresh() {
    // No-op
  }

  observeFilesystemChange(events) {
    this.repository.refresh();
  }

  updateCommitMessageAfterFileSystemChange() {
    // this is only used in unit tests, we don't need no stinkin coverage
    /* istanbul ignore next */
    this.repository.refresh();
  }

  // Git operations ////////////////////////////////////////////////////////////////////////////////////////////////////
  // These default to rejecting a Promise with an error stating that the operation is not supported in the current
  // state.

  // Staging and unstaging

  stageFiles(paths) {
    return unsupportedOperationPromise(this, 'stageFiles');
  }

  unstageFiles(paths) {
    return unsupportedOperationPromise(this, 'unstageFiles');
  }

  stageFilesFromParentCommit(paths) {
    return unsupportedOperationPromise(this, 'stageFilesFromParentCommit');
  }

  applyPatchToIndex(patch) {
    return unsupportedOperationPromise(this, 'applyPatchToIndex');
  }

  applyPatchToWorkdir(patch) {
    return unsupportedOperationPromise(this, 'applyPatchToWorkdir');
  }

  // Committing

  commit(message, options) {
    return unsupportedOperationPromise(this, 'commit');
  }

  // Merging

  merge(branchName) {
    return unsupportedOperationPromise(this, 'merge');
  }

  abortMerge() {
    return unsupportedOperationPromise(this, 'abortMerge');
  }

  checkoutSide(side, paths) {
    return unsupportedOperationPromise(this, 'checkoutSide');
  }

  mergeFile(oursPath, commonBasePath, theirsPath, resultPath) {
    return unsupportedOperationPromise(this, 'mergeFile');
  }

  writeMergeConflictToIndex(filePath, commonBaseSha, oursSha, theirsSha) {
    return unsupportedOperationPromise(this, 'writeMergeConflictToIndex');
  }

  // Checkout

  checkout(revision, options = {}) {
    return unsupportedOperationPromise(this, 'checkout');
  }

  checkoutPathsAtRevision(paths, revision = 'HEAD') {
    return unsupportedOperationPromise(this, 'checkoutPathsAtRevision');
  }

  // Reset

  undoLastCommit() {
    return unsupportedOperationPromise(this, 'undoLastCommit');
  }

  // Remote interactions

  fetch(branchName) {
    return unsupportedOperationPromise(this, 'fetch');
  }

  pull(branchName) {
    return unsupportedOperationPromise(this, 'pull');
  }

  push(branchName) {
    return unsupportedOperationPromise(this, 'push');
  }

  // Configuration

  setConfig(option, value, {replaceAll} = {}) {
    return unsupportedOperationPromise(this, 'setConfig');
  }

  unsetConfig(option) {
    return unsupportedOperationPromise(this, 'unsetConfig');
  }

  // Direct blob interactions

  createBlob({filePath, stdin} = {}) {
    return unsupportedOperationPromise(this, 'createBlob');
  }

  expandBlobToFile(absFilePath, sha) {
    return unsupportedOperationPromise(this, 'expandBlobToFile');
  }

  // Discard history

  createDiscardHistoryBlob() {
    return unsupportedOperationPromise(this, 'createDiscardHistoryBlob');
  }

  updateDiscardHistory() {
    return unsupportedOperationPromise(this, 'updateDiscardHistory');
  }

  storeBeforeAndAfterBlobs(filePaths, isSafe, destructiveAction, partialDiscardFilePath = null) {
    return unsupportedOperationPromise(this, 'storeBeforeAndAfterBlobs');
  }

  restoreLastDiscardInTempFiles(isSafe, partialDiscardFilePath = null) {
    return unsupportedOperationPromise(this, 'restoreLastDiscardInTempFiles');
  }

  popDiscardHistory(partialDiscardFilePath = null) {
    return unsupportedOperationPromise(this, 'popDiscardHistory');
  }

  clearDiscardHistory(partialDiscardFilePath = null) {
    return unsupportedOperationPromise(this, 'clearDiscardHistory');
  }

  discardWorkDirChangesForPaths(paths) {
    return unsupportedOperationPromise(this, 'discardWorkDirChangesForPaths');
  }

  // Accessors /////////////////////////////////////////////////////////////////////////////////////////////////////////
  // When possible, these default to "empty" results when invoked in states that don't have information available, or
  // fail in a way that's consistent with the requested information not being found.

  // Index queries

  getStatusBundle() {
    return Promise.resolve({
      stagedFiles: {},
      unstagedFiles: {},
      mergeConflictFiles: {},
      branch: {
        oid: null,
        head: null,
        upstream: null,
        aheadBehind: {ahead: null, behind: null},
      },
    });
  }

  getStatusesForChangedFiles() {
    return Promise.resolve({
      stagedFiles: [],
      unstagedFiles: [],
      mergeConflictFiles: [],
    });
  }

  getFilePatchForPath(filePath, options = {}) {
    return Promise.resolve(new MultiFilePatch({}));
  }

  getStagedChangesPatch() {
    return Promise.resolve(new MultiFilePatch({}));
  }

  readFileFromIndex(filePath) {
    return Promise.reject(new Error(`fatal: Path ${filePath} does not exist (neither on disk nor in the index).`));
  }

  // Commit access

  getLastCommit() {
    return Promise.resolve(nullCommit);
  }

  getRecentCommits() {
    return Promise.resolve([]);
  }

  // Author information

  getAuthors() {
    return Promise.resolve([]);
  }

  // Branches

  getBranches() {
    return Promise.resolve(new BranchSet());
  }

  getHeadDescription() {
    return Promise.resolve('(no repository)');
  }

  // Merging and rebasing status

  isMerging() {
    return Promise.resolve(false);
  }

  isRebasing() {
    return Promise.resolve(false);
  }

  // Remotes

  getRemotes() {
    return Promise.resolve(new RemoteSet([]));
  }

  addRemote() {
    return unsupportedOperationPromise(this, 'addRemote');
  }

  getAheadCount(branchName) {
    return Promise.resolve(null);
  }

  getBehindCount(branchName) {
    return Promise.resolve(null);
  }

  getConfig(option, {local} = {}) {
    return Promise.resolve(null);
  }

  // Direct blob access

  getBlobContents(sha) {
    return Promise.reject(new Error(`fatal: Not a valid object name ${sha}`));
  }

  // Discard history

  hasDiscardHistory(partialDiscardFilePath = null) {
    return false;
  }

  getDiscardHistory(partialDiscardFilePath = null) {
    return [];
  }

  getLastHistorySnapshots(partialDiscardFilePath = null) {
    return null;
  }

  // Atom repo state

  getOperationStates() {
    return nullOperationStates;
  }

  setCommitMessage(message) {
    return unsupportedOperationPromise(this, 'setCommitMessage');
  }

  getCommitMessage() {
    return '';
  }

  fetchCommitMessageTemplate() {
    return unsupportedOperationPromise(this, 'fetchCommitMessageTemplate');
  }

  // Cache

  getCache() {
    return null;
  }

  // Internal //////////////////////////////////////////////////////////////////////////////////////////////////////////
  // Non-delegated methods that provide subclasses with convenient access to Repository properties.

  git() {
    return this.repository.git;
  }

  workdir() {
    return this.repository.getWorkingDirectoryPath();
  }

  // Call methods on the active Repository state, even if the state has transitioned beneath you.
  // Use this to perform operations within `start()` methods to guard against interrupted state transitions.
  current() {
    return this.repository.state;
  }

  // pipeline
  executePipelineAction(...args) {
    return this.repository.executePipelineAction(...args);
  }

  // Return a Promise that will resolve once the state transitions from Loading.
  getLoadPromise() {
    return this.repository.getLoadPromise();
  }

  getRemoteForBranch(branchName) {
    return this.repository.getRemoteForBranch(branchName);
  }

  saveDiscardHistory() {
    return this.repository.saveDiscardHistory();
  }

  // Initiate a transition to another state.
  transitionTo(stateName, ...payload) {
    const StateConstructor = stateConstructors.get(stateName);
    if (StateConstructor === undefined) {
      throw new Error(`Attempt to transition to unrecognized state ${stateName}`);
    }
    return this.repository.transition(this, StateConstructor, ...payload);
  }

  // Event broadcast

  didDestroy() {
    return this.repository.emitter.emit('did-destroy');
  }

  didUpdate() {
    return this.repository.emitter.emit('did-update');
  }

  // Direct git access
  // Non-delegated git operations for internal use within states.

  directResolveDotGitDir() {
    return Promise.resolve(null);
  }

  directGetConfig(key, options = {}) {
    return Promise.resolve(null);
  }

  directGetBlobContents() {
    return Promise.reject(new Error('Not a valid object name'));
  }

  directInit() {
    return Promise.resolve();
  }

  directClone(remoteUrl, options) {
    return Promise.resolve();
  }

  // Deferred operations
  // Direct raw git operations to the current state, even if the state has been changed. Use these methods within
  // start() methods.

  resolveDotGitDir() {
    return this.current().directResolveDotGitDir();
  }

  doInit(workdir) {
    return this.current().directInit();
  }

  doClone(remoteUrl, options) {
    return this.current().directClone(remoteUrl, options);
  }

  // Parse a DiscardHistory payload from the SHA recorded in config.
  async loadHistoryPayload() {
    const historySha = await this.current().directGetConfig('atomGithub.historySha');
    if (!historySha) {
      return {};
    }

    let blob;
    try {
      blob = await this.current().directGetBlobContents(historySha);
    } catch (e) {
      if (/Not a valid object name/.test(e.stdErr)) {
        return {};
      }

      throw e;
    }

    try {
      return JSON.parse(blob);
    } catch (e) {
      return {};
    }
  }

  // Debugging assistance.

  toString() {
    return this.constructor.name;
  }
}

function unsupportedOperationPromise(self, opName) {
  return Promise.reject(new Error(`${opName} is not available in ${self} state`));
}
<|endoftext|>


<commit_before>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

const glob2regexp = require("glob-to-regexp");
const { STAGE_DEFAULT } = require("../OptimizationStages");
const HarmonyExportImportedSpecifierDependency = require("../dependencies/HarmonyExportImportedSpecifierDependency");
const HarmonyImportSideEffectDependency = require("../dependencies/HarmonyImportSideEffectDependency");
const HarmonyImportSpecifierDependency = require("../dependencies/HarmonyImportSpecifierDependency");

/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Module")} Module */

/**
 * @typedef {Object} ExportInModule
 * @property {Module} module the module
 * @property {string} exportName the name of the export
 */

/** @type {WeakMap<any, Map<string, RegExp>>} */
const globToRegexpCache = new WeakMap();

/**
 * @param {string} glob the pattern
 * @param {Map<string, RegExp>} cache the glob to RegExp cache
 * @returns {RegExp} a regular expression
 */
const globToRegexp = (glob, cache) => {
	const cacheEntry = cache.get(glob);
	if (cacheEntry !== undefined) return cacheEntry;
	if (!glob.includes("/")) {
		glob = `**/${glob}`;
	}
	const baseRegexp = glob2regexp(glob, { globstar: true, extended: true });
	const regexpSource = baseRegexp.source;
	const regexp = new RegExp("^(\\./)?" + regexpSource.slice(1));
	cache.set(glob, regexp);
	return regexp;
};

class SideEffectsFlagPlugin {
	/**
	 * @param {Compiler} compiler webpack compiler
	 * @returns {void}
	 */
	apply(compiler) {
		let cache = globToRegexpCache.get(compiler.root);
		if (cache === undefined) {
			cache = new Map();
			globToRegexpCache.set(compiler.root, cache);
		}
		compiler.hooks.normalModuleFactory.tap("SideEffectsFlagPlugin", nmf => {
			nmf.hooks.module.tap("SideEffectsFlagPlugin", (module, data) => {
				const resolveData = data.resourceResolveData;
				if (
					resolveData &&
					resolveData.descriptionFileData &&
					resolveData.relativePath
				) {
					const sideEffects = resolveData.descriptionFileData.sideEffects;
					const hasSideEffects = SideEffectsFlagPlugin.moduleHasSideEffects(
						resolveData.relativePath,
						sideEffects,
						cache
					);
					if (!hasSideEffects) {
						module.factoryMeta.sideEffectFree = true;
					}
				}

				return module;
			});
			nmf.hooks.module.tap("SideEffectsFlagPlugin", (module, data) => {
				if (data.settings.sideEffects === false) {
					module.factoryMeta.sideEffectFree = true;
				} else if (data.settings.sideEffects === true) {
					module.factoryMeta.sideEffectFree = false;
				}
				return module;
			});
		});
		compiler.hooks.compilation.tap("SideEffectsFlagPlugin", compilation => {
			const moduleGraph = compilation.moduleGraph;
			compilation.hooks.optimizeDependencies.tap(
				{
					name: "SideEffectsFlagPlugin",
					stage: STAGE_DEFAULT
				},
				modules => {
					/** @type {Map<Module, Map<string, ExportInModule>>} */
					const reexportMaps = new Map();

					// Capture reexports of sideEffectFree modules
					for (const module of modules) {
						for (const dep of module.dependencies) {
							if (dep instanceof HarmonyExportImportedSpecifierDependency) {
								if (module.factoryMeta.sideEffectFree) {
									const mode = dep.getMode(moduleGraph, true);
									if (mode.type === "normal-reexport") {
										let map = reexportMaps.get(module);
										if (!map) {
											reexportMaps.set(module, (map = new Map()));
										}
										for (const [key, ids] of mode.map) {
											if (ids.length > 0 && !mode.checked.has(key)) {
												map.set(key, {
													module: moduleGraph.getModule(dep),
													exportName: ids[0]
												});
											}
										}
									}
								}
							} else if (dep instanceof HarmonyImportSideEffectDependency) {
								const connection = moduleGraph.getConnection(dep);
								if (connection) {
									const refModule = connection.resolvedModule;
									if (refModule && refModule.factoryMeta.sideEffectFree) {
										connection.active = false;
									}
								}
							}
						}
					}

					// Flatten reexports
					for (const map of reexportMaps.values()) {
						for (const pair of map) {
							let mapping = pair[1];
							while (mapping) {
								const innerMap = reexportMaps.get(mapping.module);
								if (!innerMap) break;
								const newMapping = innerMap.get(mapping.exportName);
								if (newMapping) {
									map.set(pair[0], newMapping);
								}
								mapping = newMapping;
							}
						}
					}

					// Update imports along the reexports from sideEffectFree modules
					for (const pair of reexportMaps) {
						const module = pair[0];
						const map = pair[1];
						for (const connection of moduleGraph.getIncomingConnections(
							module
						)) {
							const dep = connection.dependency;
							if (
								dep instanceof HarmonyExportImportedSpecifierDependency ||
								(dep instanceof HarmonyImportSpecifierDependency &&
									!dep.namespaceObjectAsContext)
							) {
								// TODO improve for nested imports
								const ids = dep.getIds(moduleGraph);
								if (ids.length > 0) {
									const mapping = map.get(ids[0]);
									if (mapping) {
										moduleGraph.updateModule(dep, mapping.module);
										moduleGraph.addExplanation(
											dep,
											"(skipped side-effect-free modules)"
										);
										dep.setIds(
											moduleGraph,
											mapping.exportName
												? [mapping.exportName, ...ids.slice(1)]
												: ids.slice(1)
										);
										continue;
									}
								}
							}
						}
					}
				}
			);
		});
	}

	static moduleHasSideEffects(moduleName, flagValue, cache) {
		switch (typeof flagValue) {
			case "undefined":
				return true;
			case "boolean":
				return flagValue;
			case "string":
				return globToRegexp(flagValue, cache).test(moduleName);
			case "object":
				return flagValue.some(glob =>
					SideEffectsFlagPlugin.moduleHasSideEffects(moduleName, glob, cache)
				);
		}
	}
}
module.exports = SideEffectsFlagPlugin;
<commit_msg>add comment, optimize code<commit_after>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

const glob2regexp = require("glob-to-regexp");
const { STAGE_DEFAULT } = require("../OptimizationStages");
const HarmonyExportImportedSpecifierDependency = require("../dependencies/HarmonyExportImportedSpecifierDependency");
const HarmonyImportSideEffectDependency = require("../dependencies/HarmonyImportSideEffectDependency");
const HarmonyImportSpecifierDependency = require("../dependencies/HarmonyImportSpecifierDependency");

/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Module")} Module */

/**
 * @typedef {Object} ExportInModule
 * @property {Module} module the module
 * @property {string} exportName the name of the export
 */

/** @type {WeakMap<any, Map<string, RegExp>>} */
const globToRegexpCache = new WeakMap();

/**
 * @param {string} glob the pattern
 * @param {Map<string, RegExp>} cache the glob to RegExp cache
 * @returns {RegExp} a regular expression
 */
const globToRegexp = (glob, cache) => {
	const cacheEntry = cache.get(glob);
	if (cacheEntry !== undefined) return cacheEntry;
	if (!glob.includes("/")) {
		glob = `**/${glob}`;
	}
	const baseRegexp = glob2regexp(glob, { globstar: true, extended: true });
	const regexpSource = baseRegexp.source;
	const regexp = new RegExp("^(\\./)?" + regexpSource.slice(1));
	cache.set(glob, regexp);
	return regexp;
};

class SideEffectsFlagPlugin {
	/**
	 * @param {Compiler} compiler webpack compiler
	 * @returns {void}
	 */
	apply(compiler) {
		let cache = globToRegexpCache.get(compiler.root);
		if (cache === undefined) {
			cache = new Map();
			globToRegexpCache.set(compiler.root, cache);
		}
		compiler.hooks.normalModuleFactory.tap("SideEffectsFlagPlugin", nmf => {
			nmf.hooks.module.tap("SideEffectsFlagPlugin", (module, data) => {
				const resolveData = data.resourceResolveData;
				if (
					resolveData &&
					resolveData.descriptionFileData &&
					resolveData.relativePath
				) {
					const sideEffects = resolveData.descriptionFileData.sideEffects;
					const hasSideEffects = SideEffectsFlagPlugin.moduleHasSideEffects(
						resolveData.relativePath,
						sideEffects,
						cache
					);
					if (!hasSideEffects) {
						module.factoryMeta.sideEffectFree = true;
					}
				}

				return module;
			});
			nmf.hooks.module.tap("SideEffectsFlagPlugin", (module, data) => {
				if (data.settings.sideEffects === false) {
					module.factoryMeta.sideEffectFree = true;
				} else if (data.settings.sideEffects === true) {
					module.factoryMeta.sideEffectFree = false;
				}
				return module;
			});
		});
		compiler.hooks.compilation.tap("SideEffectsFlagPlugin", compilation => {
			const moduleGraph = compilation.moduleGraph;
			compilation.hooks.optimizeDependencies.tap(
				{
					name: "SideEffectsFlagPlugin",
					stage: STAGE_DEFAULT
				},
				modules => {
					/** @type {Map<Module, Map<string, ExportInModule>>} */
					const reexportMaps = new Map();

					// Capture reexports of sideEffectFree modules
					for (const module of modules) {
						for (const dep of module.dependencies) {
							if (dep instanceof HarmonyExportImportedSpecifierDependency) {
								if (module.factoryMeta.sideEffectFree) {
									const mode = dep.getMode(moduleGraph, true);
									if (mode.type === "normal-reexport") {
										let map = reexportMaps.get(module);
										if (!map) {
											reexportMaps.set(module, (map = new Map()));
										}
										for (const [key, ids] of mode.map) {
											// TODO Support reexporting namespace object
											if (ids.length > 0 && !mode.checked.has(key)) {
												map.set(key, {
													module: moduleGraph.getModule(dep),
													exportName: ids[0]
												});
											}
										}
									}
								}
							} else if (dep instanceof HarmonyImportSideEffectDependency) {
								const connection = moduleGraph.getConnection(dep);
								if (connection) {
									const refModule = connection.resolvedModule;
									if (refModule && refModule.factoryMeta.sideEffectFree) {
										connection.active = false;
									}
								}
							}
						}
					}

					// Flatten reexports
					for (const map of reexportMaps.values()) {
						for (const pair of map) {
							let mapping = pair[1];
							while (mapping) {
								const innerMap = reexportMaps.get(mapping.module);
								if (!innerMap) break;
								const newMapping = innerMap.get(mapping.exportName);
								if (newMapping) {
									map.set(pair[0], newMapping);
								}
								mapping = newMapping;
							}
						}
					}

					// Update imports along the reexports from sideEffectFree modules
					for (const [module, map] of reexportMaps) {
						for (const connection of moduleGraph.getIncomingConnections(
							module
						)) {
							const dep = connection.dependency;
							if (
								dep instanceof HarmonyExportImportedSpecifierDependency ||
								(dep instanceof HarmonyImportSpecifierDependency &&
									!dep.namespaceObjectAsContext)
							) {
								// TODO improve for nested imports
								const ids = dep.getIds(moduleGraph);
								if (ids.length > 0) {
									const mapping = map.get(ids[0]);
									if (mapping) {
										moduleGraph.updateModule(dep, mapping.module);
										moduleGraph.addExplanation(
											dep,
											"(skipped side-effect-free modules)"
										);
										dep.setIds(
											moduleGraph,
											mapping.exportName
												? [mapping.exportName, ...ids.slice(1)]
												: ids.slice(1)
										);
										continue;
									}
								}
							}
						}
					}
				}
			);
		});
	}

	static moduleHasSideEffects(moduleName, flagValue, cache) {
		switch (typeof flagValue) {
			case "undefined":
				return true;
			case "boolean":
				return flagValue;
			case "string":
				return globToRegexp(flagValue, cache).test(moduleName);
			case "object":
				return flagValue.some(glob =>
					SideEffectsFlagPlugin.moduleHasSideEffects(moduleName, glob, cache)
				);
		}
	}
}
module.exports = SideEffectsFlagPlugin;
<|endoftext|>