<commit_before>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300
//Added a comment that achieves no real goal.

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<commit_msg>Who you calling a fool?<commit_after>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300
//Added a comment that achieves no real goal.

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 <|endoftext|>


<commit_before>(function() {
  'use strict';

  angular.module('dimApp')
    .factory('dimCsvService', CsvService);

  function CsvService() {
    // step node names we'll hide, we'll leave "* Chroma" for now though, since we don't otherwise indicate Chroma
    var FILTER_NODE_NAMES = ["Upgrade Defense", "Ascend", "Infuse", "Increase Intellect", "Increase Discipline",
      "Increase Strength", "Twist Fate", "The Life Exotic", "Reforge Artifact", "Reforge Shell",
      "Deactivate Chroma", "Kinetic Damage", "Solar Damage", "Arc Damage", "Void Damage"];

    function capitalizeFirstLetter(string) {
      if (!string || string.length === 0) {
        return "";
      }
      return string.charAt(0).toUpperCase() + string.slice(1);
    }

    function downloadCsv(filename, csv) {
      filename = filename + ".csv";
      var pom = document.createElement('a');
      pom.setAttribute('href', 'data:text/csv;charset=utf-8,' + encodeURIComponent(csv));
      pom.setAttribute('download', filename);
      pom.click();
    }

    function buildNodeString(nodes) {
      var data = "";
      nodes.forEach(function(node) {
        if (_.contains(FILTER_NODE_NAMES, node.name)){
          return;
        }
        data += node.name;
        if (node.activated){
          data += "*";
        }
        data += ", ";
      });

      return data;
    }

    function downloadArmor(items, nameMap) {
      var header = "Name, Tag, Tier, Type, Equippable, Light, Owner, % Leveled, Locked, Equipped, Year, " +
        "% Quality, % IntQ, % DiscQ, % StrQ, Int, Disc, Str, Notes, Perks\n";
      var data = "";
      items.forEach(function(item) {
        data += item.name + ", ";
        data += (item.dimInfo.tag || '') + ", ";
        data += item.tier + ", ";
        data += item.typeName + ", ";
        var equippable = item.classTypeName;
        if (!equippable || equippable === "unknown") {
          equippable = "Any";
        }
        else {
          equippable = capitalizeFirstLetter(equippable);
        }
        data += equippable + ", ";
        data += item.primStat.value + ", ";
        data += nameMap[item.owner] + ", ";
        data += (item.percentComplete * 100).toFixed(0) + ", ";
        data += item.locked + ", ";
        data += item.equipped + ", ";
        data += item.year + ", ";
        data += item.quality ? item.quality.max + ", " : "0, ";
        var stats = {};
        if (item.stats) {
          item.stats.forEach(function(stat) {
            var pct = 0;
            if (stat.scaled && stat.scaled.min) {
              pct = Math.round(100 * stat.scaled.min / stat.split);
            }
            stats[stat.name] = {
              value: stat.value,
              pct: pct
            };
          });
        }
        data += stats.Intellect ? stats.Intellect.pct + ", " : "0, ";
        data += stats.Discipline ? stats.Discipline.pct + ", " : "0, ";
        data += stats.Strength ? stats.Strength.pct + ", " : "0, ";
        data += stats.Intellect ? stats.Intellect.value + ", " : "0, ";
        data += stats.Discipline ? stats.Discipline.value + ", " : "0, ";
        data += stats.Strength ? stats.Strength.value + ", " : "0, ";
        data += ((item.dimInfo && item.dimInfo.notes) || '') + ", ";
        // if DB is out of date this can be null, can't hurt to be careful
        if (item.talentGrid) {
          data += buildNodeString(item.talentGrid.nodes);
        }
        data += "\n";
      });
      downloadCsv("destinyArmor", header + data);
    }

    function downloadWeapons(guns, nameMap) {
      var header = "Name, Tag, Tier, Type, Light, Dmg, Owner, % Leveled, Locked, Equipped, Year," +
        "AA, Impact, Range, Stability, ROF, Reload, Mag, Equip, " +
        "Notes, Nodes\n";
      var data = "";
      guns.forEach(function(gun) {
        data += gun.name + ", ";
        data += (gun.dimInfo.tag || '') + ", ";
        data += gun.tier + ", ";
        data += gun.typeName + ", ";
        data += gun.primStat.value + ", ";
        if (gun.dmg) {
          data += capitalizeFirstLetter(gun.dmg) + ", ";
        }
        else {
          data += "Kinetic, ";
        }
        data += nameMap[gun.owner] + ", ";
        data += (gun.percentComplete * 100).toFixed(0) + ", ";
        data += gun.locked + ", ";
        data += gun.equipped + ", ";
        data += gun.year + ", ";
        var stats = {
          aa: 0,
          impact: 0,
          range: 0,
          stability: 0,
          rof: 0,
          reload: 0,
          magazine: 0,
          equipSpeed: 0
        };
        gun.stats.forEach(function(stat) {
          switch (stat.statHash) {
          case 1345609583: // Aim Assist
            stats.aa = stat.value;
            break;
          case 4043523819: // Impact
            stats.impact = stat.value;
            break;
          case 1240592695: // Range
            stats.range = stat.value;
            break;
          case 155624089: // Stability
            stats.stability = stat.value;
            break;
          case 4284893193: // Rate of fire
            stats.rof = stat.value;
            break;
          case 4188031367: // Reload
            stats.reload = stat.value;
            break;
          case 3871231066: // Magazine
          case 925767036: // Energy
            stats.magazine = stat.value;
            break;
          case 943549884: // Equip Speed
            stats.equipSpeed = stat.value;
            break;
          }
        });
        data += stats.aa + ", ";
        data += stats.impact + ", ";
        data += stats.range + ", ";
        data += stats.stability + ", ";
        data += stats.rof + ", ";
        data += stats.reload + ", ";
        data += stats.magazine + ", ";
        data += stats.equipSpeed + ", ";
        data += ((gun.dimInfo && gun.dimInfo.notes) || '') + ", ";
        // haven't seen this null yet, but can't hurt to check since we saw it on armor above
        if (gun.talentGrid) {
          data += buildNodeString(gun.talentGrid.nodes);
        }
        data += "\n";
      });
      downloadCsv("destinyWeapons", header + data);
    }

    function downloadCsvFiles(stores, type) {
      // perhaps we're loading
      if (stores.length === 0){
        return;
      }
      var nameMap = {};
      var allItems = [];
      stores.forEach(function(store) {
        allItems = allItems.concat(store.items);
        if (store.id === "vault") {
          nameMap[store.id] = "Vault";
        }
        else {
          nameMap[store.id] = capitalizeFirstLetter(store.class) + "(" + store.powerLevel + ")";
        }
      });
      var items = [];
      allItems.forEach(function(item) {
        if (!item.primStat) {
          return;
        }
        if (type === "Weapons") {
          if (item.primStat.statHash === 368428387) {
            items.push(item);
          }
        }
        else if (type === "Armor") {
          if (item.primStat.statHash === 3897883278) {
            items.push(item);
          }
        }
      });
      if (type === "Weapons") {
        downloadWeapons(items, nameMap);
      }
      else {
        downloadArmor(items, nameMap);
      }
    }

    return {
      downloadCsvFiles: downloadCsvFiles
    };
  }
})();
<commit_msg>fix item quality %<commit_after>(function() {
  'use strict';

  angular.module('dimApp')
    .factory('dimCsvService', CsvService);

  function CsvService() {
    // step node names we'll hide, we'll leave "* Chroma" for now though, since we don't otherwise indicate Chroma
    var FILTER_NODE_NAMES = ["Upgrade Defense", "Ascend", "Infuse", "Increase Intellect", "Increase Discipline",
      "Increase Strength", "Twist Fate", "The Life Exotic", "Reforge Artifact", "Reforge Shell",
      "Deactivate Chroma", "Kinetic Damage", "Solar Damage", "Arc Damage", "Void Damage"];

    function capitalizeFirstLetter(string) {
      if (!string || string.length === 0) {
        return "";
      }
      return string.charAt(0).toUpperCase() + string.slice(1);
    }

    function downloadCsv(filename, csv) {
      filename = filename + ".csv";
      var pom = document.createElement('a');
      pom.setAttribute('href', 'data:text/csv;charset=utf-8,' + encodeURIComponent(csv));
      pom.setAttribute('download', filename);
      pom.click();
    }

    function buildNodeString(nodes) {
      var data = "";
      nodes.forEach(function(node) {
        if (_.contains(FILTER_NODE_NAMES, node.name)){
          return;
        }
        data += node.name;
        if (node.activated){
          data += "*";
        }
        data += ", ";
      });

      return data;
    }

    function downloadArmor(items, nameMap) {
      var header = "Name, Tag, Tier, Type, Equippable, Light, Owner, % Leveled, Locked, Equipped, Year, " +
        "% Quality, % IntQ, % DiscQ, % StrQ, Int, Disc, Str, Notes, Perks\n";
      var data = "";
      items.forEach(function(item) {
        data += item.name + ", ";
        data += (item.dimInfo.tag || '') + ", ";
        data += item.tier + ", ";
        data += item.typeName + ", ";
        var equippable = item.classTypeName;
        if (!equippable || equippable === "unknown") {
          equippable = "Any";
        }
        else {
          equippable = capitalizeFirstLetter(equippable);
        }
        data += equippable + ", ";
        data += item.primStat.value + ", ";
        data += nameMap[item.owner] + ", ";
        data += (item.percentComplete * 100).toFixed(0) + ", ";
        data += item.locked + ", ";
        data += item.equipped + ", ";
        data += item.year + ", ";
        data += item.quality ? item.quality.min + ", " : "0, ";
        var stats = {};
        if (item.stats) {
          item.stats.forEach(function(stat) {
            var pct = 0;
            if (stat.scaled && stat.scaled.min) {
              pct = Math.round(100 * stat.scaled.min / stat.split);
            }
            stats[stat.name] = {
              value: stat.value,
              pct: pct
            };
          });
        }
        data += stats.Intellect ? stats.Intellect.pct + ", " : "0, ";
        data += stats.Discipline ? stats.Discipline.pct + ", " : "0, ";
        data += stats.Strength ? stats.Strength.pct + ", " : "0, ";
        data += stats.Intellect ? stats.Intellect.value + ", " : "0, ";
        data += stats.Discipline ? stats.Discipline.value + ", " : "0, ";
        data += stats.Strength ? stats.Strength.value + ", " : "0, ";
        data += ((item.dimInfo && item.dimInfo.notes) || '') + ", ";
        // if DB is out of date this can be null, can't hurt to be careful
        if (item.talentGrid) {
          data += buildNodeString(item.talentGrid.nodes);
        }
        data += "\n";
      });
      downloadCsv("destinyArmor", header + data);
    }

    function downloadWeapons(guns, nameMap) {
      var header = "Name, Tag, Tier, Type, Light, Dmg, Owner, % Leveled, Locked, Equipped, Year," +
        "AA, Impact, Range, Stability, ROF, Reload, Mag, Equip, " +
        "Notes, Nodes\n";
      var data = "";
      guns.forEach(function(gun) {
        data += gun.name + ", ";
        data += (gun.dimInfo.tag || '') + ", ";
        data += gun.tier + ", ";
        data += gun.typeName + ", ";
        data += gun.primStat.value + ", ";
        if (gun.dmg) {
          data += capitalizeFirstLetter(gun.dmg) + ", ";
        }
        else {
          data += "Kinetic, ";
        }
        data += nameMap[gun.owner] + ", ";
        data += (gun.percentComplete * 100).toFixed(0) + ", ";
        data += gun.locked + ", ";
        data += gun.equipped + ", ";
        data += gun.year + ", ";
        var stats = {
          aa: 0,
          impact: 0,
          range: 0,
          stability: 0,
          rof: 0,
          reload: 0,
          magazine: 0,
          equipSpeed: 0
        };
        gun.stats.forEach(function(stat) {
          switch (stat.statHash) {
          case 1345609583: // Aim Assist
            stats.aa = stat.value;
            break;
          case 4043523819: // Impact
            stats.impact = stat.value;
            break;
          case 1240592695: // Range
            stats.range = stat.value;
            break;
          case 155624089: // Stability
            stats.stability = stat.value;
            break;
          case 4284893193: // Rate of fire
            stats.rof = stat.value;
            break;
          case 4188031367: // Reload
            stats.reload = stat.value;
            break;
          case 3871231066: // Magazine
          case 925767036: // Energy
            stats.magazine = stat.value;
            break;
          case 943549884: // Equip Speed
            stats.equipSpeed = stat.value;
            break;
          }
        });
        data += stats.aa + ", ";
        data += stats.impact + ", ";
        data += stats.range + ", ";
        data += stats.stability + ", ";
        data += stats.rof + ", ";
        data += stats.reload + ", ";
        data += stats.magazine + ", ";
        data += stats.equipSpeed + ", ";
        data += ((gun.dimInfo && gun.dimInfo.notes) || '') + ", ";
        // haven't seen this null yet, but can't hurt to check since we saw it on armor above
        if (gun.talentGrid) {
          data += buildNodeString(gun.talentGrid.nodes);
        }
        data += "\n";
      });
      downloadCsv("destinyWeapons", header + data);
    }

    function downloadCsvFiles(stores, type) {
      // perhaps we're loading
      if (stores.length === 0){
        return;
      }
      var nameMap = {};
      var allItems = [];
      stores.forEach(function(store) {
        allItems = allItems.concat(store.items);
        if (store.id === "vault") {
          nameMap[store.id] = "Vault";
        }
        else {
          nameMap[store.id] = capitalizeFirstLetter(store.class) + "(" + store.powerLevel + ")";
        }
      });
      var items = [];
      allItems.forEach(function(item) {
        if (!item.primStat) {
          return;
        }
        if (type === "Weapons") {
          if (item.primStat.statHash === 368428387) {
            items.push(item);
          }
        }
        else if (type === "Armor") {
          if (item.primStat.statHash === 3897883278) {
            items.push(item);
          }
        }
      });
      if (type === "Weapons") {
        downloadWeapons(items, nameMap);
      }
      else {
        downloadArmor(items, nameMap);
      }
    }

    return {
      downloadCsvFiles: downloadCsvFiles
    };
  }
})();
<|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 add<commit_msg>Five bullets or six?<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 adde<|endoftext|>


