<commit_before>define(['player', 'utils', 'terrariaserver', 'net', 'config', 'packettypes', 'underscore'], function(Player, Utils, TerrariaServer, net, Config, PacketTypes, _) {
  var Client = Class.extend({
    init: function(id, socket, server, serverCounts, globalHandlers, servers) {
      this.ID = id;

      // TerrariaServer information available for connecting to
      this.servers = servers;

      // The socket connection to the net server associated with this client
      this.socket = socket;

      // The unformatted ip address for the current socket connection to the net server
      this.ip = socket.remoteAddress;

      // This clients player object which can be used
      // for storing inventory and other player information
      this.player = new Player();

      // Global Handlers object whose contents may be updated (reloaded/refreshed)
      this.globalHandlers = globalHandlers;

      // TerrariaServer socket connection and packet handler
      this.server = new TerrariaServer(null, this);
      this.server.ip = server.serverIP;
      this.server.port = server.serverPort;
      this.server.name = server.name;

      // Current connection state to TerrariaServer
      this.connected = false;

      // Connection State
      // 0 => Fresh Connection
      // 1 => Finished Sending Inventory / Completed Server switch
      // 2 => Connection to new server established (extra packet help required because of the actual clients state
      //      being incapable of sending certain packets)
      // 3 => Packet Help sent  Get Section/Request Sync [8] packet in response to world info [7], now waiting on Update Shield Strengths [101]
      this.state = 0;

      // Incomplete packet from last data received. This is used because all packets are inspected
      this.bufferPacket = "";

      // This is used to make the first connection to a TerrariaServer after receiving data
      this.initialConnectionAlreadyCreated = false;

      // A boolean of whether the current client has made it in-game (they can see minimap, world, tiles, their inventory)
      this.ingame = false;

      // UUID of client
      this.UUID = "";

      // A boolean indicating that the socket was closed because the client was booted from the TerrariaServers
      // This is set to false again after the close handler has been run
      this.wasKicked = false;

      // Information to the server about a type of join (gamemode)
      this.routingInformation = null;

      // Whether or not count was incremented
      // this will be turned off when we minus from count
      this.countIncremented = false;

      // The counts of all TerrariaServers available
      this.serverCounts = serverCounts;

      this.preventSpawnOnJoin = false;

      this.ServerHandleError = this.server.handleError.bind(this.server);
      this.ServerHandleData = this.server.handleData.bind(this.server);
      this.ServerHandleClose = this.server.handleClose.bind(this.server);
    },

    setName: function(name) {
      this.player.name = name;
    },

    getName: function() {
      return this.player.name;
    },

    handleDataSend: function(encodedData) {
      try {
        var self = this;

        var incompleteData = Utils.hex2str(encodedData);
        //console.log(entireData);

        // Add Buffer Packet (incomplete packet from last data)
        // to the new data
        var bufferPacket = this.bufferPacket;
        var entireData = bufferPacket + incompleteData;

        // Get the individual packets from the data
        var entireDataInfo = Utils.getPacketsFromHexString(entireData);

        // Update Buffer Packet using the new incomplete packet (if any)
        this.bufferPacket = entireDataInfo.bufferPacket;

        var packets = entireDataInfo.packets;

        // The packets are only handled if the client has already connected
        // to a server for the first time
        if (this.initialConnectionAlreadyCreated) {
          var allowedData = "";
          _.each(packets, function(packet) {
            allowedData += self.handlePacket(packet);
          });

          // Send allowedData to the server if the client is connected to one
          if (allowedData.length > 0 && this.connected) {
            if (this.server.socket) {
              this.server.socket.write(new Buffer(allowedData, 'hex'));
            }
            else {
              this.sendChatMessage("Are you even connected?", "ff0000");
            }
          }
        }
        else {
          // Connect to the server for the first time
          this.initialConnectionAlreadyCreated = true;
          this.server.socket = new net.Socket();

          this.server.socket.connect(self.server.port, self.server.ip, function() {
            self.countIncremented = true;
            self.serverCounts[self.server.name]++;
            self.connected = true;

            // Write the data the client sent us to the now connected server
            self.server.socket.write(encodedData);
          });

          this.server.socket.on('data', this.ServerHandleData);
          this.server.socket.on('close', this.ServerHandleClose);
          this.server.socket.on('error', this.ServerHandleError);
        }
      }
      catch (e) {
        console.log("Client Handle Send Data Error: " + e);
      }
    },

    handlePacket: function(packet) {
      var self = this;
      var packetType = packet.packetType;
      var handled = false;

      switch (packetType) {
        case PacketTypes.PlayerInfo:
          handled = self.handlePlayerInfo(packet);
          break;

          // Either will be sent, but not both
        case PacketTypes.ContinueConnecting2:
        case PacketTypes.Status:
          if (self.state === 0) {
            // Finished sending inventory
            self.state = 1;
          }
          break;

        case PacketTypes.UpdateItemOwner:
          // Prevent this being sent unless state is 1
          // this prevents issues with joining while items
          // are next to the player on the past server
          if (self.state !== 1) {
            handled = true;
          }
          break;

        case PacketTypes.ChatMessage:
          handled = self.handleChatMessage(packet);
          break;

        case PacketTypes.DimensionsUpdate:
          // Client cannot send 67 (It's used by Dimensions to communicate special info)
          handled = true;
          break;

        case PacketTypes.ClientUUID:
          handled = self.handleClientUUID(packet);
          break;
      }

      if (!handled) {
        return packet.data;
      }
    },

    /* Start Packet Handlers */
    handlePlayerInfo: function(packet) {
      var self = this;
      var nameLength = parseInt(packet.data.substr(12, 2), 16);
      if (self.name === null) {
        // Take the appropriate hex chars out of the packet
        // then convert them to ascii
        var name = Utils.hex2a(packet.data.substr(14, nameLength * 2));
        self.setName(name);
      }

      return false;
    },

    handleChatMessage: function(packet) {
      var self = this;
      var handled = false;
      var chatMessage = Utils.hex2a(packet.data.substr(16));

      // If chat message is a commandcommand
      if (chatMessage.length > 1 && chatMessage.substr(0, 1) === "/") {
        var command = self.globalHandlers.command.parseCommand(chatMessage);
        handled = self.globalHandlers.command.handle(command.name.toLowerCase(), command.args, self);
      }

      return handled;
    },

    handleClientUUID: function(packet) {
      var self = this;
      var reader = Utils.ReadPacketFactory(packet.data);
      self.clientUUID = reader.readString();

      return false;
    },

    /* End Packet Handlers */

    sendChatMessage: function(message, color) {
      if (message.length > 0) {
        // Set default color to green if no color specified
        if (typeof color === "undefined") {
          color = "00ff00";
        }

        var packetData = Utils.PacketFactory()
          .setType(PacketTypes.ChatMessage)
          .packByte(255)
          .packHex(color)
          .packString(message)
          .data();
        var msg = new Buffer(packetData, 'hex');
        this.socket.write(msg);
      }
    },

    changeServer: function(server, options) {
      var self = this;

      var ip = server.serverIP;
      var port = server.serverPort;
      var name = server.name;

      if (typeof options !== 'undefined' && options.preventSpawnOnJoin !== 'undefined') {
        self.preventSpawnOnJoin = options.preventSpawnOnJoin;
      }
      else {
        self.preventSpawnOnJoin = false;
      }

      // Client is now not connected to a server
      this.connected = false;

      // Remove data and error listeners on TerrariaServer socket
      self.server.socket.removeListener('data', self.ServerHandleData);
      self.server.socket.removeListener('error', self.ServerHandleError);

      self.server.afterClosed = function(self) {
        self.server.afterClosed = null;
        // Remove close listener now that socket has been closed and event was called
        self.server.socket.removeListener('close', self.ServerHandleClose);

        // Start new socket
        self.server.socket = new net.Socket();

        //console.log("Connecting to " + ip + ":" + port);

        // Update server information
        self.server.ip = ip;
        self.server.port = port;
        self.server.name = name;

        // Create connection
        self.server.socket.connect(parseInt(port), ip, function() {
          // Increment server count
          self.countIncremented = true;
          self.serverCounts[self.server.name]++;

          // Send Packet 1
          // This needs to be changed; it should not be hardcoded data
          self.server.socket.write(new Buffer("0f00010b5465727261726961313639", "hex"));
          if (typeof options !== 'undefined' && typeof options.routingInformation !== 'undefined') {
            self.routingInformation = options.routingInformation;
          }
          self.state = 2;
          self.connected = true;
        });

        self.server.socket.on('data', self.ServerHandleData);
        self.server.socket.on('close', self.ServerHandleClose);
        self.server.socket.on('error', self.ServerHandleError);
      };

      // Close the TerrariaServer socket completely
      if (!self.server.socket.destroyed) {
        self.server.socket.destroy();
      }
      else {
        self.server.afterClosed(self);
      }
    },

    tellSelfToClearPlayers: function() {
      var packet;
      var playerID, firstByte, secondByte, packetLength, prePacketLength;
      for (var i = 0; i < 255; i++) {
        if (i === this.player.id)
          continue;

        playerID = (i).toString(16);
        if (playerID.length % 2 !== 0) {
          playerID = "0" + playerID;
        }

        prePacketLength = ((playerID.length + 4) / 2).toString(16);
        if (prePacketLength.length !== 4) {
          for (var j = prePacketLength.length; j < 4; j++) {
            prePacketLength = "0" + prePacketLength;
          }
        }

        // Assign hex packet length
        packetLength = (prePacketLength.length / 2 + parseInt(prePacketLength, 16)).toString(16);

        // Ensure it takes up 4 hex digits
        if (packetLength.length !== 4) {
          for (var j = packetLength.length; j < 4; j++) {
            packetLength = "0" + packetLength;
          }
        }

        // Reverse byte order
        firstByte = packetLength.substr(0, 2);
        secondByte = packetLength.substr(2, 2);
        packetLength = secondByte + firstByte + packetLength.substr(4);

        packet = packetLength + "0e" + playerID + "00";
        this.socket.write(new Buffer(packet, 'hex'));
      }
    },

    tellSelfToClearNPCs: function() {
      var packet;
      var playerID, firstByte, secondByte, packetLength, prePacketLength;
      for (var i = 0; i < 255; i++) {
        if (i === this.player.id)
          continue;

        playerID = (i).toString(16);
        if (playerID.length % 2 !== 0) {
          playerID = "0" + playerID;
        }

        prePacketLength = ((playerID.length + 4) / 2).toString(16);
        if (prePacketLength.length !== 4) {
          for (var j = prePacketLength.length; j < 4; j++) {
            prePacketLength = "0" + prePacketLength;
          }
        }

        // Assign hex packet length
        packetLength = (prePacketLength.length / 2 + parseInt(prePacketLength, 16)).toString(16);

        // Ensure it takes up 4 hex digits
        if (packetLength.length !== 4) {
          for (var j = packetLength.length; j < 4; j++) {
            packetLength = "0" + packetLength;
          }
        }

        // Reverse byte order
        firstByte = packetLength.substr(0, 2);
        secondByte = packetLength.substr(2, 2);
        packetLength = secondByte + firstByte + packetLength.substr(4);

        packet = packetLength + "0e" + playerID + "00";
        this.socket.write(new Buffer(packet, 'hex'));
      }
    },

    tellSelfToClearItems: function() {
      var packet;
      var playerID, firstByte, secondByte, packetLength, prePacketLength;
      for (var i = 0; i < 400; i++) {
        itemID = (i).toString(16);
        if (itemID.length % 2 !== 0) {
          itemID = "0" + itemID;
        }

        prePacketLength = ((playerID.length + 4) / 2).toString(16);
        if (prePacketLength.length !== 4) {
          for (var j = prePacketLength.length; j < 4; j++) {
            prePacketLength = "0" + prePacketLength;
          }
        }

        // Assign hex packet length
        packetLength = (prePacketLength.length / 2 + parseInt(prePacketLength, 16)).toString(16);

        // Ensure it takes up 4 hex digits
        if (packetLength.length !== 4) {
          for (var j = packetLength.length; j < 4; j++) {
            packetLength = "0" + packetLength;
          }
        }

        // Reverse byte order
        firstByte = packetLength.substr(0, 2);
        secondByte = packetLength.substr(2, 2);
        packetLength = secondByte + firstByte + packetLength.substr(4);

        packet = packetLength + "0e" + playerID + "00";
        this.socket.write(new Buffer(packet, 'hex'));
      }
    },

    handleError: function(err) {
      //console.log("Client Socket Error: " + err);
    },

    handleClose: function() {
      //console.log("Client Socket Closed.");
      if (this.server.socket && typeof this.server.socket.destroy === 'function') {
        this.server.socket.destroy();
        this.server.socket.handleClose = function() {};
      }
    },
  });

  return Client;
});
<commit_msg>Fix handlePacket not returning<commit_after>define(['player', 'utils', 'terrariaserver', 'net', 'config', 'packettypes', 'underscore'], function(Player, Utils, TerrariaServer, net, Config, PacketTypes, _) {
  var Client = Class.extend({
    init: function(id, socket, server, serverCounts, globalHandlers, servers) {
      this.ID = id;

      // TerrariaServer information available for connecting to
      this.servers = servers;

      // The socket connection to the net server associated with this client
      this.socket = socket;

      // The unformatted ip address for the current socket connection to the net server
      this.ip = socket.remoteAddress;

      // This clients player object which can be used
      // for storing inventory and other player information
      this.player = new Player();

      // Global Handlers object whose contents may be updated (reloaded/refreshed)
      this.globalHandlers = globalHandlers;

      // TerrariaServer socket connection and packet handler
      this.server = new TerrariaServer(null, this);
      this.server.ip = server.serverIP;
      this.server.port = server.serverPort;
      this.server.name = server.name;

      // Current connection state to TerrariaServer
      this.connected = false;

      // Connection State
      // 0 => Fresh Connection
      // 1 => Finished Sending Inventory / Completed Server switch
      // 2 => Connection to new server established (extra packet help required because of the actual clients state
      //      being incapable of sending certain packets)
      // 3 => Packet Help sent  Get Section/Request Sync [8] packet in response to world info [7], now waiting on Update Shield Strengths [101]
      this.state = 0;

      // Incomplete packet from last data received. This is used because all packets are inspected
      this.bufferPacket = "";

      // This is used to make the first connection to a TerrariaServer after receiving data
      this.initialConnectionAlreadyCreated = false;

      // A boolean of whether the current client has made it in-game (they can see minimap, world, tiles, their inventory)
      this.ingame = false;

      // UUID of client
      this.UUID = "";

      // A boolean indicating that the socket was closed because the client was booted from the TerrariaServers
      // This is set to false again after the close handler has been run
      this.wasKicked = false;

      // Information to the server about a type of join (gamemode)
      this.routingInformation = null;

      // Whether or not count was incremented
      // this will be turned off when we minus from count
      this.countIncremented = false;

      // The counts of all TerrariaServers available
      this.serverCounts = serverCounts;

      this.preventSpawnOnJoin = false;

      this.ServerHandleError = this.server.handleError.bind(this.server);
      this.ServerHandleData = this.server.handleData.bind(this.server);
      this.ServerHandleClose = this.server.handleClose.bind(this.server);
    },

    setName: function(name) {
      this.player.name = name;
    },

    getName: function() {
      return this.player.name;
    },

    handleDataSend: function(encodedData) {
      try {
        var self = this;

        var incompleteData = Utils.hex2str(encodedData);
        //console.log(entireData);

        // Add Buffer Packet (incomplete packet from last data)
        // to the new data
        var bufferPacket = this.bufferPacket;
        var entireData = bufferPacket + incompleteData;

        // Get the individual packets from the data
        var entireDataInfo = Utils.getPacketsFromHexString(entireData);

        // Update Buffer Packet using the new incomplete packet (if any)
        this.bufferPacket = entireDataInfo.bufferPacket;

        var packets = entireDataInfo.packets;

        // The packets are only handled if the client has already connected
        // to a server for the first time
        if (this.initialConnectionAlreadyCreated) {
          var allowedData = "";
          _.each(packets, function(packet) {
            allowedData += self.handlePacket(packet);
          });

          // Send allowedData to the server if the client is connected to one
          if (allowedData.length > 0 && this.connected) {
            if (this.server.socket) {
              this.server.socket.write(new Buffer(allowedData, 'hex'));
            }
            else {
              this.sendChatMessage("Are you even connected?", "ff0000");
            }
          }
        }
        else {
          // Connect to the server for the first time
          this.initialConnectionAlreadyCreated = true;
          this.server.socket = new net.Socket();

          this.server.socket.connect(self.server.port, self.server.ip, function() {
            self.countIncremented = true;
            self.serverCounts[self.server.name]++;
            self.connected = true;

            // Write the data the client sent us to the now connected server
            self.server.socket.write(encodedData);
          });

          this.server.socket.on('data', this.ServerHandleData);
          this.server.socket.on('close', this.ServerHandleClose);
          this.server.socket.on('error', this.ServerHandleError);
        }
      }
      catch (e) {
        console.log("Client Handle Send Data Error: " + e);
      }
    },

    handlePacket: function(packet) {
      var self = this;
      var packetType = packet.packetType;
      var handled = false;

      switch (packetType) {
        case PacketTypes.PlayerInfo:
          handled = self.handlePlayerInfo(packet);
          break;

          // Either will be sent, but not both
        case PacketTypes.ContinueConnecting2:
        case PacketTypes.Status:
          if (self.state === 0) {
            // Finished sending inventory
            self.state = 1;
          }
          break;

        case PacketTypes.UpdateItemOwner:
          // Prevent this being sent unless state is 1
          // this prevents issues with joining while items
          // are next to the player on the past server
          if (self.state !== 1) {
            handled = true;
          }
          break;

        case PacketTypes.ChatMessage:
          handled = self.handleChatMessage(packet);
          break;

        case PacketTypes.DimensionsUpdate:
          // Client cannot send 67 (It's used by Dimensions to communicate special info)
          handled = true;
          break;

        case PacketTypes.ClientUUID:
          handled = self.handleClientUUID(packet);
          break;
      }

      return !handled ? packet.data : "";
    },

    /* Start Packet Handlers */
    handlePlayerInfo: function(packet) {
      var self = this;
      var nameLength = parseInt(packet.data.substr(12, 2), 16);
      if (self.name === null) {
        // Take the appropriate hex chars out of the packet
        // then convert them to ascii
        var name = Utils.hex2a(packet.data.substr(14, nameLength * 2));
        self.setName(name);
      }

      return false;
    },

    handleChatMessage: function(packet) {
      var self = this;
      var handled = false;
      var chatMessage = Utils.hex2a(packet.data.substr(16));

      // If chat message is a commandcommand
      if (chatMessage.length > 1 && chatMessage.substr(0, 1) === "/") {
        var command = self.globalHandlers.command.parseCommand(chatMessage);
        handled = self.globalHandlers.command.handle(command.name.toLowerCase(), command.args, self);
      }

      return handled;
    },

    handleClientUUID: function(packet) {
      var self = this;
      var reader = Utils.ReadPacketFactory(packet.data);
      self.clientUUID = reader.readString();

      return false;
    },

    /* End Packet Handlers */

    sendChatMessage: function(message, color) {
      if (message.length > 0) {
        // Set default color to green if no color specified
        if (typeof color === "undefined") {
          color = "00ff00";
        }

        var packetData = Utils.PacketFactory()
          .setType(PacketTypes.ChatMessage)
          .packByte(255)
          .packHex(color)
          .packString(message)
          .data();
        var msg = new Buffer(packetData, 'hex');
        this.socket.write(msg);
      }
    },

    changeServer: function(server, options) {
      var self = this;

      var ip = server.serverIP;
      var port = server.serverPort;
      var name = server.name;

      if (typeof options !== 'undefined' && options.preventSpawnOnJoin !== 'undefined') {
        self.preventSpawnOnJoin = options.preventSpawnOnJoin;
      }
      else {
        self.preventSpawnOnJoin = false;
      }

      // Client is now not connected to a server
      this.connected = false;

      // Remove data and error listeners on TerrariaServer socket
      self.server.socket.removeListener('data', self.ServerHandleData);
      self.server.socket.removeListener('error', self.ServerHandleError);

      self.server.afterClosed = function(self) {
        self.server.afterClosed = null;
        // Remove close listener now that socket has been closed and event was called
        self.server.socket.removeListener('close', self.ServerHandleClose);

        // Start new socket
        self.server.socket = new net.Socket();

        //console.log("Connecting to " + ip + ":" + port);

        // Update server information
        self.server.ip = ip;
        self.server.port = port;
        self.server.name = name;

        // Create connection
        self.server.socket.connect(parseInt(port), ip, function() {
          // Increment server count
          self.countIncremented = true;
          self.serverCounts[self.server.name]++;

          // Send Packet 1
          // This needs to be changed; it should not be hardcoded data
          self.server.socket.write(new Buffer("0f00010b5465727261726961313639", "hex"));
          if (typeof options !== 'undefined' && typeof options.routingInformation !== 'undefined') {
            self.routingInformation = options.routingInformation;
          }
          self.state = 2;
          self.connected = true;
        });

        self.server.socket.on('data', self.ServerHandleData);
        self.server.socket.on('close', self.ServerHandleClose);
        self.server.socket.on('error', self.ServerHandleError);
      };

      // Close the TerrariaServer socket completely
      if (!self.server.socket.destroyed) {
        self.server.socket.destroy();
      }
      else {
        self.server.afterClosed(self);
      }
    },

    tellSelfToClearPlayers: function() {
      var packet;
      var playerID, firstByte, secondByte, packetLength, prePacketLength;
      for (var i = 0; i < 255; i++) {
        if (i === this.player.id)
          continue;

        playerID = (i).toString(16);
        if (playerID.length % 2 !== 0) {
          playerID = "0" + playerID;
        }

        prePacketLength = ((playerID.length + 4) / 2).toString(16);
        if (prePacketLength.length !== 4) {
          for (var j = prePacketLength.length; j < 4; j++) {
            prePacketLength = "0" + prePacketLength;
          }
        }

        // Assign hex packet length
        packetLength = (prePacketLength.length / 2 + parseInt(prePacketLength, 16)).toString(16);

        // Ensure it takes up 4 hex digits
        if (packetLength.length !== 4) {
          for (var j = packetLength.length; j < 4; j++) {
            packetLength = "0" + packetLength;
          }
        }

        // Reverse byte order
        firstByte = packetLength.substr(0, 2);
        secondByte = packetLength.substr(2, 2);
        packetLength = secondByte + firstByte + packetLength.substr(4);

        packet = packetLength + "0e" + playerID + "00";
        this.socket.write(new Buffer(packet, 'hex'));
      }
    },

    tellSelfToClearNPCs: function() {
      var packet;
      var playerID, firstByte, secondByte, packetLength, prePacketLength;
      for (var i = 0; i < 255; i++) {
        if (i === this.player.id)
          continue;

        playerID = (i).toString(16);
        if (playerID.length % 2 !== 0) {
          playerID = "0" + playerID;
        }

        prePacketLength = ((playerID.length + 4) / 2).toString(16);
        if (prePacketLength.length !== 4) {
          for (var j = prePacketLength.length; j < 4; j++) {
            prePacketLength = "0" + prePacketLength;
          }
        }

        // Assign hex packet length
        packetLength = (prePacketLength.length / 2 + parseInt(prePacketLength, 16)).toString(16);

        // Ensure it takes up 4 hex digits
        if (packetLength.length !== 4) {
          for (var j = packetLength.length; j < 4; j++) {
            packetLength = "0" + packetLength;
          }
        }

        // Reverse byte order
        firstByte = packetLength.substr(0, 2);
        secondByte = packetLength.substr(2, 2);
        packetLength = secondByte + firstByte + packetLength.substr(4);

        packet = packetLength + "0e" + playerID + "00";
        this.socket.write(new Buffer(packet, 'hex'));
      }
    },

    tellSelfToClearItems: function() {
      var packet;
      var playerID, firstByte, secondByte, packetLength, prePacketLength;
      for (var i = 0; i < 400; i++) {
        itemID = (i).toString(16);
        if (itemID.length % 2 !== 0) {
          itemID = "0" + itemID;
        }

        prePacketLength = ((playerID.length + 4) / 2).toString(16);
        if (prePacketLength.length !== 4) {
          for (var j = prePacketLength.length; j < 4; j++) {
            prePacketLength = "0" + prePacketLength;
          }
        }

        // Assign hex packet length
        packetLength = (prePacketLength.length / 2 + parseInt(prePacketLength, 16)).toString(16);

        // Ensure it takes up 4 hex digits
        if (packetLength.length !== 4) {
          for (var j = packetLength.length; j < 4; j++) {
            packetLength = "0" + packetLength;
          }
        }

        // Reverse byte order
        firstByte = packetLength.substr(0, 2);
        secondByte = packetLength.substr(2, 2);
        packetLength = secondByte + firstByte + packetLength.substr(4);

        packet = packetLength + "0e" + playerID + "00";
        this.socket.write(new Buffer(packet, 'hex'));
      }
    },

    handleError: function(err) {
      //console.log("Client Socket Error: " + err);
    },

    handleClose: function() {
      //console.log("Client Socket Closed.");
      if (this.server.socket && typeof this.server.socket.destroy === 'function') {
        this.server.socket.destroy();
        this.server.socket.handleClose = function() {};
      }
    },
  });

  return Client;
});
<|endoftext|>