<commit_before>
/**
 * Module dependencies
 */
var tessel = require('tessel')
  , climate = require('climate-si7005').use(tessel.port.B)
  , duration = 1000
  , ip = require('os').networkInterfaces().en1[0].address
  , net = require('net')
  , port = 1337;

/**
 * Climate module
 */

// vars
var climateData = {
  humidity: 0,
  temperature: 0
};

// functions
var climateFunctions = {
  noop: function () {},
  read: function () {
    climate.readTemperature('f', function (err, temp) {
      if (err) return console.error(err);
      climateData.temperature = temp.toFixed(4);
      climate.readHumidity(function (err, humid) { // sync reads; async reads distort reporting on one or both values
        if (err) return console.error(err);
        climateData.humidity = humid.toFixed(4);
      });
    });
  },
  update: function () {}
};

// events
climate.on('error', function (err) {
 console.error('Climate module error', err);
 climateFunctions.update = climateFunctions.noop;
 climateData = { humidity: 0, temperature: 0 };
});
climate.on('ready', function () {
  console.log('Connected to si7005 climate module');
  climateFunctions.update = climateFunctions.read;
  climateFunctions.read();
});

/**
 * TCP server
 */

// vars
var connections = [];

// functions
var socketFunctions = {
  closeAll: function () {
    var i = connections.length;
    while (i--) connections[i].close();
    connections = [];
  },
  loop: function (socket) {
    climateFunctions.update();
    setTimeout(function () {
      socket.write(JSON.stringify(climateData) + '\r\n');
      socketFunctions[socket.id](socket);
    }, duration);
  },
  removeLoop: function (socket) {
    delete socketFunctions[socket.id];
  }
};

// events
var server = net.createServer(function (socket) {
  socketFunctions.closeAll(); // close other sockets; limit to one open connection
  socket.id = Math.round(new Date().getTime());
  connections.push(socket);
  socketFunctions[socket.id] = socketFunctions.loop;
  socketFunctions[socket.id](socket);
  console.log('socket connection ' + socket.id + ' opened');
  socket.on('close', function () {
    socketFunctions[socket.id] = socketFunctions.removeLoop;
    console.log('socket connection ' + socket.id + ' closed');
  });
  socket.on('data', function (data) {
    console.log(data.toString());
  });
});

server.listen(port, ip);
console.log('Tessel listening on: ', { ip: ip, port: port });
<commit_msg>correct spacing issue<commit_after>
/**
 * Module dependencies
 */
var tessel = require('tessel')
  , climate = require('climate-si7005').use(tessel.port.B)
  , duration = 1000
  , ip = require('os').networkInterfaces().en1[0].address
  , net = require('net')
  , port = 1337;

/**
 * Climate module
 */

// vars
var climateData = {
  humidity: 0,
  temperature: 0
};

// functions
var climateFunctions = {
  noop: function () {},
  read: function () {
    climate.readTemperature('f', function (err, temp) {
      if (err) return console.error(err);
      climateData.temperature = temp.toFixed(4);
      climate.readHumidity(function (err, humid) { // sync reads; async reads distort reporting on one or both values
        if (err) return console.error(err);
        climateData.humidity = humid.toFixed(4);
      });
    });
  },
  update: function () {}
};

// events
climate.on('error', function (err) {
  console.error('Climate module error', err);
  climateFunctions.update = climateFunctions.noop;
  climateData = { humidity: 0, temperature: 0 };
});
climate.on('ready', function () {
  console.log('Connected to si7005 climate module');
  climateFunctions.update = climateFunctions.read;
  climateFunctions.read();
});

/**
 * TCP server
 */

// vars
var connections = [];

// functions
var socketFunctions = {
  closeAll: function () {
    var i = connections.length;
    while (i--) connections[i].close();
    connections = [];
  },
  loop: function (socket) {
    climateFunctions.update();
    setTimeout(function () {
      socket.write(JSON.stringify(climateData) + '\r\n');
      socketFunctions[socket.id](socket);
    }, duration);
  },
  removeLoop: function (socket) {
    delete socketFunctions[socket.id];
  }
};

// events
var server = net.createServer(function (socket) {
  socketFunctions.closeAll(); // close other sockets; limit to one open connection
  socket.id = Math.round(new Date().getTime());
  connections.push(socket);
  socketFunctions[socket.id] = socketFunctions.loop;
  socketFunctions[socket.id](socket);
  console.log('socket connection ' + socket.id + ' opened');
  socket.on('close', function () {
    socketFunctions[socket.id] = socketFunctions.removeLoop;
    console.log('socket connection ' + socket.id + ' closed');
  });
  socket.on('data', function (data) {
    console.log(data.toString());
  });
});

server.listen(port, ip);
console.log('Tessel listening on: ', { ip: ip, port: port });
<|endoftext|>


<commit_before>(function() {
  'use strict';

  angular.module('dimApp')
    .factory('dimCsvService', CsvService);

  CsvService.$inject = ['dimStoreService'];

  function CsvService() {
    // step node names we'll hide, we'll leave "* Chroma" for now though, since we don't otherwise indicate Chroma
    var FILTER_NODE_NAMES = ["Upgrade Defense", "Ascend", "Infuse", "Increase Intellect", "Increase Discipline",
      "Increase Strength", "Twist Fate", "The Life Exotic", "Reforge Artifact", "Reforge Shell",
      "Deactivate Chroma", "Kinetic Damage", "Solar Damage", "Arc Damage", "Void Damage"];

    function capitalizeFirstLetter(string) {
      if (!string || string.length === 0) {
        return "";
      }
      return string.charAt(0).toUpperCase() + string.slice(1);
    }

    function downloadCsv(filename, csv) {
      filename = filename + ".csv";
      var pom = document.createElement('a');
      pom.setAttribute('href', 'data:text/csv;charset=utf-8,' + encodeURIComponent(csv));
      pom.setAttribute('download', filename);
      pom.click();
    }

    function buildNodeString(nodes) {
      var data = "";
      nodes.forEach(function(node) {
        if (_.contains(FILTER_NODE_NAMES, node.name)){
          return;
        }
        data += node.name;
        if (node.activated){
          data += "*";
        }
        data += ", ";
      });

      return data;
    }

    function downloadArmor(items, nameMap) {
      var header = "Name, Tier, Type, Equippable, Light, Owner, % Leveled, Locked, Equipped, " +
        "% Quality, % IntQ, % DiscQ, % StrQ, Int, Disc, Str, Perks\n";
      var data = "";
      items.forEach(function(item) {
        data += item.name + ", ";
        data += item.tier + ", ";
        data += item.typeName + ", ";
        var equippable = item.classTypeName;
        if (!equippable || equippable === "unknown") {
          equippable = "Any";
        }
        else {
          equippable = capitalizeFirstLetter(equippable);
        }
        data += equippable + ", ";
        data += item.primStat.value + ", ";
        data += nameMap[item.owner] + ", ";
        data += (item.percentComplete * 100).toFixed(0) + ", ";
        data += item.locked + ", ";
        data += item.equipped + ", ";
        data += item.quality ? item.quality.max + ", " : "0, ";
        var stats = {};
        item.stats.forEach(function(stat) {
          var pct = 0;
          if (stat.scaled && stat.scaled.min) {
            pct = Math.round(100 * stat.scaled.min / stat.split);
          }
          stats[stat.name] = {
            value: stat.value,
            pct: pct
          };
        });
        data += stats.Intellect ? stats.Intellect.pct + ", " : "0, ";
        data += stats.Discipline ? stats.Discipline.pct + ", " : "0, ";
        data += stats.Strength ? stats.Strength.pct + ", " : "0, ";
        data += stats.Intellect ? stats.Intellect.value + ", " : "0, ";
        data += stats.Discipline ? stats.Discipline.value + ", " : "0, ";
        data += stats.Strength ? stats.Strength.value + ", " : "0, ";
        // if DB is out of date this can be null, can't hurt to be careful
        if (item.talentGrid) {
          data += buildNodeString(item.talentGrid.nodes);
        }
        data += "\n";
      });
      downloadCsv("destinyArmor", header + data);
    }

    function downloadWeapons(guns, nameMap) {
      var header = "Name, Tier, Type, Light, Dmg, Owner, % Leveled, Locked, Equipped, " +
        "AA, Impact, Range, Stability, ROF, Reload, Mag, Equip, " +
        "Nodes\n";
      var data = "";
      guns.forEach(function(gun) {
        data += gun.name + ", ";
        data += gun.tier + ", ";
        data += gun.typeName + ", ";
        data += gun.primStat.value + ", ";
        if (gun.dmg) {
          data += capitalizeFirstLetter(gun.dmg) + ", ";
        }
        else {
          data += "Kinetic, ";
        }
        data += nameMap[gun.owner] + ", ";
        data += (gun.percentComplete * 100).toFixed(0) + ", ";
        data += gun.locked + ", ";
        data += gun.equipped + ", ";
        var stats = {
          aa: 0,
          impact: 0,
          range: 0,
          stability: 0,
          rof: 0,
          reload: 0,
          magazine: 0,
          equipSpeed: 0
        };
        gun.stats.forEach(function(stat) {
          switch (stat.name) {
          case 'Aim Assist':
            stats.aa = stat.value;
            break;
          case 'Impact':
            stats.impact = stat.value;
            break;
          case 'Range':
            stats.range = stat.value;
            break;
          case 'Stability':
            stats.stability = stat.value;
            break;
          case 'Rate of Fire':
            stats.rof = stat.value;
            break;
          case 'Reload':
            stats.reload = stat.value;
            break;
          case 'Magazine':
            stats.magazine = stat.value;
            break;
          case 'Equip Speed':
            stats.equipSpeed = stat.value;
            break;
          }
        });
        data += stats.aa + ", ";
        data += stats.impact + ", ";
        data += stats.range + ", ";
        data += stats.stability + ", ";
        data += stats.rof + ", ";
        data += stats.reload + ", ";
        data += stats.magazine + ", ";
        data += stats.equipSpeed + ", ";
        // haven't seen this null yet, but can't hurt to check since we saw it on armor above
        if (gun.talentGrid) {
          data += buildNodeString(gun.talentGrid.nodes);
        }
        data += "\n";
      });
      downloadCsv("destinyWeapons", header + data);
    }

    function downloadCsvFiles(stores, type) {
      // perhaps we're loading
      if (stores.length === 0){
        return;
      }
      var nameMap = {};
      var allItems = [];
      stores.forEach(function(store) {
        allItems = allItems.concat(store.items);
        if (store.id === "vault") {
          nameMap[store.id] = "Vault";
        }
        else {
          nameMap[store.id] = capitalizeFirstLetter(store.class) + "(" + store.powerLevel + ")";
        }
      });
      var items = [];
      allItems.forEach(function(item) {
        if (!item.primStat) {
          return;
        }
        if (type === "Weapons") {
          if (item.primStat.statHash === 368428387) {
            items.push(item);
          }
        }
        else if (type === "Armor") {
          if (item.primStat.statHash === 3897883278) {
            items.push(item);
          }
        }
      });
      if (type === "Weapons") {
        downloadWeapons(items, nameMap);
      }
      else {
        downloadArmor(items, nameMap);
      }
    }

    return {
      downloadCsvFiles: downloadCsvFiles
    };
  }
})();
<commit_msg>check for undefined stats in armor export<commit_after>(function() {
  'use strict';

  angular.module('dimApp')
    .factory('dimCsvService', CsvService);

  CsvService.$inject = ['dimStoreService'];

  function CsvService() {
    // step node names we'll hide, we'll leave "* Chroma" for now though, since we don't otherwise indicate Chroma
    var FILTER_NODE_NAMES = ["Upgrade Defense", "Ascend", "Infuse", "Increase Intellect", "Increase Discipline",
      "Increase Strength", "Twist Fate", "The Life Exotic", "Reforge Artifact", "Reforge Shell",
      "Deactivate Chroma", "Kinetic Damage", "Solar Damage", "Arc Damage", "Void Damage"];

    function capitalizeFirstLetter(string) {
      if (!string || string.length === 0) {
        return "";
      }
      return string.charAt(0).toUpperCase() + string.slice(1);
    }

    function downloadCsv(filename, csv) {
      filename = filename + ".csv";
      var pom = document.createElement('a');
      pom.setAttribute('href', 'data:text/csv;charset=utf-8,' + encodeURIComponent(csv));
      pom.setAttribute('download', filename);
      pom.click();
    }

    function buildNodeString(nodes) {
      var data = "";
      nodes.forEach(function(node) {
        if (_.contains(FILTER_NODE_NAMES, node.name)){
          return;
        }
        data += node.name;
        if (node.activated){
          data += "*";
        }
        data += ", ";
      });

      return data;
    }

    function downloadArmor(items, nameMap) {
      var header = "Name, Tier, Type, Equippable, Light, Owner, % Leveled, Locked, Equipped, " +
        "% Quality, % IntQ, % DiscQ, % StrQ, Int, Disc, Str, Perks\n";
      var data = "";
      items.forEach(function(item) {
        data += item.name + ", ";
        data += item.tier + ", ";
        data += item.typeName + ", ";
        var equippable = item.classTypeName;
        if (!equippable || equippable === "unknown") {
          equippable = "Any";
        }
        else {
          equippable = capitalizeFirstLetter(equippable);
        }
        data += equippable + ", ";
        data += item.primStat.value + ", ";
        data += nameMap[item.owner] + ", ";
        data += (item.percentComplete * 100).toFixed(0) + ", ";
        data += item.locked + ", ";
        data += item.equipped + ", ";
        data += item.quality ? item.quality.max + ", " : "0, ";
        var stats = {};
        if(item.stats) {
          item.stats.forEach(function(stat) {
            var pct = 0;
            if (stat.scaled && stat.scaled.min) {
              pct = Math.round(100 * stat.scaled.min / stat.split);
            }
            stats[stat.name] = {
              value: stat.value,
              pct: pct
            };
          });
        }
        data += stats.Intellect ? stats.Intellect.pct + ", " : "0, ";
        data += stats.Discipline ? stats.Discipline.pct + ", " : "0, ";
        data += stats.Strength ? stats.Strength.pct + ", " : "0, ";
        data += stats.Intellect ? stats.Intellect.value + ", " : "0, ";
        data += stats.Discipline ? stats.Discipline.value + ", " : "0, ";
        data += stats.Strength ? stats.Strength.value + ", " : "0, ";
        // if DB is out of date this can be null, can't hurt to be careful
        if (item.talentGrid) {
          data += buildNodeString(item.talentGrid.nodes);
        }
        data += "\n";
      });
      downloadCsv("destinyArmor", header + data);
    }

    function downloadWeapons(guns, nameMap) {
      var header = "Name, Tier, Type, Light, Dmg, Owner, % Leveled, Locked, Equipped, " +
        "AA, Impact, Range, Stability, ROF, Reload, Mag, Equip, " +
        "Nodes\n";
      var data = "";
      guns.forEach(function(gun) {
        data += gun.name + ", ";
        data += gun.tier + ", ";
        data += gun.typeName + ", ";
        data += gun.primStat.value + ", ";
        if (gun.dmg) {
          data += capitalizeFirstLetter(gun.dmg) + ", ";
        }
        else {
          data += "Kinetic, ";
        }
        data += nameMap[gun.owner] + ", ";
        data += (gun.percentComplete * 100).toFixed(0) + ", ";
        data += gun.locked + ", ";
        data += gun.equipped + ", ";
        var stats = {
          aa: 0,
          impact: 0,
          range: 0,
          stability: 0,
          rof: 0,
          reload: 0,
          magazine: 0,
          equipSpeed: 0
        };
        gun.stats.forEach(function(stat) {
          switch (stat.name) {
          case 'Aim Assist':
            stats.aa = stat.value;
            break;
          case 'Impact':
            stats.impact = stat.value;
            break;
          case 'Range':
            stats.range = stat.value;
            break;
          case 'Stability':
            stats.stability = stat.value;
            break;
          case 'Rate of Fire':
            stats.rof = stat.value;
            break;
          case 'Reload':
            stats.reload = stat.value;
            break;
          case 'Magazine':
            stats.magazine = stat.value;
            break;
          case 'Equip Speed':
            stats.equipSpeed = stat.value;
            break;
          }
        });
        data += stats.aa + ", ";
        data += stats.impact + ", ";
        data += stats.range + ", ";
        data += stats.stability + ", ";
        data += stats.rof + ", ";
        data += stats.reload + ", ";
        data += stats.magazine + ", ";
        data += stats.equipSpeed + ", ";
        // haven't seen this null yet, but can't hurt to check since we saw it on armor above
        if (gun.talentGrid) {
          data += buildNodeString(gun.talentGrid.nodes);
        }
        data += "\n";
      });
      downloadCsv("destinyWeapons", header + data);
    }

    function downloadCsvFiles(stores, type) {
      // perhaps we're loading
      if (stores.length === 0){
        return;
      }
      var nameMap = {};
      var allItems = [];
      stores.forEach(function(store) {
        allItems = allItems.concat(store.items);
        if (store.id === "vault") {
          nameMap[store.id] = "Vault";
        }
        else {
          nameMap[store.id] = capitalizeFirstLetter(store.class) + "(" + store.powerLevel + ")";
        }
      });
      var items = [];
      allItems.forEach(function(item) {
        if (!item.primStat) {
          return;
        }
        if (type === "Weapons") {
          if (item.primStat.statHash === 368428387) {
            items.push(item);
          }
        }
        else if (type === "Armor") {
          if (item.primStat.statHash === 3897883278) {
            items.push(item);
          }
        }
      });
      if (type === "Weapons") {
        downloadWeapons(items, nameMap);
      }
      else {
        downloadArmor(items, nameMap);
      }
    }

    return {
      downloadCsvFiles: downloadCsvFiles
    };
  }
})();
<|endoftext|>