<commit_before>import R from 'ramda';
import makeCard from '../makeCard/makeCard';

const join = R.join('\n');

const ankifySenseData = R.curry((makeCard, { headword, pronunciation }, senseData) => {
  const { definition, situation, synonym, antonym, examples, exampleGroups } = senseData;

  const ankifyExample = R.pipe(
    R.objOf('example'),
    R.merge({ situation, form: headword, pronunciation, definition, synonym, antonym }),
    makeCard
  );

  const ankifyExampleGroup = R.pipe(
    R.merge({ situation, pronunciation, definition, synonym, antonym }),
    makeCard
  );

  const cardsFromExamples = examples
    ? R.pipe(R.map(ankifyExample), join)(examples)
    : '';

  const cardsFromExampleGroups = exampleGroups
    ? R.pipe(R.map(ankifyExampleGroup), join)(exampleGroups)
    : '';

  const cards = R.pipe(
    R.reject(R.isEmpty),
    join
  )([cardsFromExamples, cardsFromExampleGroups]);

  return cards;
});

export {ankifySenseData};
export default ankifySenseData(makeCard);
<commit_msg>feature: ankifySenseData now create cards for subsenses<commit_after>import R from 'ramda';
import makeCard from '../makeCard/makeCard';

const join = R.join('\n');

const ankifySenseData = R.curry((makeCard, { headword, pronunciation }, senseData) => {
  const { definition, situation, synonym, antonym, examples, exampleGroups, subsenses } = senseData;

  const ankifyExample = R.pipe(
    R.objOf('example'),
    R.merge({ situation, form: headword, pronunciation, definition, synonym, antonym }),
    makeCard
  );

  const ankifyExampleGroup = R.pipe(
    R.merge({ situation, pronunciation, definition, synonym, antonym }),
    makeCard
  );

  const cardsFromExamples = examples
    ? R.pipe(R.map(ankifyExample), join)(examples)
    : '';

  const cardsFromExampleGroups = exampleGroups
    ? R.pipe(R.map(ankifyExampleGroup), join)(exampleGroups)
    : '';

  const cardsFromSubsenses = subsenses
    ? R.pipe(R.map(ankifySenseData(makeCard, { headword, pronunciation })), join)(subsenses)
    : '';

  const cards = R.pipe(
    R.reject(R.isEmpty),
    join
  )([cardsFromExamples, cardsFromExampleGroups, cardsFromSubsenses]);

  return cards;
});

export {ankifySenseData};
export default ankifySenseData(makeCard);
<|endoftext|>


<commit_before>var config = require('../config/settings');
var async = require('async');
var mysql = require('mysql');
var connection = mysql.createConnection(config.db.connection);
var weather = require('./weather');
var weatherMapper = require('./weatherMapper');

module.exports = function (app, passport) {
  app.get('/', isLoggedIn, function (req, res) {
    res.render('index.ejs', {
      user: req.user
    });
  });

  app.get('/ajax/places', function (req, res, next) {
    connection.query('SELECT * FROM ' + config.db.tables.places, function (err, rows) {
      if (err) {
        res.status(500).send('Something broke!');

        return next();
      }

      res.json(rows);
    });
  });

  app.post('/ajax/places/filter', function (req, res, next) {
    connection.query('SELECT id, name, latitude, longitude, (? * acos(cos(radians(?)) * cos(radians(latitude)) * cos(radians(longitude) - radians(?)) + sin(radians(?)) * sin(radians(latitude)))) AS distance FROM ' + config.db.tables.places + ' HAVING distance < ?', [
      req.body.distance === 'mi' ? 3959 : 6371, req.body.latitude, req.body.longitude, req.body.latitude, req.body.radius
    ], function (err, rows) {
      if (err) {
        res.status(500).send('Something broke!');

        return next();
      }

      res.json(rows);
    });
  });

  app.post('/ajax/places/add', isLoggedIn, function (req, res, next) {
    if (req.user.id !== 1) {
      res.status(403).send('Forbidden!');

      return next();
    }

    connection.query('INSERT INTO ' + config.db.tables.places + ' VALUES (NULL, ?, ?, ?, ?)', [
      req.body.name, req.body.latitude, req.body.longitude, req.body.description || null
    ],
      function (err, rows) {
        if (err) {
          res.status(500).send('Something broke!');

          return next();
        }

        res.json(rows.insertId);
      });
  });

  app.get('/ajax/places/:id', isLoggedIn, function (req, res, next) {
    var id = req.params.id;

    connection.query('SELECT * FROM ' + config.db.tables.places + ' WHERE id = ?', [id], function (err, rows) {
      if (err) {
        res.status(500).send('Something broke!');

        return next();
      }

      if (!rows.length) {
        res.status(404).send('Something broke!');

        return next();
      }

      async.parallel([
        function (callback) {
          var params = {
            lat: rows[0].latitude,
            lon: rows[0].longitude
          };

          weather.currentWeather(params, function (data) {
            callback(null, data);
          });
        },
        function (callback) {
          var params = {
            lat: rows[0].latitude,
            lon: rows[0].longitude
          };

          weather.dailyForecast(params, function (data) {
            callback(null, data);
          });
        },
      ],
        function (err, results) {
          res.render('ajax/place.ejs', {
            dayNames: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
            disqus: config.disqus,
            forecast: results[1],
            place: rows[0],
            weather: results[0]
          });
        });
    });
  });

  // Weather API
  app.get('/api/weather', function (req, res) {
    var query = null;

    if (isValidWeatherRequestByName(req.query)) {
      query = {
        q: req.query.name
      };
    }

    if (!query && isValidWeatherRequestByCoordinates(req.query)) {
      query = req.query;
    }

    if(!query){
      res.status(400).send('Bad request!');
    }

    weather.dailyForecast(query, function (data) {
      var response = weatherMapper.mapWeatherResponse(data);
      res.json(response);
    });
  });

  function isValidWeatherRequestByName(query) {
    return query && query.name;
  }

  function isValidWeatherRequestByCoordinates(query) {
    return query && query.lat && query.lon;
  }

  // Weather API - end

  app.get('/login', function (req, res) {
    res.render('login.ejs', {
      message: req.flash('loginMessage')
    });
  });

  app.post('/login', passport.authenticate('login', {
    failureFlash: true,
    failureRedirect: '/login',
    successRedirect: '/'
  }),
    function (req, res) {
      if (req.body.remember) {
        req.session.cookie.maxAge = 5 * 60 * 1000;
      } else {
        req.session.cookie.expires = false;
      }

      res.redirect('/');
    });

  app.get('/join', function (req, res) {
    res.render('join.ejs', {
      message: req.flash('joinMessage')
    });
  });

  app.post('/join', passport.authenticate('join', {
    failureFlash: true,
    failureRedirect: '/join',
    successRedirect: '/'
  }));

  app.get('/logout', function (req, res) {
    req.logout();
    res.redirect('/');
  });
};