<commit_before>||prizes.giftstoday.site/*
||wowmovie.me/main.js
||vjs.zencdn.net/ie8/*/*.js
||894de8bb784.com/929095/*
||123moviesfree.com/ip.temp/at_001/js/jquery.hover-intent.js
||123moviesfree.com/ip.temp/at_001/js/slide.min.js
||123moviesfree.com/ip.temp/at_001/js/ipmovies.min.js
||justdating.online/*
||894de8bb784.com/*
||srv.bebi.com/*
||dating-snap.com/*
||www.toonova.net/scripts/front/search.js
||www.mgid.com##[src^="data:"]
||gomovies.to##[src^="data:"]
||123movies.to##[src^="data:"]
||20a840a14a0ef7d6.com/*
||20a840a14a0ef7d6.com
||openload.co/assets/js/script.*
||openload.co/assets/js/license.5.js
||imgg-cdn.adskeeper.co.uk/*
||c.adskeeper.co.uk/*
||rlqvyqgjkxgx.com/*
||bet365.com/*
||104.198.61.4/*
||bet365.com/*
||static.addtoany.com/*
||jbjcv.com/*
||qrklxapy.bid/*
http://qrklxapy.bid/*
http://crtntrvjuun.bid/*
http://cdn.www1.bob-bob-bobble.com/*
https://streamango.com/assets/js/license.adbl.js
||http://rlqvyqgjkxgx.com/*
||mg.adskeeper.co.uk/*
||ajax.cloudflare.com/cdn-cgi/nexp/*
||static.beeg.com/js/ads/ads.js
!||static.beeg.com/cpl/2096.js
||www.toonova.net###M_AD
||www.toonova.net###Mad
||www.toonova.net###mini-announcement
||www.toonova.net###content > .info_block
||www.toonova.net###disqus_thread
||www.toonova.net###eps_blocks
||gomovies.to###main > .container > .main-detail.main-content > .main-category.main-content > .content-kuss
||https://onclkds.com/apu.php*
||openload.co###exoNativeWidget2554079
https://ads.exdynsrv.com/*
https://streamango.com/*
gomovies.to###sanygrA3SQkfVi7phvJ3EyQqu
ultra-vid.com###g367CB268B1094004A3689751E7AC568FFloatingBanner0
www.toonova.net###M_AD
||i.toonova.com/ab/300.php?url=http://i.toonova.com/ab/bar300anime.html
www.toonova.net###content > .info_block
gomovies.to###main > .container > .main-detail.main-content > .main-category.main-content > .content-kuss
openload.co###exoNativeWidget2554079
gostream.is###overlay-123plugin
openload.co###exoNativeWidget2554079
||https://go.padsdel.com/ajs.php
!||https://go.padsdel.com/*
!||go.padsdel.com/ajs.php^*
||www.facebook.com/plugins/comments.php
www.animetoon.org###mini-announcement
||www.animetoon.org/ads/sidebar_upper_1.html
||www.spendcrazy.net/ads/embed_ad_fl.html
www.animetoon.org###ads-banner-inner
www.animetoon.org###body > .s_right_col
www.animetoon.org###comments
www.animetoon.org###eps_blocks
www.animetoon.org###M_AD
www.animetoon.org###Mad
<commit_msg>updated flters<commit_after>||prizes.giftstoday.site/*
||wowmovie.me/main.js
||vjs.zencdn.net/ie8/*/*.js
||894de8bb784.com/929095/*
||123moviesfree.com/ip.temp/at_001/js/jquery.hover-intent.js
||123moviesfree.com/ip.temp/at_001/js/slide.min.js
||123moviesfree.com/ip.temp/at_001/js/ipmovies.min.js
||justdating.online/*
||894de8bb784.com/*
||srv.bebi.com/*
||dating-snap.com/*
||www.toonova.net/scripts/front/search.js
||www.mgid.com##[src^="data:"]
||gomovies.to##[src^="data:"]
||123movies.to##[src^="data:"]
||20a840a14a0ef7d6.com/*
||20a840a14a0ef7d6.com
||openload.co/assets/js/script.*
||openload.co/assets/js/license.5.js
||imgg-cdn.adskeeper.co.uk/*
||c.adskeeper.co.uk/*
https://c.disquscdn.com/*
||rlqvyqgjkxgx.com/*
||bet365.com/*
||104.198.61.4/*
||bet365.com/*
||static.addtoany.com/*
||jbjcv.com/*
||qrklxapy.bid/*
http://qrklxapy.bid/*
http://crtntrvjuun.bid/*
http://cdn.www1.bob-bob-bobble.com/*
https://streamango.com/assets/js/license.adbl.js
https://st.bebi.com/*
https://go.bebi.com/*
zedo.com/*
||http://rlqvyqgjkxgx.com/*
||mg.adskeeper.co.uk/*
||ajax.cloudflare.com/cdn-cgi/nexp/*
||static.beeg.com/js/ads/ads.js
!||static.beeg.com/cpl/2096.js
||www.toonova.net###M_AD
||www.toonova.net###Mad
||www.toonova.net###mini-announcement
||www.toonova.net###content > .info_block
||www.toonova.net###disqus_thread
||www.toonova.net###eps_blocks
||gomovies.to###main > .container > .main-detail.main-content > .main-category.main-content > .content-kuss
||https://onclkds.com/apu.php*
||openload.co###exoNativeWidget2554079
https://ads.exdynsrv.com/*
https://streamango.com/*
gomovies.to###sanygrA3SQkfVi7phvJ3EyQqu
ultra-vid.com###g367CB268B1094004A3689751E7AC568FFloatingBanner0
www.toonova.net###M_AD
||i.toonova.com/ab/300.php?url=http://i.toonova.com/ab/bar300anime.html
www.toonova.net###content > .info_block
gomovies.to###main > .container > .main-detail.main-content > .main-category.main-content > .content-kuss
openload.co###exoNativeWidget2554079
gostream.is###overlay-123plugin
openload.co###exoNativeWidget2554079
||https://go.padsdel.com/ajs.php
!||https://go.padsdel.com/*
!||go.padsdel.com/ajs.php^*
||www.facebook.com/plugins/comments.php
www.animetoon.org###mini-announcement
||www.animetoon.org/ads/sidebar_upper_1.html
||www.spendcrazy.net/ads/embed_ad_fl.html
www.animetoon.org###ads-banner-inner
www.animetoon.org###body > .s_right_col
www.animetoon.org###comments
www.animetoon.org###eps_blocks
www.animetoon.org###M_AD
www.animetoon.org###Mad
www.youtube.com###player-ads
www.youtube.com###masthead-ad
www5.animesubhd.net###main > .section:nth-of-type(2)
www5.animesubhd.net###main > .section > .section-content > .sponsor-box
www.animetoon.org###BB_SK_4922130028
animetoon.org###M_AD
animetoon.org###ads-banner-inner
animetoon.org###Mad
animetoon.org###mini-announcement
animetoon.org###body > .s_right_col
www.mangabb.co###BB_SK_4968885002
www.mangabb.co###mini-announcement
www.mangabb.co###home_sidebar:nth-of-type(2)
www.mangabb.co###home_social
animetoon.org###BB_SK_2674014850
ww3.gogoanime.io###BB_SK_4491291615
ww3.gogoanime.io###wrapper_bg > .content > .content_left > .main_body:nth-of-type(1) > .anime_video_body > .anime_video_body_comment
ww3.gogoanime.io###BB_SK_8926345353

! 9/26/2017, 8:42:29 PM http://kimcartoon.me/Cartoon/Teenage-Mutant-Ninja-Turtles-2012-Season-5/Episode-11-12-13?id=77080
kimcartoon.me###myContainer > .w-right

! 9/26/2017, 8:42:56 PM http://kimcartoon.me/Cartoon/Teenage-Mutant-Ninja-Turtles-2012-Season-5/Episode-11-12-13?id=77080
kimcartoon.me###myContainer > .kcAds

! 9/26/2017, 8:43:10 PM http://kimcartoon.me/Cartoon/Teenage-Mutant-Ninja-Turtles-2012-Season-5/Episode-11-12-13?id=77080
kimcartoon.me###containerRoot > div:nth-of-type(7)

! 9/26/2017, 8:43:26 PM http://kimcartoon.me/Cartoon/Teenage-Mutant-Ninja-Turtles-2012-Season-5/Episode-11-12-13?id=77080
kimcartoon.me###divComments

! 9/26/2017, 8:43:42 PM http://kimcartoon.me/Cartoon/Teenage-Mutant-Ninja-Turtles-2012-Season-5/Episode-11-12-13?id=77080
kimcartoon.me###myContainer > .w-left > .clsTempMSg

! 9/26/2017, 8:44:20 PM http://kimcartoon.me/Cartoon/Teenage-Mutant-Ninja-Turtles-2012-Season-5/Episode-11-12-13?id=77080
kimcartoon.me###head > h3

! 9/26/2017, 8:44:58 PM http://kimcartoon.me/Cartoon/Teenage-Mutant-Ninja-Turtles-2012-Season-5/Episode-11-12-13?id=77080
kimcartoon.me###hideAds[href="javascript:void(0)"]

www.youtube.com###movie_player > .ytp-share-button-visible.ytp-chrome-top > .ytp-chrome-top-buttons
<|endoftext|>


<commit_before>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  console.info("Writing from %s to %s, with up to %s seconds between commits", inFile, outFile, max_sleep)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    console.info("Bytes: %s", length)
    try {
      child_process.execFileSync('/usr/bin/git', ['add', outFile])
    } catch (e) {
      console.error("Couldn't add %s to git: %s", outFile, e)
    }
    var args = ['commit', outFile, '-m', 'Update character']
    for (var counter = 0; counter < len<commit_msg>Update character<commit_after>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  console.info("Writing from %s to %s, with up to %s seconds between commits", inFile, outFile, max_sleep)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    console.info("Bytes: %s", length)
    try {
      child_process.execFileSync('/usr/bin/git', ['add', outFile])
    } catch (e) {
      console.error("Couldn't add %s to git: %s", outFile, e)
    }
    var args = ['commit', outFile, '-m', 'Update character']
    for (var counter = 0; counter < leng<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

// application config
var config = require('./config.json');

var app         = require('express')(),
    http        = require('http').Server(app),
    cors        = require('cors'),
    bodyParser  = require('body-parser');

var request         = require('request'),
    extend          = require('util')._extend,
    cliOptions 	    = require('commander'),
    fs              = require('fs'),
    pathUtil        = require('path'),
    execSync        = require('child_process').execSync,
    when            = require("promised-io/promise").when;

var validateToken 	= require('./lib/validateToken.js');

cliOptions.version('0.0.1')
           .option('-d, --dev', 'Developer mode; this option attempts to use a token in the file: dev-user-token')
           .parse(process.argv);

// if --dev option is used, set token to token in file "./dev-user-token"
// otherwise, pass on token, if there is one
if (cliOptions.dev) {
    let token = fs.readFileSync('dev-user-token', 'utf8').trim();
    console.log('\n\x1b[36m'+'using development token:'+'\x1b[0m', token, '\n')

    app.all('/', (req, res, next) => {
        req.headers = {"Authorization": token};
        next();
    }).use((req, res, next) => {
        req.headers = {"Authorization": token};
        next();
    })
}

// Configure CORs and body parser.
app.use( cors() )
   .use( bodyParser.urlencoded({extended: false, limit: '50mb'}) );

// Configure Logging
app.use( (req, res, next) => {
    console.log('%s %s', req.method, req.url);
    next();
});

/**
 * @api {get} /list/:path list files/folders in path
 * @apiName list
 *
 * @apiParam {string} path path to directory
 * @apiParam {string} ?type=(folders|files) only fetch folders or files
 *
 * @apiSampleRequest /list/my/models/
 *
 * @apiSuccess {json} meta metadata for listed objects
 * @apiSuccessExample {json} Success-Response:
 *     HTTP/1.1 200 OK
 *      [
 *       {
 *           name: "blue-panda",
 *           mtime: 1459822597000,
 *           size: 476,
 *           is_dir: true
 *       }, {
 *           name: "blue-zebra",
  *          mtime: 1458347601000,
 *           size: 170,
 *           is_dir: true
 *       }
 *     ]
 */
app.get('/v0/list/*', AuthRequired, (req, res) => {
    console.log('req.user.id', req.user.id)
    const opts = req.query;

    const rootDir = config.ftpRoot,
          path = '/'+req.params[0],
          fullPath = rootDir+path;

    let files = [];
    fs.readdirSync(fullPath).forEach( (file) => {
        let filePath = pathUtil.join(fullPath, file),
            stats = fs.statSync(filePath),
            isDir = stats.isDirectory();

        if (opts.type === 'file' && isDir) return;
        if (opts.type === 'folder' && !isDir) return;

        let fileObj = {
            name: file,
            path: path+'/'+file,
            mtime: stats.mtime.getTime(),
            size: stats.size,
            nlink: stats.nlink - 2
        }

        // additional info if is directory
        if (isDir) {
            fileObj.isFolder = true;
            fileObj.folderCount = parseInt( execSync("find "+
                filePath+" -maxdepth 1 -type d | wc -l").toString() ) - 1;
        }

        files.push(fileObj)
    });

    res.send(files)
})

/**
 * @api {get} /quota get spaced used and total allowed for user (not in use!)
 * @apiName quota
 *
 *
 * @apiSampleRequest /quota/
 *
 * @apiSuccess {json} meta give used/available space for user
 * @apiSuccessExample {json} Success-Response:
 *     HTTP/1.1 200 OK
 *     {
 *        used: used,
 *        available: 1000000
 *     }
 */
/*
.get('/v0/quota', AuthRequired, (req, res) => {
    const opts = req.query;
    const userDir = '/Users/nc/';

    let str = execSync("du -s "+userDir).toString(),
        e = str.indexOf('\t');
    let used = parseInt(str.slice(0, e));

    let quota = {
        used: used,
        available: 1000000
    }

    res.send(quota);
})
*/

/**
 * @api {get} /test-service/  Way to test simple, unauthenticated GET request.
 *  Note: no version "v0", "v1", etc, in the endpoint.
 *
 * @apiName test-service
 *
 * @apiSampleRequest /test-server/
 *
 * @apiSuccess {json} string Should return code 200 with string
 *  "This is just a test. This is only a test."
 *
 */
.get('/test-service', (req, res) => {
    res.status(200).send( 'This is just a test. This is only a test.' );
})

function AuthRequired(req, res, next) {
    // if no token at all, return 401
    if (!('authorization' in req.headers)) {
        res.status(401).send( {error: 'Auth is required!'} );
    }

    when(validateToken(req.headers.authorization),
        userObj => {
            if (!(userObj && 'id' in userObj)) {
                res.status(401).send( {error: 'Invalid token!'} );
                return;
            }

            // Pass user id along
            req.user = userObj;

            // safe to move along.
            next();
        })
}

var server = http.listen(3000, () => {
    var host = server.address().address;
    var port = server.address().port;

    console.log('Example app listening at http://%s:%s', host, port);
});
<commit_msg>fix folder count bug<commit_after>#!/usr/bin/env node
'use strict';

// application config
var config = require('./config.json');

var app         = require('express')(),
    http        = require('http').Server(app),
    cors        = require('cors'),
    bodyParser  = require('body-parser');

var request         = require('request'),
    extend          = require('util')._extend,
    cliOptions 	    = require('commander'),
    fs              = require('fs'),
    pathUtil        = require('path'),
    execSync        = require('child_process').execSync,
    when            = require("promised-io/promise").when;

var validateToken 	= require('./lib/validateToken.js');

cliOptions.version('0.0.1')
           .option('-d, --dev', 'Developer mode; this option attempts to use a token in the file: dev-user-token')
           .parse(process.argv);

// if --dev option is used, set token to token in file "./dev-user-token"
// otherwise, pass on token, if there is one
if (cliOptions.dev) {
    let token = fs.readFileSync('dev-user-token', 'utf8').trim();
    console.log('\n\x1b[36m'+'using development token:'+'\x1b[0m', token, '\n')

    app.all('/', (req, res, next) => {
        req.headers = {"Authorization": token};
        next();
    }).use((req, res, next) => {
        req.headers = {"Authorization": token};
        next();
    })
}

// Configure CORs and body parser.
app.use( cors() )
   .use( bodyParser.urlencoded({extended: false, limit: '50mb'}) );

// Configure Logging
app.use( (req, res, next) => {
    console.log('%s %s', req.method, req.url);
    next();
});

/**
 * @api {get} /list/:path list files/folders in path
 * @apiName list
 *
 * @apiParam {string} path path to directory
 * @apiParam {string} ?type=(folders|files) only fetch folders or files
 *
 * @apiSampleRequest /list/my/models/
 *
 * @apiSuccess {json} meta metadata for listed objects
 * @apiSuccessExample {json} Success-Response:
 *     HTTP/1.1 200 OK
 *      [
 *       {
 *           name: "blue-panda",
 *           mtime: 1459822597000,
 *           size: 476,
 *           is_dir: true
 *       }, {
 *           name: "blue-zebra",
  *          mtime: 1458347601000,
 *           size: 170,
 *           is_dir: true
 *       }
 *     ]
 */
app.get('/v0/list/*', AuthRequired, (req, res) => {
    console.log('req.user.id', req.user.id)
    const opts = req.query;

    const rootDir = config.ftpRoot,
          path = '/'+req.params[0],
          fullPath = rootDir+path;

    let files = [];
    fs.readdirSync(fullPath).forEach( (file) => {
        let filePath = pathUtil.join(fullPath, file),
            stats = fs.statSync(filePath),
            isDir = stats.isDirectory();

        if (opts.type === 'file' && isDir) return;
        if (opts.type === 'folder' && !isDir) return;

        let fileObj = {
            name: file,
            path: path+'/'+file,
            mtime: stats.mtime.getTime(),
            size: stats.size
        }

        // additional info if is directory
        if (isDir) {
            fileObj.isFolder = true;
            fileObj.folderCount = parseInt( execSync('find "' +
                filePath+'" -maxdepth 1 -type d | wc -l').toString() ) - 1;
        }

        files.push(fileObj)
    });

    res.send(files)
})

/**
 * @api {get} /quota get spaced used and total allowed for user (not in use!)
 * @apiName quota
 *
 *
 * @apiSampleRequest /quota/
 *
 * @apiSuccess {json} meta give used/available space for user
 * @apiSuccessExample {json} Success-Response:
 *     HTTP/1.1 200 OK
 *     {
 *        used: used,
 *        available: 1000000
 *     }
 */
/*
.get('/v0/quota', AuthRequired, (req, res) => {
    const opts = req.query;
    const userDir = '/Users/nc/';

    let str = execSync("du -s "+userDir).toString(),
        e = str.indexOf('\t');
    let used = parseInt(str.slice(0, e));

    let quota = {
        used: used,
        available: 1000000
    }

    res.send(quota);
})
*/

/**
 * @api {get} /test-service/  Way to test simple, unauthenticated GET request.
 *  Note: no version "v0", "v1", etc, in the endpoint.
 *
 * @apiName test-service
 *
 * @apiSampleRequest /test-server/
 *
 * @apiSuccess {json} string Should return code 200 with string
 *  "This is just a test. This is only a test."
 *
 */
.get('/test-service', (req, res) => {
    res.status(200).send( 'This is just a test. This is only a test.' );
})

function AuthRequired(req, res, next) {
    // if no token at all, return 401
    if (!('authorization' in req.headers)) {
        res.status(401).send( {error: 'Auth is required!'} );
    }

    when(validateToken(req.headers.authorization),
        userObj => {
            if (!(userObj && 'id' in userObj)) {
                res.status(401).send( {error: 'Invalid token!'} );
                return;
            }

            // Pass user id along
            req.user = userObj;

            // safe to move along.
            next();
        })
}

var server = http.listen(3000, () => {
    var host = server.address().address;
    var port = server.address().port;

    console.log('Example app listening at http://%s:%s', host, port);
});
<|endoftext|>


<commit_before>/*
 Copyright 2016 Mozilla

 Licensed under the Apache License, Version 2.0 (the "License"); you may not use
 this file except in compliance with the License. You may obtain a copy of the
 License at http://www.apache.org/licenses/LICENSE-2.0
 Unless required by applicable law or agreed to in writing, software distributed
 under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 CONDITIONS OF ANY KIND, either express or implied. See the License for the
 specific language governing permissions and limitations under the License.
 */

/**
 * The storage module that makes up the majority of the profile service.
 * @module ProfileStorage
 */

import Immutable from 'immutable';
import fs from 'fs-promise';
import microtime from 'microtime-fast';
import path from 'path';
import escaper from 'true-html-escape';
import { DB, verbose } from 'promise-sqlite';