function isLoggedIn(req, res, next) {
  if (req.isAuthenticated()) {
    return next();
  }

  res.redirect('/login');
}
<commit_msg>Switched mysql with mongodb<commit_after>var config = require('../config/settings');
var async = require('async');
var User = require('../models/user');
var Place = require('../models/place');
var weather = require('./weather');
var weatherMapper = require('./weatherMapper');

module.exports = function (app, passport) {
  app.get('/', isLoggedIn, function (req, res) {
    res.render('index.ejs', {
      user: req.user
    });
  });

  app.get('/ajax/places', function (req, res, next) {
    Place.find({}, function (err, places) {
      if (err) {
        res.status(500).send('Something broke!');

        return next();
      }

      res.json(places);
    });
  });

  app.post('/ajax/places/filter', function (req, res, next) {
    var location = {
      $geoWithin: {
        $centerSphere: [
          [
            req.body.longitude,
            req.body.latitude
          ],
          req.body.radius / (req.body.distance === 'mi' ? 3963.2 : 6378.1)
        ]
      }
    };

    Place.find({ location: location }, function (err, places) {
      if (err) {
        res.status(500).send('Something broke!');

        return next();
      }

      res.json(places);
    });
  });

  app.post('/ajax/places/add', isLoggedIn, function (req, res, next) {
    if (req.user.username !== 'admin') {
      res.status(403).send('Forbidden!');

      return next();
    }

    var place = new Place({
      name: req.body.name,
      location: [
        req.body.longitude,
        req.body.latitude
      ]
    });

    if (req.body.description) {
      place.description = req.body.description
    }

    place.save(function (err) {
      if (err) {
        res.status(500).send('Something broke!');

        return next();
      }

      res.json(place._id);
    });
  });

  app.get('/ajax/places/:id', isLoggedIn, function (req, res, next) {
    var id = req.params.id;

    Place.findOne({ _id: id }, function (err, place) {
      if (err) {
        res.status(500).send('Something broke!');

        return next();
      }

      if (!place) {
        res.status(404).send('Place not found!');

        return next();
      }

      async.parallel([
        function (callback) {
          var params = {
            lat: place.latitude,
            lon: place.longitude
          };

          weather.currentWeather(params, function (data) {
            callback(null, data);
          });
        },
        function (callback) {
          var params = {
            lat: place.latitude,
            lon: place.longitude
          };

          weather.dailyForecast(params, function (data) {
            callback(null, data);
          });
        },
      ],
      function (err, results) {
        res.render('ajax/place.ejs', {
          dayNames: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
          disqus: config.disqus,
          forecast: results[1],
          place: place,
          weather: results[0]
        });
      });
    });
  });

  // Weather API
  app.get('/api/weather', function (req, res) {
    var query = null;

    if (isValidWeatherRequestByName(req.query)) {
      query = {
        q: req.query.name
      };
    }

    if (!query && isValidWeatherRequestByCoordinates(req.query)) {
      query = req.query;
    }

    if (!query) {
      res.status(400).send('Bad request!');
    }

    weather.dailyForecast(query, function (data) {
      var response = weatherMapper.mapWeatherResponse(data);

      res.json(response);
    });
  });

  function isValidWeatherRequestByName(query) {
    return query && query.name;
  }

  function isValidWeatherRequestByCoordinates(query) {
    return query && query.lat && query.lon;
  }

  // Weather API - end

  app.get('/login', function (req, res) {
    res.render('login.ejs', {
      message: req.flash('loginMessage')
    });
  });

  app.post('/login', passport.authenticate('login', {
    failureFlash: true,
    failureRedirect: '/login',
    successRedirect: '/'
  }),
  function (req, res) {
    if (req.body.remember) {
      req.session.cookie.maxAge = 5 * 60 * 1000;
    } else {
      req.session.cookie.expires = false;
    }

    res.redirect('/');
  });

  app.get('/join', function (req, res) {
    res.render('join.ejs', {
      message: req.flash('joinMessage')
    });
  });

  app.post('/join', passport.authenticate('join', {
    failureFlash: true,
    failureRedirect: '/join',
    successRedirect: '/'
  }));

  app.get('/logout', function (req, res) {
    req.logout();
    res.redirect('/');
  });
};

function isLoggedIn(req, res, next) {
  if (req.isAuthenticated()) {
    return next();
  }

  res.redirect('/login');
}
<|endoftext|>


<commit_before>/**
 *
 * ### options permitted : attribute(not Used For Now)
 * attribute        |   options     |   Value {default}
 * -----------------|---------------|------------------
 * data-html        |   html        |   true|false {false}
 * data-next        |   next        |   string|html depends on the data-html/html {'Next ↪'}
 * data-prev        |   prev        |   string|html depends on the data-html/html {'↩ Previous'}
 * data-overlay     |   overlay     |   true|false {true}
 *
 *
 * ### functions triggered
 * 1. onStart --> when the tour is start tour method is called.
 * 2. onClosed --> when the tour is close or destroyed. The current step element is available as a JQueryObject
 * 3. onChanged  --> when our tour moved from one step to another. The current step element is available as a JQueryObject
 * 4. onFinished(No use for the now) --> when our tour has finished its processing. The current step element is available as a JQueryObject
 */