import { Bookmark } from '../../shared/model';
import { ProfileStorageSchemaV5 } from './profile-schema';
import { SessionEndReason, SessionStartReason, SnippetSize, StarOp, VisitType } from './storage';
import { logger } from '../../shared/logging';

/**
 * Public API:
 *
 *   let storage = await ProfileStorage.open(dir);
 *   let session = await storage.startSession(scope, ancestor, reason);
 *   await storage.visit(url, session, microseconds);
 *   await storage.endSession(session, microseconds, reason);
 *   let visitedURLs = await storage.visited(since, limit);
 *   await storage.close();
 */
export class ProfileStorage {
  constructor(db) {
    this.db = db;
    this.places = new Map();
    this.nextPlace = 0;
  }

  /**
   * Open a database and return a promise-based wrapper.
   * @param dir the containing directory.
   * @returns {Promise}
   */
  static async open(dir) {
    verbose();

    const filePath = path.join(dir, 'browser.db');

    await fs.mkdirp(dir);
    const db = await DB.open(filePath, { logger });
    return new ProfileStorage(db).init();
  }

  close() {
    this.places.clear();
    this.nextPlace = 0;
    return this.db.close();
  }

  /**
   * Load the place map out of the DB.
   */
  async loadPlaces() {
    this.places.clear();

    const rows = await this.db.all('SELECT url, id FROM placeEvents');
    let max = 0;
    for (const row of rows) {
      const id = row.id;
      if (id > max) {
        max = id;
      }
      this.places.set(row.url, id);
    }

    this.nextPlace = max + 1;
    return max;
  }

  async init(schema = new ProfileStorageSchemaV5()) {
    await this.db.exec('PRAGMA foreign_keys = ON');
    const v = await schema.createOrUpdate(this);

    if (v !== schema.version) {
      throw new Error(`Incorrect version ${v}; expected ${schema.version}.`);
    }

    await this.loadPlaces();

    return this;
  }

  /**
   * Saves a place, expecting a transaction to be established if necessary.
   * Does not update `this.places`! Callers should do that once the entire
   * transaction completes.
   *
   * If the URL is already known, returns the place ID immediately.
   *
   * @param url the URL to store.
   * @param now the current timestamp.
   * @returns {*} the place ID.
   */
  async savePlaceWithoutEstablishingTransaction(url, now) {
    if (this.places.has(url)) {
      return this.places.get(url);
    }

    // We don't use INSERT OR IGNORE -- we want to fail if our write-through cache breaks.
    // We also aggressively increment, so any race here is less likely to cause problems.
    const id = this.nextPlace++;
    const args = [id, url, now];
    await this.db.run('INSERT INTO placeEvents (id, url, ts) VALUES (?, ?, ?)', args);
    return id;
  }

  savePlace(url, now = microtime.now()) {
    const place = this.places.get(url);
    if (place) {
      return Promise.resolve(place);
    }

    return this.savePlaceWithoutEstablishingTransaction(url, now)
               .then(id => this.savePlaceIDMapping(url, id));
  }

  /**
   * Store a visit for a given place ID. Chains through the place ID.
   */
  saveVisitForPlaceWithoutEstablishingTransaction(place, session, now = microtime.now()) {
    return this.db
               .run('INSERT INTO visitEvents (place, session, ts, type) VALUES (?, ?, ?, ?)',
                 [place, session, now, VisitType.unknown])
               .then(() => Promise.resolve(place));
  }

  /**
   * Store a title for a given place ID. Chains through the place ID. Writes nothing if the
   * title is undefined.
   */
  saveTitleForPlaceWithoutEstablishingTransaction(place, title,
                                                  now = microtime.now()) {
    if (title === undefined) {
      return Promise.resolve(place);
    }

    return this.db
               .run('INSERT INTO titleEvents (place, title, ts) VALUES (?, ?, ?)',
                 [place, title, now])
               .then(() => Promise.resolve(place));
  }

  savePlaceIDMapping(url, id) {
    this.places.set(url, id);
    return Promise.resolve(id);
  }

  async startSession(scope, ancestor, now = microtime.now(), reason = SessionStartReason.newTab) {
    const result =
      await this.db
                .run('INSERT INTO sessionStarts (scope, ancestor, ts, reason) VALUES (?, ?, ?, ?)',
                  [scope, ancestor, now, reason]);
    return result.lastID;
  }

  async endSession(session, now = microtime.now(), reason = SessionEndReason.tabClosed) {
    const result =
      await this.db
                .run('INSERT INTO sessionEnds (id, ts, reason) VALUES (?, ?, ?)',
                  [session, now, reason]);
    return result.lastID;
  }

  async materializeStars() {
    await this.db.run('DELETE FROM mStarred');
    await this.db.run('INSERT INTO mStarred SELECT place, ts, url FROM vStarred');
  }

  async materializeHistory() {
    await this.db.run('DELETE FROM mHistory');
    await this.db.run(`INSERT INTO mHistory
      SELECT place, url, lastTitle, lastVisited, visitCount
      FROM vHistory
    `);
  }

  async rematerialize() {
    await this.materializeHistory();
    await this.materializeStars();
  }

  /**
   * Update the contents of the `mStarred` materialized view in response to a starring change.
   * This implementation depends on the ordering of star/unstar events being strictly:
   *
   *   1, -1, 1, -1, 1, -1, …
   *
   * Any other order will result in inconsistency. An alternative implementation is to do a partial
   * rebuild -- exactly the normal materialization query, but with a `WHERE place = ?` clause added.
   */
  updateMaterializedStars(url, place, session, action, now = microtime.now()) {
    switch (action) {
      case StarOp.star:
        return this.db.run('INSERT OR REPLACE INTO mStarred (place, ts, url) VALUES (?, ?, ?)',
          [place, now, url]);
      case StarOp.unstar:
        return this.db.run('DELETE FROM mStarred WHERE place = ?', [place]);

      default:
        return Promise.reject(new Error(`Unknown action ${action}.`));
    }
  }

  // Chains through place ID.
  starPage(url, session, action, now = microtime.now()) {
    if (action !== StarOp.star && action !== StarOp.unstar) {
      return Promise.reject(new Error(`Unknown action ${action}.`));
    }

    const star = place =>
      this.db
          .run('INSERT INTO starEvents (place, session, action, ts) VALUES (?, ?, ?, ?)',
            [place, session, action, now])

          // Update materialized views.
          .then(() => this.updateMaterializedStars(url, place, session, action, now))
          .then(() => Promise.resolve(place));

    const place = this.places.get(url);
    if (place) {
      return star(place);
    }

    return this.db.inTransaction(() =>
                 this.savePlaceWithoutEstablishingTransaction(url, now)
                     .then(star))

               // If the transaction committed, keep the URL -> ID mapping in memory.
               .then(id => this.savePlaceIDMapping(url, id));
  }

  async recordVisitWithoutEstablishingTransaction(url, session, title, now = microtime.now()) {
    const place = await this.savePlaceWithoutEstablishingTransaction(url, now);
    await this.saveVisitForPlaceWithoutEstablishingTransaction(place, session, now);
    await this.saveTitleForPlaceWithoutEstablishingTransaction(place, title, now);

    // Update the materialized view.
    // This assumes uniqueness of events, which is true so long as we're conflating these
    // API calls and the generation of events themselves.
    // We can't just use UPDATE here because we can't trust that `this.places` implies
    // presence in `mHistory` -- we also use it for bookmarks -- so UPDATE OR INSERT.
    const result = await this.db.run(`
      UPDATE mHistory
      SET lastVisited = ?, visitCount = visitCount + 1, lastTitle = ?
      WHERE place = ?`,
      [now, title, place]);

    if (result.changes === 0) {
      await this.db.run(`INSERT INTO mHistory
        (place, lastTitle, url, lastVisited, visitCount)
        VALUES (?, ?, ?, ?, ?)`,
        [place, title, url, now, 1]);
    }

    return place;
  }

  /**
   * Record a visit to a URL.
   * @param url the visited URL.
   * @param title (optional) a known title for the page.
   * @param now (optional) microsecond timestamp.
   * @returns {Promise} a promise that resolves to the place ID.
   */
  visit(url, session, title, now = microtime.now()) {
    return this.db.inTransaction(() =>
      this.recordVisitWithoutEstablishingTransaction(url, session, title, now)

          // If the transaction committed, keep the URL -> ID mapping in memory.
          .then(place => this.savePlaceIDMapping(url, place)));
  }

  collectURLs(rows) {
    const out = [];
    for (const row of rows) {
      out.push(row.url);
    }
    return out;
  }

  /**
   * Search for places that match title, URL, or saved content
   * since the specified timestamp.
   *
   * Snippets in the returned results are guaranteed to be HTML-escaped.
   * They will include `<b>` `</b>` tags around matches.
   *
   * @param string The substring to match. Not tokenized.
   * @param since The earliest visit time in microseconds.
   * @param limit The maximum number of results to return.
   * @returns {Promise<[AwesomebarMatch]>}
   */
  async query(string, since = 0, limit = 10, snippetSize = SnippetSize.medium) {
    const contentMatches = `
    SELECT p.id AS place,
           p.url AS uri,
           pages.title AS title,
           snippet(pages, '╞', '╡', '…', -1, ?) AS snippet,
           pages.ts AS lastVisited
    FROM placeEvents AS p JOIN pages ON p.id = pages.place
    WHERE pages.ts > ? AND pages MATCH ?
    `;

    const visitMatches = `
    SELECT place,
           url AS uri,
           lastTitle AS title,
           NULL AS snippet,
           lastVisited
    FROM mHistory
    WHERE lastVisited > ? AND (
      lastTitle LIKE ? OR url LIKE ?
    )`;

    const query = `
    SELECT place,
           uri,
           MAX(title) AS title,
           MAX(snippet) AS snippet,
           MAX(lastVisited) AS lastVisited
    FROM (${contentMatches} UNION ${visitMatches})
    GROUP BY place
    ORDER BY lastVisited DESC
    LIMIT ?`;

    const like = `%${string}%`;
    const rows = await this.db.all(query, [snippetSize, since, string, since, like, like, limit]);

    // We escape on the way out, not the way in, for two reasons:
    // 1. We can't rely on the retrieved content correctly including the entirety of an escaped
    //    character like '&'.
    // 2. We'd like to be able to match on the unescaped text.
    function escape(s) {
      if (!s) {
        return s;
      }

      return escaper.escape(s)
                    .replace(/╞/g, '<b>')
                    .replace(/╡/g, '</b>');
    }

    rows.forEach(row => {
      row.snippet = escape(row.snippet);
    });
    return rows;
  }