var ProductTour;
(function (jQuery) {
    /**
     * Productor Class object
     * @param options holds the possible set of options allowed
     * @constructor
     */
    ProductTour = function (options) {
        var added = false;//if tour steps has been added
        var global_items = [];//holds our items for us globally
        var global_target_selectors = [];//holds all selector to be targeted by tour

        /**
         *  options initialization
         */
        options.next = options.next ? options.next : "Next";
        options.prev = options.prev ? options.prev : "Previous";
        options.html = "html" in options ? options.html : false;
        options.overlay = "overlay" in options ? options.overlay : true;

        //functions triggers
        options.onStart = (typeof options.onStart === 'function') ? options.onStart : undefined;
        options.onChanged = (typeof options.onChanged === 'function') ? options.onChanged : undefined;
        options.onClosed = (typeof options.onClosed === 'function') ? options.onClosed : undefined;

        /**
         * startTour method
         * this checks if tour items has been added to the page, then initialize
         *
         * if the onStart method is added in our options run it
         */
        this.startTour = function () {
            jQuery.fn.exists = function () { return this.length > 0; };
            jQuery('.cd-tour-wrapper').exists() && initTour();//checks if tour items has been added to the page, then initialize

            //running call our onStart trigger if specified
            if (jQuery.isFunction(options.onStart)) options.onStart();
        };

        /**
         * this add steps to our tour
         * @param items {Array} of item_template below
         *  each item hold this possible items
         * {
            element,//{selector}specify the target element
            title,//title of the tour , can be optional incase the content design has title in it
            content,//content to  display
            image,//specify image to be shown on mobile view
            position//top, bottom, right, left  default-> bottom
            }
         */
        this.steps = function (items) {
            if (!(items instanceof Array)) return;//expecting an array. If not one, return
            if (added) {
                console.warn("Tour steps has already been added, you can't add step on the fly, coming on next version");
                return;
            }

            //make it global
            global_items = items;//keep it there so we can use it from other functions

            //DOM initialization
            var stepsHTML = "
";
            var itemHTML = "

Step

";

            var itemString = "";
            jQuery.each(items, function (index, item) {
                //check and initialize items array of objects
                item.element = item.element ? item.element : undefined;
                item.title = item.title ? item.title : undefined;//if not specified dont add default since the content can do the design too
                item.content = item.content ? item.content : '';//leave blank
                item.image = item.image ? item.image : 'images/sample.jpg';//image is needed for mobile, dont see the need here
                item.position = item.position ? item.position : 'bottom';

                //find out if the element exists
                if (item.element == undefined)
                    item.element = 'body';//if the item dosen't exist make it point to body
                else
                    item.element += ":first"; //if exist just reference the first element

                global_target_selectors.push(item.element);//store the selector globally

                var htmlElement = jQuery(itemHTML);

                /*
                    If no title was added delete the node to allow for custom style from content
                 */
                if (item.title != undefined)
                    jQuery('h2', htmlElement).text(item.title);
                else //remove the h2 node , in case the content want to override the design
                    jQuery('h2', htmlElement).remove();

                jQuery('p', htmlElement).text(item.content);
                jQuery('.cd-more-info', htmlElement).addClass(item.position);
                jQuery('img', htmlElement).attr("src", item.image);//dont see the need here

                if (item.element) {
                    var widthHalf = jQuery(item.element).outerWidth() / 2;//get half the width of item
                    var heightHalf = jQuery(item.element).innerHeight() / 2;//get half the height

                    var top = jQuery(item.element).offset().top + -12 + heightHalf;//12px is the magical pixel ;)
                    var left = jQuery(item.element).offset().left + widthHalf;

                    jQuery('li.cd-single-step', htmlElement).css({ top: top, left: left, height: '10px', width: '10px' });//Item is ready
                }
                //build the tour content
                itemString += htmlElement.html();
            });

            stepsHTML += itemString + "
";
            //attach tour to the dom
            jQuery('body').append(stepsHTML);
            if (options.overlay)
                jQuery('body').append("

");
            added = true;
        };

        function initTour() {
            var tourWrapper = jQuery('.cd-tour-wrapper'), //get the wrapper element
                tourSteps = tourWrapper.children('li'), //get all its children with tag 'li'
                stepsNumber = tourSteps.length, //get the no of elements in this array
                coverLayer = jQuery('.cd-cover-layer'),
                tourStepInfo = jQuery('.cd-more-info'),
                tourTrigger = jQuery('#cd-tour-trigger');
            jQuery('.overlay-tour').css({ display: 'block' });

            //create the navigation for each step of the tour
            createNavigation(tourSteps, stepsNumber);

            //configuration setting initialzation
            if (options.html) {
                jQuery('.cd-next').html(options.next);
                jQuery('.cd-prev').html(options.prev);
            } else {
                jQuery('.cd-next').text(options.next);
                jQuery('.cd-prev').text(options.prev);
            }
            //init for first tour step display
            jQuery(global_target_selectors[getActiveStepCount()]).addClass('tour-target-element');

            tourTrigger.on('click', function () {
                //start tour
                if (!tourWrapper.hasClass('active')) {
                    //in that case, the tour has not been started yet
                    tourWrapper.addClass('active');
                    showStep(tourSteps.eq(0), coverLayer);
                }
            });

            //change visible step
            tourStepInfo.on('click', '.cd-prev', function (event) {
                //go to prev step - if available
                (!jQuery(event.target).hasClass('inactive')) && changeStep(tourSteps, coverLayer, 'prev');
            });
            tourStepInfo.on('click', '.cd-next', function (event) {
                //go to next step - if available
                (!jQuery(event.target).hasClass('inactive')) && changeStep(tourSteps, coverLayer, 'next');
            });

            //close tour
            tourStepInfo.on('click', '.cd-close', function (event) {
                closeTour(tourSteps, tourWrapper, coverLayer);
                //running call our onClosed trigger if specified
                if (jQuery.isFunction(options.onClosed))
                    options.onClosed(jQuery(global_items[getActiveStepCount()].element));//returns the current layer as JQuery object);
            });

            //detect swipe event on mobile - change visible step
            tourStepInfo.on('swiperight', function (event) {
                //go to prev step - if available
                if (!jQuery(this).find('.cd-prev').hasClass('inactive') && viewportSize() == 'mobile') changeStep(tourSteps, coverLayer, 'prev');
            });
            tourStepInfo.on('swipeleft', function (event) {
                //go to next step - if available
                if (!jQuery(this).find('.cd-next').hasClass('inactive') && viewportSize() == 'mobile') changeStep(tourSteps, coverLayer, 'next');
            });

            //keyboard navigation
            jQuery(document).keyup(function (event) {
                if (event.which == '37' && !tourSteps.filter('.is-selected').find('.cd-prev').hasClass('inactive')) {
                    changeStep(tourSteps, coverLayer, 'prev');
                } else if (event.which == '39' && !tourSteps.filter('.is-selected').find('.cd-next').hasClass('inactive')) {
                    changeStep(tourSteps, coverLayer, 'next');
                } else if (event.which == '27') {
                    closeTour(tourSteps, tourWrapper, coverLayer);
                    //running call our onClosed trigger if specified
                    if (jQuery.isFunction(options.onClosed))
                        options.onClosed(jQuery(global_items[getActiveStepCount()].element));
                }
            });

            if (!tourWrapper.hasClass('active')) {
                //in that case, the tour has not been started yet
                tourWrapper.addClass('active');
                showStep(tourSteps.eq(0), coverLayer);
            }
        }

        function createNavigation(steps, n) {
            var tourNavigationHtml = '
1 of ' + n + '

↩ Previous

Next ↪

Close';

            steps.each(function (index) {
                var step = jQuery(this),
                    stepNumber = index + 1,
                    nextClass = (stepNumber < n) ? '' : 'inactive',
                    prevClass = (stepNumber == 1) ? 'inactive' : '';
                var nav = jQuery(tourNavigationHtml).find('.cd-next').addClass(nextClass).end().find('.cd-prev').addClass(prevClass).end().find('.cd-actual-step').html(stepNumber).end().appendTo(step.children('.cd-more-info'));
            });
        }

        function showStep(step, layer) {
            step.addClass('is-selected').removeClass('move-left');
            smoothScroll(step.children('.cd-more-info'));
            showLayer(layer);
        }

        function smoothScroll(element) {
            if (jQuery(window).width() < 768) return;
            var top = jQuery(global_target_selectors[getActiveStepCount()]).offset().top;
            var height = jQuery(global_target_selectors[getActiveStepCount()]).height();

            (top < jQuery(window).scrollTop()) && jQuery('body,html').animate({ 'scrollTop': top - height }, 'slow');
            (top + height > jQuery(window).scrollTop() + jQuery(window).height()) && jQuery('body,html').animate({ 'scrollTop': top - height }, 'slow');
        }

        function showLayer(layer) {
            layer.addClass('is-visible').on('webkitAnimationEnd oanimationend msAnimationEnd animationend', function () {
                layer.removeClass('is-visible');
            });
        }

        /**
         * this changes step , if any is active call the onChanged(current_layer):JQueryObject function if specified
         * @param steps
         * @param layer
         * @param bool
         */
        function changeStep(steps, layer, bool) {
            var visibleStep = steps.filter('.is-selected'),
                delay = (viewportSize() == 'desktop') ? 300 : 0;
            visibleStep.removeClass('is-selected');

            //remove class that make tour target elemment pop when selected
            jQuery(global_target_selectors[getActiveStepCount()]).removeClass('tour-target-element');

            (bool == 'next') && visibleStep.addClass('move-left');

            setTimeout(function () {
                (bool == 'next')
                    ? showStep(visibleStep.next(), layer)
                    : showStep(visibleStep.prev(), layer);

                //add class to make tour target element pop
                jQuery(global_target_selectors[getActiveStepCount()]).addClass('tour-target-element');

                //call onChange trigger if specified
                if (jQuery.isFunction(options.onChanged))
                    options.onChanged(jQuery(global_items[getActiveStepCount()].element));//returns the current layer as JQuery object
            }, delay);
        }

        /**
         * this returns the active step number
         * @return {number}
         */
        function getActiveStepCount() {
            var tourWrapper = jQuery('.cd-tour-wrapper'), //get the wrapper element
                tourSteps = tourWrapper.children('li'); //get all its children with tag 'li'

            var current_index = 0;
            tourSteps.each(function (index, li) {
                if (jQuery(li).hasClass('is-selected'))
                    current_index = index;
            });

            return current_index;
        }

        function closeTour(steps, wrapper, layer) {
            steps.removeClass('is-selected move-left');
            wrapper.removeClass('active');
            layer.removeClass('is-visible');
            jQuery('.overlay-tour').css({ display: 'none' });
            jQuery(global_target_selectors[getActiveStepCount()]).removeClass('tour-target-element');
        }

        function viewportSize() {
            /* retrieve the content value of .cd-main::before to check the actua mq */
            return window.getComputedStyle(document.querySelector('.cd-tour-wrapper'), '::before').getPropertyValue('content').replace(/"/g, "").replace(/'/g, "");
        }

    }
} (jQuery));<commit_msg>Update product-tour.js<commit_after>/**
 *
 * ### options permitted : attribute(not Used For Now)
 * attribute        |   options     |   Value {default}
 * -----------------|---------------|------------------
 * data-html        |   html        |   true|false {false}
 * data-next        |   next        |   string|html depends on the data-html/html {'Next ↪'}
 * data-prev        |   prev        |   string|html depends on the data-html/html {'↩ Previous'}
 * data-overlay     |   overlay     |   true|false {true}
 *
 *
 * ### functions triggered
 * 1. onStart --> when the tour is start tour method is called.
 * 2. onClosed --> when the tour is close or destroyed. The current step element is available as a JQueryObject
 * 3. onChanged  --> when our tour moved from one step to another. The current step element is available as a JQueryObject
 * 4. onFinished(No use for the now) --> when our tour has finished its processing. The current step element is available as a JQueryObject
 */
var ProductTour;
(function (jQuery) {
    /**
     * Productor Class object
     * @param options holds the possible set of options allowed
     * @constructor
     */
    ProductTour = function (options) {
        var added = false;//if tour steps has been added
        var global_items = [];//holds our items for us globally
        var global_target_selectors = [];//holds all selector to be targeted by tour

        /**
         *  options initialization
         */
        options.next = options.next ? options.next : "Next";
        options.prev = options.prev ? options.prev : "Previous";
        options.html = "html" in options ? options.html : false;
        options.overlay = "overlay" in options ? options.overlay : true;

        //functions triggers
        options.onStart = (typeof options.onStart === 'function') ? options.onStart : undefined;
        options.onChanged = (typeof options.onChanged === 'function') ? options.onChanged : undefined;
        options.onClosed = (typeof options.onClosed === 'function') ? options.onClosed : undefined;

        /**
         * startTour method
         * this checks if tour items has been added to the page, then initialize
         *
         * if the onStart method is added in our options run it
         */
        this.startTour = function () {
            jQuery.fn.exists = function () { return this.length > 0; };
            jQuery('.cd-tour-wrapper').exists() && initTour();//checks if tour items has been added to the page, then initialize

            //running call our onStart trigger if specified
            if (jQuery.isFunction(options.onStart)) options.onStart();
        };

        /**
         * this add steps to our tour
         * @param items {Array} of item_template below
         *  each item hold this possible items
         * {
            element,//{selector}specify the target element
            title,//title of the tour , can be optional incase the content design has title in it
            content,//content to  display
            image,//specify image to be shown on mobile view
            position//top, bottom, right, left  default-> bottom
            }
         */
        this.steps = function (items) {
            if (!(items instanceof Array)){
                console.error("Expecting an array of tour items or steps.");
                return;//expecting an array. If not one, return
            }
            if (added) {
                console.warn("Tour steps has already been added, you can't add step on the fly, coming on next version");
                return;
            }

            //make it global
            global_items = items;//keep it there so we can use it from other functions

            //DOM initialization
            var stepsHTML = "
";
            var itemHTML = "

Step

";

            var itemString = "";
            jQuery.each(items, function (index, item) {
                //check and initialize items array of objects
                item.element = item.element ? item.element : undefined;
                item.title = item.title ? item.title : undefined;//if not specified dont add default since the content can do the design too
                item.content = item.content ? item.content : '';//leave blank
                item.image = item.image ? item.image : 'images/sample.jpg';//image is needed for mobile, dont see the need here
                item.position = item.position ? item.position : 'bottom';

                //find out if the element exists
                if (item.element == undefined)
                    item.element = 'body';//if the item dosen't exist make it point to body
                else
                    item.element += ":first"; //if exist just reference the first element

                global_target_selectors.push(item.element);//store the selector globally

                var htmlElement = jQuery(itemHTML);

                /*
                    If no title was added delete the node to allow for custom style from content
                 */
                if (item.title != undefined)
                    jQuery('h2', htmlElement).text(item.title);
                else //remove the h2 node , in case the content want to override the design
                    jQuery('h2', htmlElement).remove();

                jQuery('p', htmlElement).text(item.content);
                jQuery('.cd-more-info', htmlElement).addClass(item.position);
                jQuery('img', htmlElement).attr("src", item.image);//dont see the need here

                if (item.element) {
                    var widthHalf = jQuery(item.element).outerWidth() / 2;//get half the width of item
                    var heightHalf = jQuery(item.element).innerHeight() / 2;//get half the height

                    var top = jQuery(item.element).offset().top + -12 + heightHalf;//12px is the magical pixel ;)
                    var left = jQuery(item.element).offset().left + widthHalf;

                    jQuery('li.cd-single-step', htmlElement).css({ top: top, left: left, height: '10px', width: '10px' });//Item is ready
                }
                //build the tour content
                itemString += htmlElement.html();
            });

            stepsHTML += itemString + "
";
            //attach tour to the dom
            jQuery('body').append(stepsHTML);
            if (options.overlay)
                jQuery('body').append("

");
            added = true;
        };

        function initTour() {
            var tourWrapper = jQuery('.cd-tour-wrapper'), //get the wrapper element
                tourSteps = tourWrapper.children('li'), //get all its children with tag 'li'
                stepsNumber = tourSteps.length, //get the no of elements in this array
                coverLayer = jQuery('.cd-cover-layer'),
                tourStepInfo = jQuery('.cd-more-info'),
                tourTrigger = jQuery('#cd-tour-trigger');
            jQuery('.overlay-tour').css({ display: 'block' });

            //create the navigation for each step of the tour
            createNavigation(tourSteps, stepsNumber);

            //configuration setting initialzation
            if (options.html) {
                jQuery('.cd-next').html(options.next);
                jQuery('.cd-prev').html(options.prev);
            } else {
                jQuery('.cd-next').text(options.next);
                jQuery('.cd-prev').text(options.prev);
            }
            //init for first tour step display
            jQuery(global_target_selectors[getActiveStepCount()]).addClass('tour-target-element');

            tourTrigger.on('click', function () {
                //start tour
                if (!tourWrapper.hasClass('active')) {
                    //in that case, the tour has not been started yet
                    tourWrapper.addClass('active');
                    showStep(tourSteps.eq(0), coverLayer);
                }
            });

            //change visible step
            tourStepInfo.on('click', '.cd-prev', function (event) {
                //go to prev step - if available
                (!jQuery(event.target).hasClass('inactive')) && changeStep(tourSteps, coverLayer, 'prev');
            });
            tourStepInfo.on('click', '.cd-next', function (event) {
                //go to next step - if available
                (!jQuery(event.target).hasClass('inactive')) && changeStep(tourSteps, coverLayer, 'next');
            });

            //close tour
            tourStepInfo.on('click', '.cd-close', function (event) {
                closeTour(tourSteps, tourWrapper, coverLayer);
                //running call our onClosed trigger if specified
                if (jQuery.isFunction(options.onClosed))
                    options.onClosed(jQuery(global_items[getActiveStepCount()].element));//returns the current layer as JQuery object);
            });

            //detect swipe event on mobile - change visible step
            tourStepInfo.on('swiperight', function (event) {
                //go to prev step - if available
                if (!jQuery(this).find('.cd-prev').hasClass('inactive') && viewportSize() == 'mobile') changeStep(tourSteps, coverLayer, 'prev');
            });
            tourStepInfo.on('swipeleft', function (event) {
                //go to next step - if available
                if (!jQuery(this).find('.cd-next').hasClass('inactive') && viewportSize() == 'mobile') changeStep(tourSteps, coverLayer, 'next');
            });

            //keyboard navigation
            jQuery(document).keyup(function (event) {
                if (event.which == '37' && !tourSteps.filter('.is-selected').find('.cd-prev').hasClass('inactive')) {
                    changeStep(tourSteps, coverLayer, 'prev');
                } else if (event.which == '39' && !tourSteps.filter('.is-selected').find('.cd-next').hasClass('inactive')) {
                    changeStep(tourSteps, coverLayer, 'next');
                } else if (event.which == '27') {
                    closeTour(tourSteps, tourWrapper, coverLayer);
                    //running call our onClosed trigger if specified
                    if (jQuery.isFunction(options.onClosed))
                        options.onClosed(jQuery(global_items[getActiveStepCount()].element));
                }
            });

            if (!tourWrapper.hasClass('active')) {
                //in that case, the tour has not been started yet
                tourWrapper.addClass('active');
                showStep(tourSteps.eq(0), coverLayer);
            }
        }

        function createNavigation(steps, n) {
            var tourNavigationHtml = '
1 of ' + n + '

↩ Previous

Next ↪

Close';

            steps.each(function (index) {
                var step = jQuery(this),
                    stepNumber = index + 1,
                    nextClass = (stepNumber < n) ? '' : 'inactive',
                    prevClass = (stepNumber == 1) ? 'inactive' : '';
                var nav = jQuery(tourNavigationHtml).find('.cd-next').addClass(nextClass).end().find('.cd-prev').addClass(prevClass).end().find('.cd-actual-step').html(stepNumber).end().appendTo(step.children('.cd-more-info'));
            });
        }

        function showStep(step, layer) {
            step.addClass('is-selected').removeClass('move-left');
            smoothScroll(step.children('.cd-more-info'));
            showLayer(layer);
        }

        function smoothScroll(element) {
            if (jQuery(window).width() < 768) return;
            var top = jQuery(global_target_selectors[getActiveStepCount()]).offset().top;
            var height = jQuery(global_target_selectors[getActiveStepCount()]).height();

            (top < jQuery(window).scrollTop()) && jQuery('body,html').animate({ 'scrollTop': top - height }, 'slow');
            (top + height > jQuery(window).scrollTop() + jQuery(window).height()) && jQuery('body,html').animate({ 'scrollTop': top - height }, 'slow');
        }

        function showLayer(layer) {
            layer.addClass('is-visible').on('webkitAnimationEnd oanimationend msAnimationEnd animationend', function () {
                layer.removeClass('is-visible');
            });
        }

        /**
         * this changes step , if any is active call the onChanged(current_layer):JQueryObject function if specified
         * @param steps
         * @param layer
         * @param bool
         */
        function changeStep(steps, layer, bool) {
            var visibleStep = steps.filter('.is-selected'),
                delay = (viewportSize() == 'desktop') ? 300 : 0;
            visibleStep.removeClass('is-selected');

            //remove class that make tour target elemment pop when selected
            jQuery(global_target_selectors[getActiveStepCount()]).removeClass('tour-target-element');

            (bool == 'next') && visibleStep.addClass('move-left');

            setTimeout(function () {
                (bool == 'next')
                    ? showStep(visibleStep.next(), layer)
                    : showStep(visibleStep.prev(), layer);

                //add class to make tour target element pop
                jQuery(global_target_selectors[getActiveStepCount()]).addClass('tour-target-element');

                //call onChange trigger if specified
                if (jQuery.isFunction(options.onChanged))
                    options.onChanged(jQuery(global_items[getActiveStepCount()].element));//returns the current layer as JQuery object
            }, delay);
        }

        /**
         * this returns the active step number
         * @return {number}
         */
        function getActiveStepCount() {
            var tourWrapper = jQuery('.cd-tour-wrapper'), //get the wrapper element
                tourSteps = tourWrapper.children('li'); //get all its children with tag 'li'

            var current_index = 0;
            tourSteps.each(function (index, li) {
                if (jQuery(li).hasClass('is-selected'))
                    current_index = index;
            });

            return current_index;
        }

        function closeTour(steps, wrapper, layer) {
            steps.removeClass('is-selected move-left');
            wrapper.removeClass('active');
            layer.removeClass('is-visible');
            jQuery('.overlay-tour').css({ display: 'none' });
            jQuery(global_target_selectors[getActiveStepCount()]).removeClass('tour-target-element');
        }

        function viewportSize() {
            /* retrieve the content value of .cd-main::before to check the actua mq */
            return window.getComputedStyle(document.querySelector('.cd-tour-wrapper'), '::before').getPropertyValue('content').replace(/"/g, "").replace(/'/g, "");
        }

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


<commit_before>var child_process = require('child_process')
var fs = require('fs');
var max_sleep = 300
var step = 10
//Added a comment that achieves no real goal.
//Non-blocking loop
//Adjustable step
//Status line
//Add a rough time indicator
//First cut at libfaketime support

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error",
  "Almost there",
  "Nearly got it",
  "Not quite working",
  "Ooops!",
  "Did I do that?",
  "Commit all the required changes",
  "Really commit all the required changes",
  "Should work now",
  "Back out the last commit",
  "Reverting changes",
  "One more time...",
  "Re-commiting last",
  "One more User Story done",
  "Can work my way through the Kanban",
  "Sprint complete",
  "Well that seemed pretty easy",
  "What do you mean you can't pack up the commit?",
  "Merge in master",
  "Fast-forwarding changes"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (inFile == outFile) {
    console.error("Aborted: infile and outfile must be different")
    return(-1);
  }
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  if (process.argv [ 5 ]) step = parseInt(process.argv [ 5 ])
  console.info("Writing from %s to %s, with up to %s seconds between commits of %s bytes", inFile, outFile, max_sleep, step)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    var commit_count = Math.ceil(length / step)
    var commit_time = commit_count * max_sleep / 2
    var commit_time_hour = Math.floor(commit_time / 3600)
    var commit_time_rem = commit_time - (commit_time_hour * 3600)
    var commit_time_min = Math.floor(commit_time_rem / 60)
    var commit_time_sec = commit_time_rem - (commit_time_min * 60)
    console.info("Input file is %s Bytes, this will generate %s commits and take approximately %s hours %s minutes %s seconds", length, commit_count, commit_time_hour, commit_time_min, commit_time_sec)
    var t_offset = 0
    if (process.env.FAKETIME) {
      // 20% buffer, to reduce the likelihood of going in to the future
      t_offset = commit_time * -1.2;
      faketime(t_offset);
    }
    try {
      child_process.execFileSync('git', ['add', outFile])
    } catch (e) {
      console.error("Couldn't add %s to git: %s", outFile, e)
    }
    var counter = 0
    process.stdout.write("Queueing up the first commit...\r");
    var timeout;
    if (process.env.FAKETIME) {
      faketime(t_offset);
      timeout = 20;
    } else {
      timeout = randomIntFromInterval(0, max_sleep) * 1000
    }
    setTimeout(commit, timeout, t_offset, outFD, outFile, data, counter, length);
  })
} else {
  console.info("Usage:")
  console.info("node commit.js infile outfile [max_time_in_seconds] [step]")
}

function sleep(seconds) {
  var endTime = new Date().getTime() + (seconds * 1000);
  while (new Date().getTime() <= endTime) {;}
}

function randomIntFromInterval(min,max)
{
   return Math.floor(Math.random()*(max-min+1)+min);
}

function commit(t_offset, outFD, outFile, data, counter, length)
{
  if (counter < length)
  {
    var message = commit_messages[randomIntFromInterval(0, commit_messages.length - 1)]
    var args = ['commit', outFile, '-m', message]
    if (counter + step >= length) step = length - counter
    process.stdout.write("\033[2K Writing " + counter + "/" + length + " bytes: " + message + "\r");
    fs.writeSync(outFD, data.slice(counter, counter+step), 0, step)
    child_process.execFileSync('/Applications/Xcode.app/Contents/Developer/usr/bin/git', args)
    var timeout = randomIntFromInterval<commit_msg>Fixing punctuation<commit_after>var child_process = require('child_process')
var fs = require('fs');
var max_sleep = 300
var step = 10
//Added a comment that achieves no real goal.
//Non-blocking loop
//Adjustable step
//Status line
//Add a rough time indicator
//First cut at libfaketime support

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error",
  "Almost there",
  "Nearly got it",
  "Not quite working",
  "Ooops!",
  "Did I do that?",
  "Commit all the required changes",
  "Really commit all the required changes",
  "Should work now",
  "Back out the last commit",
  "Reverting changes",
  "One more time...",
  "Re-commiting last",
  "One more User Story done",
  "Can work my way through the Kanban",
  "Sprint complete",
  "Well that seemed pretty easy",
  "What do you mean you can't pack up the commit?",
  "Merge in master",
  "Fast-forwarding changes"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (inFile == outFile) {
    console.error("Aborted: infile and outfile must be different")
    return(-1);
  }
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  if (process.argv [ 5 ]) step = parseInt(process.argv [ 5 ])
  console.info("Writing from %s to %s, with up to %s seconds between commits of %s bytes", inFile, outFile, max_sleep, step)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    var commit_count = Math.ceil(length / step)
    var commit_time = commit_count * max_sleep / 2
    var commit_time_hour = Math.floor(commit_time / 3600)
    var commit_time_rem = commit_time - (commit_time_hour * 3600)
    var commit_time_min = Math.floor(commit_time_rem / 60)
    var commit_time_sec = commit_time_rem - (commit_time_min * 60)
    console.info("Input file is %s Bytes, this will generate %s commits and take approximately %s hours %s minutes %s seconds", length, commit_count, commit_time_hour, commit_time_min, commit_time_sec)
    var t_offset = 0
    if (process.env.FAKETIME) {
      // 20% buffer, to reduce the likelihood of going in to the future
      t_offset = commit_time * -1.2;
      faketime(t_offset);
    }
    try {
      child_process.execFileSync('git', ['add', outFile])
    } catch (e) {
      console.error("Couldn't add %s to git: %s", outFile, e)
    }
    var counter = 0
    process.stdout.write("Queueing up the first commit...\r");
    var timeout;
    if (process.env.FAKETIME) {
      faketime(t_offset);
      timeout = 20;
    } else {
      timeout = randomIntFromInterval(0, max_sleep) * 1000
    }
    setTimeout(commit, timeout, t_offset, outFD, outFile, data, counter, length);
  })
} else {
  console.info("Usage:")
  console.info("node commit.js infile outfile [max_time_in_seconds] [step]")
}

function sleep(seconds) {
  var endTime = new Date().getTime() + (seconds * 1000);
  while (new Date().getTime() <= endTime) {;}
}

function randomIntFromInterval(min,max)
{
   return Math.floor(Math.random()*(max-min+1)+min);
}

function commit(t_offset, outFD, outFile, data, counter, length)
{
  if (counter < length)
  {
    var message = commit_messages[randomIntFromInterval(0, commit_messages.length - 1)]
    var args = ['commit', outFile, '-m', message]
    if (counter + step >= length) step = length - counter
    process.stdout.write("\033[2K Writing " + counter + "/" + length + " bytes: " + message + "\r");
    fs.writeSync(outFD, data.slice(counter, counter+step), 0, step)
    child_process.execFileSync('/Applications/Xcode.app/Contents/Developer/usr/bin/git', args)
    var timeout = randomIntFromInterval(<|endoftext|>


<commit_before>
var express = require("express")
,   app = express()
,   genMap = {
        respec: require("./generators/respec").generate
    }
,   num2 = function (num) {
        var str = num + "";
        if (str.length >= 2) return str;
        return "0" + str;
    }
,   path = require('path')
,   request = require("request")
,   URL = require('url').URL
,   fileUpload = require('express-fileupload')
,   BASE_URI = process.env.BASE_URI || ""
;
app.use(fileUpload({
    createParentPath: true,
    useTempFiles: true,
    tempFileDir: 'uploads/'
}));

// Listens to GET at the root, expects two required query string parameters:
//  type:   the type of the generator (case-insensitive)
//  url:    the URL to the source document
app.get("/", function (req, res) {
    var type = (req.query.type || "").toLowerCase()
    ,   url = req.query.url ? decodeURIComponent(req.query.url) : undefined
    ;
    if (!url || !type) return res.status(500).json({ error: "Both 'type' and 'url' are required." });
    if (!genMap[type]) return res.status(500).json({ error: "Unknown generator: " + type });
    // We look if the provided URL comes with a shortName in the query string
    var specURL = new URL(url);
    if (specURL.hostname === "raw.githubusercontent.com")
      return res.status(500).json({ error: "raw.githubusercontent.com URLs aren't supported. Use github pages instead."});
    var shortName = specURL.searchParams.get("shortName");

    let publishDate;
    if (specURL.searchParams.get("publishDate")) {
        publishDate = specURL.searchParams.get("publishDate");
    } else {
        const d = new Date();
        publishDate = [d.getFullYear(), num2(d.getMonth() + 1), num2(d.getDate())].join("-");
    }
    specURL.searchParams.set("publishDate", publishDate);

    // if shortName was provided, we collect info on previous version
    if (shortName) {
        request.get("https://www.w3.org/TR/" + shortName + "/", function(error, response, body) {
            if (error)
                return res.status(400).json({error: error});
            else if (response && response.statusCode >= 400 && response.statusCode < 500)
                return res.status(response.statusCode).json({error: response.statusMessage});
            var $   = require('whacko').load(body)
            ,   $dl = $("body div.head dl")
            ,   thisURI
            ,   previousURI;
            if ($dl) {
                $dl.find("dt").each(function() {
                    var $dt = $(this)
                    txt = $dt.text()
                    .toLowerCase()
                    .replace(":", "")
                    .replace("published ", "")
                    .trim();
                    $dd = $dt.next();
                    if (txt === "this version") {
                        thisURI = $dd.find('a').attr('href');
                    }
                    else if (/^previous version(?:s)?$/.test(txt))
                    previousURI = $dd.find('a').first().attr('href');
                })
            }
            if (!thisURI) return res.status(500).json({ error: "Couldn't find a 'This version' uri in the previous version." });
            var thisDate = thisURI.match(/[1-2][0-9]{7}/)[0]
            ,   prev     = (thisDate === publishDate.replace(/\-/g, '')) ? previousURI : thisURI
            ,   pDate    = prev.match(/[1-2][0-9]{7}/)[0];
            specURL.searchParams.set("previousMaturity", prev.match(/\/TR\/[0-9]{4}\/([A-Z]+)/)[1]);
            specURL.searchParams.set("previousPublishDate", pDate.substring(0, 4) + '-' +
            pDate.substring(4, 6) + '-' + pDate.substring(6, 8));
            generate(specURL.href);
        });
    } else {
        generate(specURL.href);
    }

    async function generate(url) {
        // if there's an error we get an err object with status and message, otherwise we get content
        try {
            const content = await genMap[type](url);
            res.send(content);
        } catch (err) {
            res.status(err.status).json({ error: err.message });
        }
    }
});

app.use('/uploads', express.static('./uploads', {
  setHeaders: (res, requestPath) => {
      let noExtension = !Boolean(path.extname(requestPath));
      if (noExtension) res.setHeader('Content-Type', 'text/html');
    }}));

app.post("/", async (req, res) => {
    try {
        if (!req.files) {
            res.send({
                status: 500,
                message: 'No file uploaded'
            });
        } else {
            const file = req.files.file,
                  baseUrl = req.protocol + "://" + req.headers.host + '/',
                  params = req.body ? Object.keys(req.body).map(key => key + '=' + req.body[key]).join('&') : "";
                  src = baseUrl  + file.tempFilePath + ('?' + params || ""),
                  qs = {url: src, type: 'respec'};
            request.get({url: baseUrl, qs: qs}, (err, response, body) => {
                if (err) {
                    res.status(500).send(err);
                } else {
                    res.send(body);
                    // delete temp file
                    require("fs").promises.unlink(file.tempFilePath);
                }
            });
        }
    } catch (err) {
        res.status(500).send(err);
    }
});

/**
* Start listening for HTTP requests.
*
* @param {Number} port - port number to use (optional); defaults to environment variable `$PORT` if exists, and to `80` if not
* @returns {Object} a `http.Server`; cf https://nodejs.org/dist/latest-v8.x/docs/api/http.html#http_class_http_server
*/

app.start = (port) => {
    if (port)
        return app.listen(port);
    else
        return app.listen(process.env.PORT || 80);
};

if (module === process.mainModule)
    app.start();
else
    module.exports = app;
<commit_msg>complete base url with BASE_URI<commit_after>
var express = require("express")
,   app = express()
,   genMap = {
        respec: require("./generators/respec").generate
    }
,   num2 = function (num) {
        var str = num + "";
        if (str.length >= 2) return str;
        return "0" + str;
    }
,   path = require('path')
,   request = require("request")
,   URL = require('url').URL
,   fileUpload = require('express-fileupload')
,   BASE_URI = process.env.BASE_URI || ""
;
app.use(fileUpload({
    createParentPath: true,
    useTempFiles: true,
    tempFileDir: 'uploads/'
}));

// Listens to GET at the root, expects two required query string parameters:
//  type:   the type of the generator (case-insensitive)
//  url:    the URL to the source document
app.get("/", function (req, res) {
    var type = (req.query.type || "").toLowerCase()
    ,   url = req.query.url ? decodeURIComponent(req.query.url) : undefined
    ;
    if (!url || !type) return res.status(500).json({ error: "Both 'type' and 'url' are required." });
    if (!genMap[type]) return res.status(500).json({ error: "Unknown generator: " + type });
    // We look if the provided URL comes with a shortName in the query string
    var specURL = new URL(url);
    if (specURL.hostname === "raw.githubusercontent.com")
      return res.status(500).json({ error: "raw.githubusercontent.com URLs aren't supported. Use github pages instead."});
    var shortName = specURL.searchParams.get("shortName");

    let publishDate;
    if (specURL.searchParams.get("publishDate")) {
        publishDate = specURL.searchParams.get("publishDate");
    } else {
        const d = new Date();
        publishDate = [d.getFullYear(), num2(d.getMonth() + 1), num2(d.getDate())].join("-");
    }
    specURL.searchParams.set("publishDate", publishDate);

    // if shortName was provided, we collect info on previous version
    if (shortName) {
        request.get("https://www.w3.org/TR/" + shortName + "/", function(error, response, body) {
            if (error)
                return res.status(400).json({error: error});
            else if (response && response.statusCode >= 400 && response.statusCode < 500)
                return res.status(response.statusCode).json({error: response.statusMessage});
            var $   = require('whacko').load(body)
            ,   $dl = $("body div.head dl")
            ,   thisURI
            ,   previousURI;
            if ($dl) {
                $dl.find("dt").each(function() {
                    var $dt = $(this)
                    txt = $dt.text()
                    .toLowerCase()
                    .replace(":", "")
                    .replace("published ", "")
                    .trim();
                    $dd = $dt.next();
                    if (txt === "this version") {
                        thisURI = $dd.find('a').attr('href');
                    }
                    else if (/^previous version(?:s)?$/.test(txt))
                    previousURI = $dd.find('a').first().attr('href');
                })
            }
            if (!thisURI) return res.status(500).json({ error: "Couldn't find a 'This version' uri in the previous version." });
            var thisDate = thisURI.match(/[1-2][0-9]{7}/)[0]
            ,   prev     = (thisDate === publishDate.replace(/\-/g, '')) ? previousURI : thisURI
            ,   pDate    = prev.match(/[1-2][0-9]{7}/)[0];
            specURL.searchParams.set("previousMaturity", prev.match(/\/TR\/[0-9]{4}\/([A-Z]+)/)[1]);
            specURL.searchParams.set("previousPublishDate", pDate.substring(0, 4) + '-' +
            pDate.substring(4, 6) + '-' + pDate.substring(6, 8));
            generate(specURL.href);
        });
    } else {
        generate(specURL.href);
    }

    async function generate(url) {
        // if there's an error we get an err object with status and message, otherwise we get content
        try {
            const content = await genMap[type](url);
            res.send(content);
        } catch (err) {
            res.status(err.status).json({ error: err.message });
        }
    }
});

app.use('/uploads', express.static('./uploads', {
  setHeaders: (res, requestPath) => {
      let noExtension = !Boolean(path.extname(requestPath));
      if (noExtension) res.setHeader('Content-Type', 'text/html');
    }}));

app.post("/", async (req, res) => {
    try {
        if (!req.files) {
            res.send({
                status: 500,
                message: 'No file uploaded'
            });
        } else {
            const file = req.files.file,
                  baseUrl = req.protocol + "://" + req.headers.host + '/' + BASE_URI,
                  params = req.body ? Object.keys(req.body).map(key => key + '=' + req.body[key]).join('&') : "";
                  src = baseUrl  + file.tempFilePath + ('?' + params || ""),
                  qs = {url: src, type: 'respec'};
            request.get({url: baseUrl, qs: qs}, (err, response, body) => {
                if (err) {
                    res.status(500).send(err);
                } else {
                    res.send(body);
                    // delete temp file
                    require("fs").promises.unlink(file.tempFilePath);
                }
            });
        }
    } catch (err) {
        res.status(500).send(err);
    }
});

/**
* Start listening for HTTP requests.
*
* @param {Number} port - port number to use (optional); defaults to environment variable `$PORT` if exists, and to `80` if not
* @returns {Object} a `http.Server`; cf https://nodejs.org/dist/latest-v8.x/docs/api/http.html#http_class_http_server
*/

app.start = (port) => {
    if (port)
        return app.listen(port);
    else
        return app.listen(process.env.PORT || 80);
};

if (module === process.mainModule)
    app.start();
else
    module.exports = app;
<|endoftext|>


<commit_before>const child_process = require('child_process');
const ProcessWrapper = require('../../process/processWrapper');
const constants = require('../../constants');

let ipfsProcess; // eslint-disable-line no-unused-vars

class IPFSProcess extends ProcessWrapper {
  constructor(_options) {
    super();

    this.checkIPFSVersion();
    this.startIPFSDaemon();
  }

  checkIPFSVersion() {
    child_process.exec('ipfs --version', {silent: true}, (err, stdout, _stderr) => {
      if (err) {
        console.error(err);
        return;
      }
      const match = stdout.match(/[0-9]+\.[0-9]+\.[0-9]+/);
      if (match[0]) {
        const versions = match[0].split('.');
        if (versions[0] <= 0 && versions[1] <= 4 && versions[2] <= 14) {
          console.error(`You are using IPFS version ${match[0]} which has an issue with processes.`);
          console.error(`Please update to IPFS version 0.4.15 or more recent: https://github.com/ipfs/ipfs-update`);
        }
      }
    });
  }

  startIPFSDaemon() {
    const self = this;
    const child = child_process.spawn('ipfs', ['daemon']);
    let lastMessage;
    child.stdout.on('data', (data) => {
      if (!self.readyCalled && data.indexOf('Daemon is ready') > -1) {
        self.readyCalled = true;
        self.send({result: constants.storage.initiated});
      }
      lastMessage = data;
      console.log('IPFS: ' + data);
    });
    child.on('exit', (code) => {
      if (code) {
        console.error('IPFS exited with error code ' + code);
        console.error(lastMessage);
      }
    });
  }
}

process.on('message', (msg) => {
  if (msg.action === constants.storage.init) {
    ipfsProcess = new IPFSProcess(msg.options);
  }
});
<commit_msg>add more error management to ipfs<commit_after>const child_process = require('child_process');
const ProcessWrapper = require('../../process/processWrapper');
const constants = require('../../constants');

let ipfsProcess; // eslint-disable-line no-unused-vars

class IPFSProcess extends ProcessWrapper {
  constructor(_options) {
    super();

    this.checkIPFSVersion();
    this.startIPFSDaemon();
  }

  checkIPFSVersion() {
    child_process.exec('ipfs --version', {silent: true}, (err, stdout, _stderr) => {
      if (err) {
        console.error(err);
        return;
      }
      const match = stdout.match(/[0-9]+\.[0-9]+\.[0-9]+/);
      if (match[0]) {
        const versions = match[0].split('.');
        if (versions[0] <= 0 && versions[1] <= 4 && versions[2] <= 14) {
          console.error(`You are using IPFS version ${match[0]} which has an issue with processes.`);
          console.error(`Please update to IPFS version 0.4.15 or more recent: https://github.com/ipfs/ipfs-update`);
        }
      }
    });
  }

  startIPFSDaemon() {
    const self = this;
    const child = child_process.spawn('ipfs', ['daemon']);
    let lastMessage;

    child.on('error', (err) => {
      err = err.toString();
      console.error('IPFS error: ', err);
    });
    child.stderr.on('data', (data) => {
      data = data.toString();
      console.log(`IPFS error: ${data}`);
    });
    child.stdout.on('data', (data) => {
      data = data.toString();
      if (!self.readyCalled && data.indexOf('Daemon is ready') > -1) {
        self.readyCalled = true;
        self.send({result: constants.storage.initiated});
      }
      lastMessage = data;
      console.log('IPFS: ' + data);
    });
    child.on('exit', (code) => {
      if (code) {
        console.error('IPFS exited with error code ' + code);
        if (lastMessage) {
          console.error(lastMessage);
        }
      }
    });
  }
}

process.on('message', (msg) => {
  if (msg.action === constants.storage.init) {
    ipfsProcess = new IPFSProcess(msg.options);
  }
});
<|endoftext|>