  async visitedMatches(substring, since = 0, limit = 10) {
    const like = `%${substring}%`;

    // Fetch all places visited, with the latest timestamp for each.
    // TODO: search historical titles?
    const query = `SELECT * FROM mHistory
      WHERE lastVisited > ? AND (
        lastTitle LIKE ? OR url LIKE ?
      )
      ORDER BY lastVisited DESC
      LIMIT ?
    `;

    return this.collectURLs(await this.db.all(query, [since, like, like, limit]));
  }

  // Ordered by last visit, descending.
  async visited(since = 0, limit = 10) {
    // Fetch all places visited, with the latest timestamp for each.
    const query = `
      SELECT h.place AS place,
             h.url AS uri,
             h.lastTitle AS title,
             pages.excerpt AS snippet,
             h.lastVisited AS lastVisited
      FROM mHistory AS h LEFT JOIN pages ON pages.place = h.place
      WHERE h.lastVisited > ?
      GROUP BY h.place
      ORDER BY h.lastVisited DESC
      LIMIT ?
    `;

    return await this.db.all(query, [since, limit]);
  }

  async getStarredWithOrderByAndLimit(newestFirst, limit) {
    const orderClause = newestFirst ? 'ORDER BY s.ts DESC' : '';
    let limitClause;
    let args;
    if (limit) {
      limitClause = 'LIMIT ?';
      args = [limit];
    } else {
      limitClause = '';
      args = [];
    }

    const fromMaterialized = `
    SELECT s.place AS place, t.title AS title, s.ts AS ts, s.url AS url
    FROM mStarred AS s LEFT JOIN vTitles AS t ON s.place = t.place
    ${orderClause} ${limitClause}
    `;

    return await this.db.all(fromMaterialized, args);
  }

  async starredURLs(limit = undefined) {
    // Fetch all places visited, with the latest timestamp for each.
    const rows = await this.getStarredWithOrderByAndLimit(false, limit);
    return Immutable.Set(rows.map(row => row.url));
  }

  async recentlyStarred(limit = 5) {
    const rows = await this.getStarredWithOrderByAndLimit(true, limit);
    return rows.map(row =>
      new Bookmark({
        title: row.title, location: row.url, visitedAt: row.ts,
      }));
  }

  async savePage(page, session, now = microtime.now()) {
    const place = await this.savePlace(page.uri, now);
    const query = `
    INSERT INTO pages (place, session, ts, title, excerpt, content)
    VALUES (?, ?, ?, ?, ?, ?)
    `;
    return this.db.run(query, [place, session, now, page.title, page.excerpt, page.textContent]);
  }

  userVersion() {
    return this.db
               .get('PRAGMA user_version')
               .then(row => Promise.resolve(row.user_version));
  }
}
<commit_msg>Pre: Return less to clients in query.<commit_after>/*
 Copyright 2016 Mozilla

 Licensed under the Apache License, Version 2.0 (the "License"); you may not use
 this file except in compliance with the License. You may obtain a copy of the
 License at http://www.apache.org/licenses/LICENSE-2.0
 Unless required by applicable law or agreed to in writing, software distributed
 under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 CONDITIONS OF ANY KIND, either express or implied. See the License for the
 specific language governing permissions and limitations under the License.
 */

/**
 * The storage module that makes up the majority of the profile service.
 * @module ProfileStorage
 */

import Immutable from 'immutable';
import fs from 'fs-promise';
import microtime from 'microtime-fast';
import path from 'path';
import escaper from 'true-html-escape';
import { DB, verbose } from 'promise-sqlite';

import { Bookmark } from '../../shared/model';
import { ProfileStorageSchemaV5 } from './profile-schema';
import { SessionEndReason, SessionStartReason, SnippetSize, StarOp, VisitType } from './storage';
import { logger } from '../../shared/logging';

/**
 * Public API:
 *
 *   let storage = await ProfileStorage.open(dir);
 *   let session = await storage.startSession(scope, ancestor, reason);
 *   await storage.visit(url, session, microseconds);
 *   await storage.endSession(session, microseconds, reason);
 *   let visitedURLs = await storage.visited(since, limit);
 *   await storage.close();
 */
export class ProfileStorage {
  constructor(db) {
    this.db = db;
    this.places = new Map();
    this.nextPlace = 0;
  }

  /**
   * Open a database and return a promise-based wrapper.
   * @param dir the containing directory.
   * @returns {Promise}
   */
  static async open(dir) {
    verbose();

    const filePath = path.join(dir, 'browser.db');

    await fs.mkdirp(dir);
    const db = await DB.open(filePath, { logger });
    return new ProfileStorage(db).init();
  }

  close() {
    this.places.clear();
    this.nextPlace = 0;
    return this.db.close();
  }

  /**
   * Load the place map out of the DB.
   */
  async loadPlaces() {
    this.places.clear();

    const rows = await this.db.all('SELECT url, id FROM placeEvents');
    let max = 0;
    for (const row of rows) {
      const id = row.id;
      if (id > max) {
        max = id;
      }
      this.places.set(row.url, id);
    }

    this.nextPlace = max + 1;
    return max;
  }

  async init(schema = new ProfileStorageSchemaV5()) {
    await this.db.exec('PRAGMA foreign_keys = ON');
    const v = await schema.createOrUpdate(this);

    if (v !== schema.version) {
      throw new Error(`Incorrect version ${v}; expected ${schema.version}.`);
    }

    await this.loadPlaces();

    return this;
  }

  /**
   * Saves a place, expecting a transaction to be established if necessary.
   * Does not update `this.places`! Callers should do that once the entire
   * transaction completes.
   *
   * If the URL is already known, returns the place ID immediately.
   *
   * @param url the URL to store.
   * @param now the current timestamp.
   * @returns {*} the place ID.
   */
  async savePlaceWithoutEstablishingTransaction(url, now) {
    if (this.places.has(url)) {
      return this.places.get(url);
    }

    // We don't use INSERT OR IGNORE -- we want to fail if our write-through cache breaks.
    // We also aggressively increment, so any race here is less likely to cause problems.
    const id = this.nextPlace++;
    const args = [id, url, now];
    await this.db.run('INSERT INTO placeEvents (id, url, ts) VALUES (?, ?, ?)', args);
    return id;
  }

  savePlace(url, now = microtime.now()) {
    const place = this.places.get(url);
    if (place) {
      return Promise.resolve(place);
    }

    return this.savePlaceWithoutEstablishingTransaction(url, now)
               .then(id => this.savePlaceIDMapping(url, id));
  }

  /**
   * Store a visit for a given place ID. Chains through the place ID.
   */
  saveVisitForPlaceWithoutEstablishingTransaction(place, session, now = microtime.now()) {
    return this.db
               .run('INSERT INTO visitEvents (place, session, ts, type) VALUES (?, ?, ?, ?)',
                 [place, session, now, VisitType.unknown])
               .then(() => Promise.resolve(place));
  }

  /**
   * Store a title for a given place ID. Chains through the place ID. Writes nothing if the
   * title is undefined.
   */
  saveTitleForPlaceWithoutEstablishingTransaction(place, title,
                                                  now = microtime.now()) {
    if (title === undefined) {
      return Promise.resolve(place);
    }

    return this.db
               .run('INSERT INTO titleEvents (place, title, ts) VALUES (?, ?, ?)',
                 [place, title, now])
               .then(() => Promise.resolve(place));
  }

  savePlaceIDMapping(url, id) {
    this.places.set(url, id);
    return Promise.resolve(id);
  }

  async startSession(scope, ancestor, now = microtime.now(), reason = SessionStartReason.newTab) {
    const result =
      await this.db
                .run('INSERT INTO sessionStarts (scope, ancestor, ts, reason) VALUES (?, ?, ?, ?)',
                  [scope, ancestor, now, reason]);
    return result.lastID;
  }

  async endSession(session, now = microtime.now(), reason = SessionEndReason.tabClosed) {
    const result =
      await this.db
                .run('INSERT INTO sessionEnds (id, ts, reason) VALUES (?, ?, ?)',
                  [session, now, reason]);
    return result.lastID;
  }

  async materializeStars() {
    await this.db.run('DELETE FROM mStarred');
    await this.db.run('INSERT INTO mStarred SELECT place, ts, url FROM vStarred');
  }

  async materializeHistory() {
    await this.db.run('DELETE FROM mHistory');
    await this.db.run(`INSERT INTO mHistory
      SELECT place, url, lastTitle, lastVisited, visitCount
      FROM vHistory
    `);
  }

  async rematerialize() {
    await this.materializeHistory();
    await this.materializeStars();
  }

  /**
   * Update the contents of the `mStarred` materialized view in response to a starring change.
   * This implementation depends on the ordering of star/unstar events being strictly:
   *
   *   1, -1, 1, -1, 1, -1, …
   *
   * Any other order will result in inconsistency. An alternative implementation is to do a partial
   * rebuild -- exactly the normal materialization query, but with a `WHERE place = ?` clause added.
   */
  updateMaterializedStars(url, place, session, action, now = microtime.now()) {
    switch (action) {
      case StarOp.star:
        return this.db.run('INSERT OR REPLACE INTO mStarred (place, ts, url) VALUES (?, ?, ?)',
          [place, now, url]);
      case StarOp.unstar:
        return this.db.run('DELETE FROM mStarred WHERE place = ?', [place]);

      default:
        return Promise.reject(new Error(`Unknown action ${action}.`));
    }
  }

  // Chains through place ID.
  starPage(url, session, action, now = microtime.now()) {
    if (action !== StarOp.star && action !== StarOp.unstar) {
      return Promise.reject(new Error(`Unknown action ${action}.`));
    }

    const star = place =>
      this.db
          .run('INSERT INTO starEvents (place, session, action, ts) VALUES (?, ?, ?, ?)',
            [place, session, action, now])

          // Update materialized views.
          .then(() => this.updateMaterializedStars(url, place, session, action, now))
          .then(() => Promise.resolve(place));

    const place = this.places.get(url);
    if (place) {
      return star(place);
    }

    return this.db.inTransaction(() =>
                 this.savePlaceWithoutEstablishingTransaction(url, now)
                     .then(star))

               // If the transaction committed, keep the URL -> ID mapping in memory.
               .then(id => this.savePlaceIDMapping(url, id));
  }

  async recordVisitWithoutEstablishingTransaction(url, session, title, now = microtime.now()) {
    const place = await this.savePlaceWithoutEstablishingTransaction(url, now);
    await this.saveVisitForPlaceWithoutEstablishingTransaction(place, session, now);
    await this.saveTitleForPlaceWithoutEstablishingTransaction(place, title, now);

    // Update the materialized view.
    // This assumes uniqueness of events, which is true so long as we're conflating these
    // API calls and the generation of events themselves.
    // We can't just use UPDATE here because we can't trust that `this.places` implies
    // presence in `mHistory` -- we also use it for bookmarks -- so UPDATE OR INSERT.
    const result = await this.db.run(`
      UPDATE mHistory
      SET lastVisited = ?, visitCount = visitCount + 1, lastTitle = ?
      WHERE place = ?`,
      [now, title, place]);

    if (result.changes === 0) {
      await this.db.run(`INSERT INTO mHistory
        (place, lastTitle, url, lastVisited, visitCount)
        VALUES (?, ?, ?, ?, ?)`,
        [place, title, url, now, 1]);
    }

    return place;
  }

  /**
   * Record a visit to a URL.
   * @param url the visited URL.
   * @param title (optional) a known title for the page.
   * @param now (optional) microsecond timestamp.
   * @returns {Promise} a promise that resolves to the place ID.
   */
  visit(url, session, title, now = microtime.now()) {
    return this.db.inTransaction(() =>
      this.recordVisitWithoutEstablishingTransaction(url, session, title, now)

          // If the transaction committed, keep the URL -> ID mapping in memory.
          .then(place => this.savePlaceIDMapping(url, place)));
  }

  collectURLs(rows) {
    const out = [];
    for (const row of rows) {
      out.push(row.url);
    }
    return out;
  }

  /**
   * Search for places that match title, URL, or saved content
   * since the specified timestamp.
   *
   * Snippets in the returned results are guaranteed to be HTML-escaped.
   * They will include `<b>` `</b>` tags around matches.
   *
   * @param string The substring to match. Not tokenized.
   * @param since The earliest visit time in microseconds.
   * @param limit The maximum number of results to return.
   * @returns {Promise<[AwesomebarMatch]>}
   */
  async query(string, since = 0, limit = 10, snippetSize = SnippetSize.medium) {
    const contentMatches = `
    SELECT p.id AS place,
           p.url AS uri,
           pages.title AS title,
           snippet(pages, '╞', '╡', '…', -1, ?) AS snippet,
           pages.ts AS lastVisited
    FROM placeEvents AS p JOIN pages ON p.id = pages.place
    WHERE pages.ts > ? AND pages MATCH ?
    `;

    const visitMatches = `
    SELECT place,
           url AS uri,
           lastTitle AS title,
           NULL AS snippet,
           lastVisited
    FROM mHistory
    WHERE lastVisited > ? AND (
      lastTitle LIKE ? OR url LIKE ?
    )`;

    const query = `
    SELECT place,
           uri,
           MAX(title) AS title,
           MAX(snippet) AS snippet,
           MAX(lastVisited) AS lastVisited
    FROM (${contentMatches} UNION ${visitMatches})
    GROUP BY place
    ORDER BY lastVisited DESC
    LIMIT ?`;

    const like = `%${string}%`;
    const rows = await this.db.all(query, [snippetSize, since, string, since, like, like, limit]);

    // We escape on the way out, not the way in, for two reasons:
    // 1. We can't rely on the retrieved content correctly including the entirety of an escaped
    //    character like '&'.
    // 2. We'd like to be able to match on the unescaped text.
    function escape(s) {
      if (!s) {
        return s;
      }

      return escaper.escape(s)
                    .replace(/╞/g, '<b>')
                    .replace(/╡/g, '</b>');
    }

    return rows.map(row =>
      ({
        uri: row.uri,
        title: row.title,
        snippet: escape(row.snippet),
        lastVisited: row.lastVisited,
      })
    );
  }

  async visitedMatches(substring, since = 0, limit = 10) {
    const like = `%${substring}%`;

    // Fetch all places visited, with the latest timestamp for each.
    // TODO: search historical titles?
    const query = `SELECT * FROM mHistory
      WHERE lastVisited > ? AND (
        lastTitle LIKE ? OR url LIKE ?
      )
      ORDER BY lastVisited DESC
      LIMIT ?
    `;

    return this.collectURLs(await this.db.all(query, [since, like, like, limit]));
  }

  // Ordered by last visit, descending.
  async visited(since = 0, limit = 10) {
    // Fetch all places visited, with the latest timestamp for each.
    const query = `
      SELECT h.place AS place,
             h.url AS uri,
             h.lastTitle AS title,
             pages.excerpt AS snippet,
             h.lastVisited AS lastVisited
      FROM mHistory AS h LEFT JOIN pages ON pages.place = h.place
      WHERE h.lastVisited > ?
      GROUP BY h.place
      ORDER BY h.lastVisited DESC
      LIMIT ?
    `;

    return await this.db.all(query, [since, limit]);
  }

  async getStarredWithOrderByAndLimit(newestFirst, limit) {
    const orderClause = newestFirst ? 'ORDER BY s.ts DESC' : '';
    let limitClause;
    let args;
    if (limit) {
      limitClause = 'LIMIT ?';
      args = [limit];
    } else {
      limitClause = '';
      args = [];
    }

    const fromMaterialized = `
    SELECT s.place AS place, t.title AS title, s.ts AS ts, s.url AS url
    FROM mStarred AS s LEFT JOIN vTitles AS t ON s.place = t.place
    ${orderClause} ${limitClause}
    `;

    return await this.db.all(fromMaterialized, args);
  }

  async starredURLs(limit = undefined) {
    // Fetch all places visited, with the latest timestamp for each.
    const rows = await this.getStarredWithOrderByAndLimit(false, limit);
    return Immutable.Set(rows.map(row => row.url));
  }

  async recentlyStarred(limit = 5) {
    const rows = await this.getStarredWithOrderByAndLimit(true, limit);
    return rows.map(row =>
      new Bookmark({
        title: row.title, location: row.url, visitedAt: row.ts,
      }));
  }

  async savePage(page, session, now = microtime.now()) {
    const place = await this.savePlace(page.uri, now);
    const query = `
    INSERT INTO pages (place, session, ts, title, excerpt, content)
    VALUES (?, ?, ?, ?, ?, ?)
    `;
    return this.db.run(query, [place, session, now, page.title, page.excerpt, page.textContent]);
  }

  userVersion() {
    return this.db
               .get('PRAGMA user_version')
               .then(row => Promise.resolve(row.user_version));
  }
}
<|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[<commit_msg>Ooops!<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[ <|endoftext|>


<commit_before>var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var phantom = require('phantom');

var public_dir = __dirname + '/public';
app.use(express.static(public_dir));

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

var port = process.env.PORT || 8080;

var router = express.Router();

app.set('views', __dirname + '/views')
app.set('view engine', 'jade')

app.get('/',function(req,res){
    res.render('index')
});

router.route('/')

.post(function(req, res) {
    var url = req.body.url;
    phantom.create("--ignore-ssl-errors=yes", "--ssl-protocol=any", function (ph) {
        ph.createPage(function(page){
            page.set('viewportSize', {width:1920,height:1080}, function(){
                page.open(url, function(status){
                    if (status == "success") {

                        var image_file_name = url.replace(/\W/g, '_') + ".png"
                        var image_path = public_dir + "/images/" + image_file_name
                        page.render(image_path, function(){
                            res.json({ result: '/images/'+image_file_name});
                        });
                    }
                    else {
                        res.json({ result: 'Hello World' });
                    }
                    page.close();
                    ph.exit();
                });
            });
        });
    });

})

app.use('/api', router);

app.listen(port);
console.log('Listening on port ' + port);

<commit_msg>set correct ports for openshift<commit_after>var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var phantom = require('phantom');

var public_dir = __dirname + '/public';
app.use(express.static(public_dir));

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

var server_port = process.env.OPENSHIFT_NODEJS_PORT || 8080
var server_ip = process.env.OPENSHIFT_NODEJS_IP || '127.0.0.1'

var router = express.Router();

app.set('views', __dirname + '/views')
app.set('view engine', 'jade')

app.get('/',function(req,res){
    res.render('index')
});

router.route('/')

.post(function(req, res) {
    var url = req.body.url;
    phantom.create("--ignore-ssl-errors=yes", "--ssl-protocol=any", function (ph) {
        ph.createPage(function(page){
            page.set('viewportSize', {width:1920,height:1080}, function(){
                page.open(url, function(status){
                    if (status == "success") {

                        var image_file_name = url.replace(/\W/g, '_') + ".png"
                        var image_path = public_dir + "/images/" + image_file_name
                        page.render(image_path, function(){
                            res.json({ result: '/images/'+image_file_name});
                        });
                    }
                    else {
                        res.json({ result: 'Hello World' });
                    }
                    page.close();
                    ph.exit();
                });
            });
        });
    });

})

app.use('/api', router);

app.listen(server_port, server_ip, function(){
  console.log("Listening on " + server_ip + ", server_port " + server_port)
});

<|endoftext|>


<commit_before>'use strict';

const assert = require( 'chai' ).assert;

const rediservice = require( '../lib/rediservice' )
                    .create( 'redis://localhost:6379' );

describe( 'rediservice', function () {

  it( 'should cache some data', function (done) {

    rediservice.setCache('author', { name: 'Kevin', age: 45 }, 2); // secs

    rediservice.getCache('author').then( (data) => {

      assert.equal( 'Kevin', data.name );
      assert.equal( 45, data.age );

    }).then( () => {

      rediservice.getCache('author', (err, data) => {

        assert.isNull( err );
        assert.equal( 'Kevin', data.name );
        assert.equal( 45, data.age );

        done();
      });

    });

  });

  it( 'should handle uncached data', function (done) {

    rediservice.getCache('missing').then( (data) => {
      assert.isNull( data );
      done();
    });

  });

});

// EOF
<commit_msg>push to trigger Travis CI<commit_after>'use strict';

const assert = require( 'chai' ).assert;

const rediservice = require( '../lib/rediservice' )
                    .create( 'redis://localhost:6379' );

describe( 'rediservice', function () {

  it( 'should cache some data', function (done) {

    rediservice.setCache('author', { name: 'Kevin', age: 45 }, 3); // secs

    rediservice.getCache('author').then( (data) => {

      assert.equal( 'Kevin', data.name );
      assert.equal( 45, data.age );

    }).then( () => {

      rediservice.getCache('author', (err, data) => {

        assert.isNull( err );
        assert.equal( 'Kevin', data.name );
        assert.equal( 45, data.age );

        done();
      });

    });

  });

  it( 'should handle uncached data', function (done) {

    rediservice.getCache('missing').then( (data) => {
      assert.isNull( data );
      done();
    });

  });

});

// EOF
<|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",<commit_msg>Grammar fix<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",
<|endoftext|>