<commit_before>'use strict';

/* Controllers */

var myappControllers = angular.module('myappControllers', []);

myappControllers.controller('LoginCtrl', ['$scope', '$routeParams', '$location', '$http', '$timeout', '$filter', 'Partage', 'Utils',
 function($scope, $routeParams, $location, $http, $timeout, $filter, Partage, Utils) {

    $scope.partage = Partage; // Share data between controllers
    $scope.message = "";
    $scope.errors = []; // errors
    $scope.alert = {text:'', type:''}; // alert

    // INIT
    $scope.init = function(){
        // CLEAN SESSION
    	$http.get(serverHost + '/logout', {
         withCredentials: true
         }).success(function(data){
            // do something ?
         });
    }

    $scope.init();

}]);

myappControllers.controller('MenuCtrl', ['$scope', '$routeParams', '$location', '$timeout', '$filter', 'Partage', 'Utils',
 function($scope, $routeParams, $location, $timeout, $filter, Partage, Utils) {

    $scope.partage = Partage; // share data between controllers

    // INIT
    $scope.init = function(){

    }

    $scope.init();

}]);

myappControllers.controller('LobbyCtrl', ['$scope', '$routeParams', '$location', '$timeout', '$filter', 'Partage', 'Utils',
 function($scope, $routeParams, $location, $timeout, $filter, Partage, Utils) {

    $scope.partage = Partage; // share data between controllers
    $scope.room = "";

    // INIT
    $scope.init = function(){
        $('#lobbyInput').focus();
    }

    $scope.join = function (){
        if($.trim($scope.room)!= ""){
            Partage.room = $scope.room;
            $location.path('/chat/'+Partage.room);
        }
        else{
            $scope.room = "";
            $('#lobbyInput').focus();
        }
    }

    $scope.init();

}]);

myappControllers.controller('ChatCtrl', ['$scope', '$routeParams', '$location', '$timeout', '$filter', 'Partage', 'Utils', 'mySocket', 'MessageStorage',
 function($scope, $routeParams, $location, $timeout, $filter, Partage, Utils, mySocket, MessageStorage) {

    $scope.partage = Partage; // share data between controllers

    $scope.errors = []; // errors
    $scope.alert = {text:'', type:''}; // alert

    $scope.room = "";

	$scope.message = ""; // chat message
	$scope.messages = [] // chat messages
    $scope.usersList = [];
    $scope.numUsers = 0;

    // INIT
    $scope.init = function(){

        // we need the room name in the route
        if($routeParams.room == null || $.trim($routeParams.room) == ""){
            $location('/lobby');
        }

        // init room from the route
        Partage.room = $routeParams.room;
        $scope.room = $routeParams.room;

        // NEW USER ENTER
        MessageStorage.setId(newValue); // set storage id
-       $scope.messages = MessageStorage.get(); // chat messages from localStorage
-       mySocket.emit('NEW_USER', {id: $scope.partage.id, provider: $scope.partage.provider, username: $scope.partage.username , room: $scope.partage.room, avatar: $scope.partage.avatar});

		// destroy socket when leaving the chat
		$scope.$on('$destroy', function () {
              Partage.room = "";
			  mySocket.disconnect();
		});

        mySocket.on('LOGIN', function(data){
            $scope.numUsers = data.numUsers;
            // scroll bottom if necessary
                if(!Partage.isScrolling){
                    Partage.isScrolling = true;
                    $("html, body").animate(
                    { scrollTop: $(document).height() },
                    1000,
                        function(){
                            Partage.isScrolling = false;
                        }
                    );
                }
            // console.log('LOGIN ' + data.numUsers);
        });

        mySocket.on('NEW_USER', function(data){
            $scope.numUsers = data.numUsers;
            $scope.usersList.push({username: data.username, room: data.room, avatar: data.avatar});
			// console.log('NEW USER ' + data.username + ' ' + data.room + ' ' + data.avatar);
        });

        mySocket.on('USER_LEFT', function(data){
            $scope.numUsers = data.numUsers;
            // console.log('USER LEFT ' + data.username + ' ' + data.room);
        });

		mySocket.on('NEW_MESSAGE', function(data){
            // limit to 100 messages (by room) in the localStorage
            if($scope.messages.length == 100){
                 $scope.messages.shift();
            }
            // console.log ('get a new message');
            $scope.messages.push(data);
            // store the messages list in the localStorage (max = 100)
            MessageStorage.put($scope.messages);

			// scroll bottom if necessary
			if(!Partage.isScrolling){
				Partage.isScrolling = true;
				$("html, body").animate(
				{ scrollTop: $(document).height() },
				1000,
					function(){
						Partage.isScrolling = false;
					}
				);
			}

        });

    }

    $scope.init();

}]);

myappControllers.controller('MessageCtrl', ['$scope', '$routeParams', '$location', '$timeout', '$filter', 'Partage', 'Utils', 'mySocket',
 function($scope, $routeParams, $location, $timeout, $filter, Partage, Utils, mySocket) {

    $scope.partage = Partage; // Share data between controllers
	$scope.message = ""; // chat message

    // INIT
    $scope.init = function(){
		$('#chatInput').focus();
    }

	$scope.sendMessage = function (){
		if($.trim($scope.message)!= ""){
			// console.log('send message')
            var mome = new Date().getTime();
			mySocket.emit('NEW_MESSAGE', {message : $scope.message, moment: mome});

			// scroll bottom if necessary
			if(!Partage.isScrolling){
				Partage.isScrolling = true;
				$("html, body").animate(
				{ scrollTop: $(document).height() },
				1000,
					function(){
						Partage.isScrolling = false;
					}
				);
			}
		}
		$scope.message = "";
		$('#chatInput').focus();
	}

    $scope.init();

}]);
<commit_msg>route params check<commit_after>'use strict';

/* Controllers */

var myappControllers = angular.module('myappControllers', []);

myappControllers.controller('LoginCtrl', ['$scope', '$routeParams', '$location', '$http', '$timeout', '$filter', 'Partage', 'Utils',
 function($scope, $routeParams, $location, $http, $timeout, $filter, Partage, Utils) {

    $scope.partage = Partage; // Share data between controllers
    $scope.message = "";
    $scope.errors = []; // errors
    $scope.alert = {text:'', type:''}; // alert

    // INIT
    $scope.init = function(){
        // CLEAN SESSION
    	$http.get(serverHost + '/logout', {
         withCredentials: true
         }).success(function(data){
            // do something ?
         });
    }

    $scope.init();

}]);

myappControllers.controller('MenuCtrl', ['$scope', '$routeParams', '$location', '$timeout', '$filter', 'Partage', 'Utils',
 function($scope, $routeParams, $location, $timeout, $filter, Partage, Utils) {

    $scope.partage = Partage; // share data between controllers

    // INIT
    $scope.init = function(){

    }

    $scope.init();

}]);

myappControllers.controller('LobbyCtrl', ['$scope', '$routeParams', '$location', '$timeout', '$filter', 'Partage', 'Utils',
 function($scope, $routeParams, $location, $timeout, $filter, Partage, Utils) {

    $scope.partage = Partage; // share data between controllers
    $scope.room = "";

    // INIT
    $scope.init = function(){
        $('#lobbyInput').focus();
    }

    $scope.join = function (){
        if($.trim($scope.room)!= ""){
            Partage.room = $scope.room;
            $location.path('/chat/'+Partage.room);
        }
        else{
            $scope.room = "";
            $('#lobbyInput').focus();
        }
    }

    $scope.init();

}]);

myappControllers.controller('ChatCtrl', ['$scope', '$routeParams', '$location', '$timeout', '$filter', 'Partage', 'Utils', 'mySocket', 'MessageStorage',
 function($scope, $routeParams, $location, $timeout, $filter, Partage, Utils, mySocket, MessageStorage) {

    $scope.partage = Partage; // share data between controllers

    $scope.errors = []; // errors
    $scope.alert = {text:'', type:''}; // alert

    $scope.room = "";

	$scope.message = ""; // chat message
	$scope.messages = [] // chat messages
    $scope.usersList = [];
    $scope.numUsers = 0;

    // INIT
    $scope.init = function(){

        // we need the room name in the route
        if($.trim($routeParams.room) == ""){
            $location('/lobby');
        }

        // init room from the route
        Partage.room = $routeParams.room;
        $scope.room = $routeParams.room;

        // NEW USER ENTER
        MessageStorage.setId($scope.room); // set storage id
-       $scope.messages = MessageStorage.get(); // chat messages from localStorage
-       mySocket.emit('NEW_USER', {id: $scope.partage.id, provider: $scope.partage.provider, username: $scope.partage.username , room: $scope.partage.room, avatar: $scope.partage.avatar});

		// destroy socket when leaving the chat
		$scope.$on('$destroy', function () {
              Partage.room = "";
			  mySocket.disconnect();
		});

        mySocket.on('LOGIN', function(data){
            $scope.numUsers = data.numUsers;
            // scroll bottom if necessary
                if(!Partage.isScrolling){
                    Partage.isScrolling = true;
                    $("html, body").animate(
                    { scrollTop: $(document).height() },
                    1000,
                        function(){
                            Partage.isScrolling = false;
                        }
                    );
                }
            // console.log('LOGIN ' + data.numUsers);
        });

        mySocket.on('NEW_USER', function(data){
            $scope.numUsers = data.numUsers;
            $scope.usersList.push({username: data.username, room: data.room, avatar: data.avatar});
			// console.log('NEW USER ' + data.username + ' ' + data.room + ' ' + data.avatar);
        });

        mySocket.on('USER_LEFT', function(data){
            $scope.numUsers = data.numUsers;
            // console.log('USER LEFT ' + data.username + ' ' + data.room);
        });

		mySocket.on('NEW_MESSAGE', function(data){
            // limit to 100 messages (by room) in the localStorage
            if($scope.messages.length == 100){
                 $scope.messages.shift();
            }
            // console.log ('get a new message');
            $scope.messages.push(data);
            // store the messages list in the localStorage (max = 100)
            MessageStorage.put($scope.messages);

			// scroll bottom if necessary
			if(!Partage.isScrolling){
				Partage.isScrolling = true;
				$("html, body").animate(
				{ scrollTop: $(document).height() },
				1000,
					function(){
						Partage.isScrolling = false;
					}
				);
			}

        });

    }

    $scope.init();

}]);

myappControllers.controller('MessageCtrl', ['$scope', '$routeParams', '$location', '$timeout', '$filter', 'Partage', 'Utils', 'mySocket',
 function($scope, $routeParams, $location, $timeout, $filter, Partage, Utils, mySocket) {

    $scope.partage = Partage; // Share data between controllers
	$scope.message = ""; // chat message

    // INIT
    $scope.init = function(){
		$('#chatInput').focus();
    }

	$scope.sendMessage = function (){
		if($.trim($scope.message)!= ""){
			// console.log('send message')
            var mome = new Date().getTime();
			mySocket.emit('NEW_MESSAGE', {message : $scope.message, moment: mome});

			// scroll bottom if necessary
			if(!Partage.isScrolling){
				Partage.isScrolling = true;
				$("html, body").animate(
				{ scrollTop: $(document).height() },
				1000,
					function(){
						Partage.isScrolling = false;
					}
				);
			}
		}
		$scope.message = "";
		$('#chatInput').focus();
	}

    $scope.init();

}]);
<|endoftext|>


<commit_before>var wt = require("fs-watch-tree");
var cp = require("child_process");
var util = require("util");
var glob = require("buster-glob");
var path = require("path");
var onInterrupt = require("./on-interrupt");

function throttle(ms, callback, thisp) {
    var timer;
    return function () {
        var args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function () { callback.apply(thisp, args); }, ms);
    };
}

function addTestOption(argv, files) {
    for (var i = 0, l = argv.length; i < l; ++i) {
        if (argv[i] === "-t" || argv[i] === "--tests") {
            var arguments = argv.slice();
            arguments[i + 1] += "," + files.join(",");
            return arguments;
        }
    }
    return argv.concat(["-t", files.join(",")]);
}

function printHeader() {
    var now = new Date();
    util.puts("\n");
    util.puts(now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds() + " Running tests");
    util.puts("``````````````````````");
}

function success(code) {
    return code === 0;
}

exports.watch = function (dir, options) {
    var running = false;
    options = options || {};

    function findFiles(event, callback) {
        if (!event || !event.name || event.isDirectory()) {
            return callback([]);
        }
        var pieces = path.basename(event.name).split(".");
        pieces.pop();
        var base = pieces.join(".");
        glob.glob([
            "{test{,s},spec{,s}}/**/*" + base + "*.js"
        ], function (err, paths) {
            paths.push(event.name);
            callback(paths);
        });
    }

    function prepareOptions(files) {
        var argv = options.argv || [];
        if (files.length > 0) { argv = addTestOption(argv, files); }
        return argv;
    }

    function runTests(event) {
        if (running) { return; }
        running = true;
        findFiles(event, function (files) {
            printHeader();
            var test = cp.spawn("buster-test", prepareOptions(files));

            var cancel = throttle(1000, function () {
                running = false;
                test.kill();
            });

            function onData(data) {
                util.print(data.toString());
                cancel();
            }

            test.stdout.on("data", onData);
            test.stderr.on("data", onData);

            test.on("exit", function (code) {
                running = false;
                if (success(code) && files.length > 0) { runTests(); }
            });
        });
    }

    var run = throttle(10, runTests);

    wt.watchTree(dir, { exclude: [/\/\./, "#", "node_modules"] }, function (e) {
        if (e.isMkdir()) { return; }
        run(e);
    });

    onInterrupt("Running all tests.", run);
};
<commit_msg>Clear screen between test runs.<commit_after>var wt = require("fs-watch-tree");
var cp = require("child_process");
var util = require("util");
var glob = require("buster-glob");
var path = require("path");
var onInterrupt = require("./on-interrupt");

function throttle(ms, callback, thisp) {
    var timer;
    return function () {
        var args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function () { callback.apply(thisp, args); }, ms);
    };
}

function addTestOption(argv, files) {
    for (var i = 0, l = argv.length; i < l; ++i) {
        if (argv[i] === "-t" || argv[i] === "--tests") {
            var arguments = argv.slice();
            arguments[i + 1] += "," + files.join(",");
            return arguments;
        }
    }
    return argv.concat(["-t", files.join(",")]);
}

var clearScreen = "\x1b[1;1H\x1b[2J";

function printHeader() {
    var now = new Date();
    var time = now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds();
    util.puts(clearScreen + time + " Running tests");
}

function success(code) {
    return code === 0;
}

exports.watch = function (dir, options) {
    var running = false;
    options = options || {};

    function findFiles(event, callback) {
        if (!event || !event.name || event.isDirectory()) {
            return callback([]);
        }
        var pieces = path.basename(event.name).split(".");
        pieces.pop();
        var base = pieces.join(".");
        glob.glob([
            "{test{,s},spec{,s}}/**/*" + base + "*.js"
        ], function (err, paths) {
            paths.push(event.name);
            callback(paths);
        });
    }

    function prepareOptions(files) {
        var argv = options.argv || [];
        if (files.length > 0) { argv = addTestOption(argv, files); }
        return argv;
    }

    function runTests(event) {
        if (running) { return; }
        running = true;
        findFiles(event, function (files) {
            printHeader();
            var test = cp.spawn("buster-test", prepareOptions(files));

            var cancel = throttle(1000, function () {
                running = false;
                test.kill();
            });

            function onData(data) {
                util.print(data.toString());
                cancel();
            }

            test.stdout.on("data", onData);
            test.stderr.on("data", onData);

            test.on("exit", function (code) {
                running = false;
                if (success(code) && files.length > 0) { runTests(); }
            });
        });
    }

    var run = throttle(10, runTests);

    wt.watchTree(dir, { exclude: [/\/\./, "#", "node_modules"] }, function (e) {
        if (e.isMkdir()) { return; }
        run(e);
    });

    onInterrupt("Running all tests.", run);
};
<|endoftext|>


<commit_before>/*
 * buster-istanbul
 * https://github.com/englishtown/buster-istanbul
 *
 * Copyright (c) 2012 kates
 * Licensed under the MIT license.
 */

var buster = require('buster-core'),
  coverage = require('./coverage'),
  glob = require('glob-whatev'),
  path = require('path');

function merge(paths) {
  return paths.reduce(function(a, c){
    glob.glob(c).forEach(function(i){
      a.push("'" + i.replace(/^\/|\.js$/, '') + "'");
    });
    return a;
  }, []).join(", ");
}

var setup = {
  node: function(group) {
    coverage.hookRequire();
    group.sources.forEach(function(pattern) {
      glob.glob(pattern).forEach(function(fPath) {
        coverage.addInstrumentCandidate(fPath);
      });
    });
  },

  browser: function(group) {
    var options = group.config.options;

    global['__coverage__'] = global['__coverage__'] || {};

    group.on('load:framework', function(rs){
      rs.addFileResource(path.resolve(__dirname, 'helper.js'), {
        path: '/coverage/helper.js'
      }).then(function() {
        rs.loadPath.append('/coverage/helper.js');
      });
    });

    group.on('load:sources', function(rs){
      rs.addProcessor(function(resource, content){
        return coverage.instrumentCode(content, resource['path'].replace(/\//, ''));
      });
    });

    group.on("load:tests", function (rs) {
      if (options && options.amd) {
        rs.addResource({
          path: "/coverage/load-sources.js",
          content: "require([" + merge(group.sources) + "]);"
        }).then(function(){
          rs.loadPath.prepend("/coverage/load-sources.js");
        });

        if (options.amd.config) {
          rs.addFileResource(path.resolve(options.amd.config), {
            path: "/" + options.amd.config
          }).then(function(){
            rs.loadPath.prepend('/' + options.amd.config);
          });
        }

        rs.addFileResource(path.resolve(options.amd.require), {
          path: "/" + options.amd.require
        }).then(function(){
          rs.loadPath.prepend('/' + options.amd.require);
        });
      }
    });
  }
};

module.exports = {
  name: 'buster-istanbul',

  create: function(options) {
    var instance = Object.create(this);
    instance.options = options;
    return instance;
  },

  configure: function(group) {
    setup[group.environment](group);
    var self = this;
    process.on("exit", function() {
      coverage.writeReports(self.options.outputDirectory);
    });
  },

  testRun: function(testRunner, messageClient) {
    testRunner.on("istanbul:report", function(result){
      coverage.addCoverage(result.data || {});
    });
  }
};
<commit_msg>Allow the developer to opt out of instrumentation<commit_after>/*
 * buster-istanbul
 * https://github.com/englishtown/buster-istanbul
 *
 * Copyright (c) 2012 kates
 * Licensed under the MIT license.
 */

var buster = require('buster-core'),
  coverage = require('./coverage'),
  glob = require('glob-whatev'),
  path = require('path');

function merge(paths) {
  return paths.reduce(function(a, c){
    glob.glob(c).forEach(function(i){
      a.push("'" + i.replace(/^\/|\.js$/, '') + "'");
    });
    return a;
  }, []).join(", ");
}

var setup = {
  node: function(group, instrument) {
    coverage.hookRequire();
    if (instrument === false) return;
    group.sources.forEach(function(pattern) {
      glob.glob(pattern).forEach(function(fPath) {
        coverage.addInstrumentCandidate(fPath);
      });
    });
  },

  browser: function(group) {
    var options = group.config.options;

    global['__coverage__'] = global['__coverage__'] || {};

    group.on('load:framework', function(rs){
      rs.addFileResource(path.resolve(__dirname, 'helper.js'), {
        path: '/coverage/helper.js'
      }).then(function() {
        rs.loadPath.append('/coverage/helper.js');
      });
    });

    group.on('load:sources', function(rs){
      if (instrument === false) return;
      rs.addProcessor(function(resource, content){
        return coverage.instrumentCode(content, resource['path'].replace(/\//, ''));
      });
    });

    group.on("load:tests", function (rs) {
      if (options && options.amd) {
        rs.addResource({
          path: "/coverage/load-sources.js",
          content: "require([" + merge(group.sources) + "]);"
        }).then(function(){
          rs.loadPath.prepend("/coverage/load-sources.js");
        });

        if (options.amd.config) {
          rs.addFileResource(path.resolve(options.amd.config), {
            path: "/" + options.amd.config
          }).then(function(){
            rs.loadPath.prepend('/' + options.amd.config);
          });
        }

        rs.addFileResource(path.resolve(options.amd.require), {
          path: "/" + options.amd.require
        }).then(function(){
          rs.loadPath.prepend('/' + options.amd.require);
        });
      }
    });
  }
};

module.exports = {
  name: 'buster-istanbul',

  create: function(options) {
    var instance = Object.create(this);
    instance.options = options;
    return instance;
  },

  configure: function(group) {
    setup[group.environment](group, this.options.instrument);
    var self = this;
    process.on("exit", function() {
      coverage.writeReports(self.options.outputDirectory);
    });
  },

  testRun: function(testRunner, messageClient) {
    testRunner.on("istanbul:report", function(result){
      coverage.addCoverage(result.data || {});
    });
  }
};
<|endoftext|>


<commit_before>/* global module, User, Reference, Flair */
/**
 * ReferenceController
 *
 * @description :: Server-side logic for managing References
 * @help        :: See http://links.sailsjs.org/docs/controllers
 */

var Q = require('q'),
  async = require('async');

module.exports = {
  all: function (req, res) {
    var dateQuery, query;
    dateQuery = {};
    if (req.query.before !== undefined) {
      dateQuery["<"] = new Date(req.query.before);
    }
    if (req.query.after !== undefined) {
      dateQuery[">"] = new Date(req.query.after);
    }
    query = {
      type: ["event", "casual", "shiny", "redemption", "bank"]
    };
    if (Object.keys(dateQuery).length > 0) {
      query.createdAt = dateQuery;
    }

    Reference.find(query)
      .sort({createdAt: "asc"})
      .exec(function (err, refs) {
        if (err) {
          return res.serverError(err);
        }
        async.map(refs, function (ref, callback) {
          User.findOne({id: ref.user}).exec(function (err, refUser) {
            ref.user = refUser;
            callback(null, ref);
          });
        }, function (err, results) {
          if (err) {
            return res.serverError(err);
          }
          return res.ok(results);
        });
      });

  },

  add: function (req, res) {
    req.params = req.allParams();
    var protomatch = /^(https?):\/\/(www|[a-z0-9]*\.)?reddit\.com/;
    var endOfUrl = req.params.url.replace(protomatch, '');

    User.findOne({id: req.user.id}, function (err, refUser) {
      if (!refUser) {
        return res.notFound("Can't find user");
      } else {
        Reference.findOne({url: {endsWith: endOfUrl}, user: refUser.id}, function (err, ref) {
          if (err) {
            return res.serverError(err);
          }
          if (ref && (ref.type !== "egg" || req.params.type !== "egg")) {
            return res.badRequest();
          } else {
            Reference.create(
              {
                url: req.params.url,
                user: refUser.id,
                user2: req.params.user2,
                description: req.params.descrip,
                type: req.params.type,
                gave: req.params.gave,
                got: req.params.got,
                notes: req.params.notes,
                privatenotes: req.params.privatenotes,
                edited: false,
                number: req.params.number || 0
              },
              function (err, ref) {
                if (err) {
                  console.log(err);
                  return res.serverError();
                } else {
                  return res.ok(ref);
                }
              }
            );
          }
        });
      }
    });
  },

  edit: function (req, res) {
    req.params = req.allParams();

    User.findOne({id: req.user.id}, function (err, refUser) {
      if (!refUser) {
        return res.notFound("Can't find user");
      }
      Reference.findOne({id: req.params.id, user: refUser.id}).exec(function (err, ref) {
        if (err || !ref) {
          return res.notFound();
        }
        var approved = ref.approved;
        if (ref.url !== req.params.url || ref.type !== req.params.type || ref.number !== req.params.number) {
          approved = false;
        }
        Reference.update({id: req.params.id, user: refUser.id},
            {
              url: req.params.url,
              user: refUser.id,
              user2: req.params.user2,
              description: req.params.description,
              type: req.params.type,
              gave: req.params.gave,
              got: req.params.got,
              notes: req.params.notes,
              privatenotes: req.params.privatenotes,
              approved: approved,
              edited: true,
              number: req.params.number
            })
            .exec(function (err, ref) {
              if (err) {
                return res.serverError(err);
              }
              if (!ref) {
                return res.notFound();
              }
              return res.ok(ref);
            });
      });
    });
  },

  deleteRef: function (req, res) {
    var id = req.allParams().refId;

    Reference.findOne({id: id}).exec(function (err, ref) {
      if (!ref) {
        return res.notFound();
      }
      if (err) {
        return res.serverError(err);
      }
      if (ref.user === req.user.id || req.user.isMod) {
        Reference.destroy({id: id})
          .exec(function (err, refs) {
            if (err) {
              return res.serverError(err);
            } else {
              return res.ok(refs);
            }
          });
      } else {
        return res.forbidden("unauthorised");
      }
    });

  },

  comment: function (req, res) {
    var user = req.user,
      refUser = req.allParams().refUser,
      comment = req.allParams().comment;

    User.findOne({id: refUser}, function (err, reference) {
      Comment.create({user: reference.id, user2: user.name, message: comment}, function (err, com) {
        if (err) {
          return res.serverError(err);
        }
        return res.ok(com);
      });
    });

  },

  delComment: function (req, res) {
    var user = req.user,
      refUser = req.allParams().refUser,
      id = req.allParams().id;

    User.findOne({id: refUser}, function () {
      Comment.findOne({id: id}, function (err, comment) {
        if (!comment || err) {
          return res.notFound(err);
        }
        if ((user.name === comment.user2) || user.isMod) {
          Comment.destroy({id: id}, function (err, com) {
            return res.ok(com);
          });
        } else {
          return res.forbidden();
        }
      });
    });

  },

  approve: function (req, res) {
    console.log("test");
    if (!req.user.isMod) {
      return res.json("Not a mod", 403);
    }

    var refUserId = req.allParams().userid,
      id = req.allParams().id,
      approve = req.allParams().approve;

    User.findOne({id: refUserId}, function (err, refUser) {
      if (!refUser) {
        return res.notFound("User not found");
      }
      Reference.findOne(id, function (err, ref) {
        References.approve(ref, res).then(function (result) {
          return res.status(200).json(ref);
        }, function (error) {
          return res.status(error).end();
        });
      });
    });
  },

  approveAll: function (req, res) {
    var refUserId = req.allParams().userid,
      type = req.allParams().type;

    User.findOne({id: refUserId}, function (err, refUser) {
      if (!refUser) {
        return res.notFound("User not found");
      }
      var promises = [];
      Reference.find(req.id).forEach(function (ref) {
        promises.push(References.approve(ref));
      });
      Promise.all(promises).then(function (result) {
        return res.status(200).json(result);
      }, function (error) {
        return res.status(error).end();
      });
    });
  },

  saveFlairs: function (req, res) {
    var flairs = req.allParams().flairs;
    Flair.destroy({}, function (err) {
      if (err) {
        return res.serverError(err);
      }
      var promises = [],
        added = [];
      flairs.forEach(function (flair) {
        promises.push(
          Flair.create(flair)
            .exec(function (err, newFlair) {
              added.push(newFlair);
            })
        );
      });

      Q.all(promises).then(function () {
        res.ok(added);
      });

    });

  },

  getFlairs: function (req, res) {
    Flair.find().exec(function (err, flairs) {
      res.ok(flairs);
    });
  }
};

<commit_msg>The Approve All button and deletion both work now<commit_after>/* global module, User, Reference, Flair */
/**
 * ReferenceController
 *
 * @description :: Server-side logic for managing References
 * @help        :: See http://links.sailsjs.org/docs/controllers
 */

var Q = require('q'),
  async = require('async');

module.exports = {
  all: function (req, res) {
    var dateQuery, query;
    dateQuery = {};
    if (req.query.before !== undefined) {
      dateQuery["<"] = new Date(req.query.before);
    }
    if (req.query.after !== undefined) {
      dateQuery[">"] = new Date(req.query.after);
    }
    query = {
      type: ["event", "casual", "shiny", "redemption", "bank"]
    };
    if (Object.keys(dateQuery).length > 0) {
      query.createdAt = dateQuery;
    }

    Reference.find(query)
      .sort({createdAt: "asc"})
      .exec(function (err, refs) {
        if (err) {
          return res.serverError(err);
        }
        async.map(refs, function (ref, callback) {
          User.findOne({id: ref.user}).exec(function (err, refUser) {
            ref.user = refUser;
            callback(null, ref);
          });
        }, function (err, results) {
          if (err) {
            return res.serverError(err);
          }
          return res.ok(results);
        });
      });

  },

  add: function (req, res) {
    req.params = req.allParams();
    var protomatch = /^(https?):\/\/(www|[a-z0-9]*\.)?reddit\.com/;
    var endOfUrl = req.params.url.replace(protomatch, '');

    User.findOne({id: req.user.id}, function (err, refUser) {
      if (!refUser) {
        return res.notFound("Can't find user");
      } else {
        Reference.findOne({url: {endsWith: endOfUrl}, user: refUser.id}, function (err, ref) {
          if (err) {
            return res.serverError(err);
          }
          if (ref && (ref.type !== "egg" || req.params.type !== "egg")) {
            return res.badRequest();
          } else {
            Reference.create(
              {
                url: req.params.url,
                user: refUser.id,
                user2: req.params.user2,
                description: req.params.descrip,
                type: req.params.type,
                gave: req.params.gave,
                got: req.params.got,
                notes: req.params.notes,
                privatenotes: req.params.privatenotes,
                edited: false,
                number: req.params.number || 0
              },
              function (err, ref) {
                if (err) {
                  console.log(err);
                  return res.serverError();
                } else {
                  return res.ok(ref);
                }
              }
            );
          }
        });
      }
    });
  },

  edit: function (req, res) {
    req.params = req.allParams();

    User.findOne({id: req.user.id}, function (err, refUser) {
      if (!refUser) {
        return res.notFound("Can't find user");
      }
      Reference.findOne({id: req.params.id, user: refUser.id}).exec(function (err, ref) {
        if (err || !ref) {
          return res.notFound();
        }
        var approved = ref.approved;
        if (ref.url !== req.params.url || ref.type !== req.params.type || ref.number !== req.params.number) {
          approved = false;
        }
        Reference.update({id: req.params.id, user: refUser.id},
            {
              url: req.params.url,
              user: refUser.id,
              user2: req.params.user2,
              description: req.params.description,
              type: req.params.type,
              gave: req.params.gave,
              got: req.params.got,
              notes: req.params.notes,
              privatenotes: req.params.privatenotes,
              approved: approved,
              edited: true,
              number: req.params.number
            })
            .exec(function (err, ref) {
              if (err) {
                return res.serverError(err);
              }
              if (!ref) {
                return res.notFound();
              }
              return res.ok(ref);
            });
      });
    });
  },

  deleteRef: function (req, res) {
    var id = req.allParams().refId;

    Reference.findOne({id: id}).exec(function (err, ref) {
      if (!ref) {
        return res.notFound();
      }
      if (err) {
        return res.serverError(err);
      }
      if (ref.user === req.user.id || req.user.isMod) {
        if (ref.verified) {
          User.findOne({name: ref.user2.substring(3)}, function (err, otherUser) {
            if (err) {
              console.log("Warning: The trade being deleted is marked 'verified', but does not appear to have a complement.");
              return;
            }
            User.findOne({id: ref.user}, function (err2, thisUser) {
              if (err2 || !thisUser) {
                return res.status(404).json("User not found");
              }
              var query = {
                user: otherUser.id,
                url: new RegExp(ref.url.substring(ref.url.indexOf("/r/"))),
                user2: '/u/' + thisUser.name
              };
              //If a verified reference is deleted, its compelmentary reference is un-verified.
              Reference.update(query, {verified: false}, function (err, otherRef) {
                if (err) {
                  console.log("Error while updating complementary trade.");
                }
              });
            });
          });
        }
        Reference.destroy({id: id})
          .exec(function (err, refs) {
            if (err) {
              return res.serverError(err);
            } else {
              return res.ok(refs);
            }
          });
      } else {
        return res.forbidden();
      }
    });

  },

  comment: function (req, res) {
    var user = req.user,
      refUser = req.allParams().refUser,
      comment = req.allParams().comment;

    User.findOne({id: refUser}, function (err, reference) {
      Comment.create({user: reference.id, user2: user.name, message: comment}, function (err, com) {
        if (err) {
          return res.serverError(err);
        }
        return res.ok(com);
      });
    });

  },

  delComment: function (req, res) {
    var user = req.user,
      refUser = req.allParams().refUser,
      id = req.allParams().id;

    User.findOne({id: refUser}, function () {
      Comment.findOne({id: id}, function (err, comment) {
        if (!comment || err) {
          return res.notFound(err);
        }
        if ((user.name === comment.user2) || user.isMod) {
          Comment.destroy({id: id}, function (err, com) {
            return res.ok(com);
          });
        } else {
          return res.forbidden();
        }
      });
    });

  },

  approve: function (req, res) {
    if (!req.user.isMod) {
      return res.json("Not a mod", 403);
    }

    var id = req.allParams().id,
      approve = req.allParams().approve;

    User.findOne({id: req.allParams().userid}, function (err, refUser) {
      if (!refUser) {
        return res.notFound("User not found");
      }
      Reference.findOne(id, function (err, ref) {
        References.approve(ref, refUser.name, approve).then(function (result) {
          return res.status(200).json(ref);
        }, function (error) {
          return res.json(error);
        });
      });
    });
  },

  approveAll: function (req, res) {
    if (!req.user.isMod) {
      return res.json("Not a mod", 403);
    }

    User.findOne({id: req.allParams().userid}, function (err, refUser) {
      if (!refUser) {
        return res.notFound("User not found");
      }
      var promises = [];
      Reference.find({user: refUser.id, type: req.allParams().type}, function (err, refs) {
        if (err) {
          return res.serverError(err);
        }
        console.log(refs);
        for (var i = 0; i < refs.length; i++) {
          promises.push(References.approve(refs[i], refUser.name, true));
        }
        Promise.all(promises).then(function (err, result) {
          return res.status(200).json(result);
        }, function (error) {
          console.log(error);
          return res.status(error.statusCode).json(error);
        });
      });
    });
  },

  saveFlairs: function (req, res) {
    var flairs = req.allParams().flairs;
    Flair.destroy({}, function (err) {
      if (err) {
        return res.serverError(err);
      }
      var promises = [],
        added = [];
      flairs.forEach(function (flair) {
        promises.push(
          Flair.create(flair)
            .exec(function (err, newFlair) {
              added.push(newFlair);
            })
        );
      });

      Q.all(promises).then(function () {
        res.ok(added);
      });

    });

  },

  getFlairs: function (req, res) {
    Flair.find().exec(function (err, flairs) {
      res.ok(flairs);
    });
  }
};

<|endoftext|>


<commit_before>//----------------------------------------------------------------------
//
// This source file is part of the Folktale project.
//
// Copyright (C) 2015-2016 Quildreen Motta.
// Licensed under the MIT licence.
//
// See LICENCE for licence information.
// See CONTRIBUTORS for the list of contributors to the project.
//
//----------------------------------------------------------------------

const hasOwnProperty = Object.prototype.hasOwnProperty;

const mapEntries = (object, transform, define) =>
        Object.keys(object).reduce((result, key) => {
          const [newKey, newValue] = transform([key, object[key]]);
          return define(result, newKey, newValue);
        }, {});

mapEntries.overwrite = (object, transform) =>
  mapEntries(object, transform, (result, key, value) => {
    result[key] = value;
    return result;
  });

mapEntries.unique = (object, transform) =>
  mapEntries(object, transform, (result, key, value) => {
    if (result::hasOwnProperty(key)) {
      throw new Error(`The property ${key} already exists in the resulting object.`);
    }
    result[key] = value;
    return result;
  });

module.exports = mapEntries;

// -- Annotations ------------------------------------------------------
if (process.env.NODE_ENV !== "production") {
  mapEntries.overwrite[Symbol.for('@@meta:magical')] = {
    name: 'overwrite',
    signature: 'overwrite(object, transform)',
    type: `
(
  { String -> α },
  (#[String, α]) -> #[String, β]
) -> { String -> β }
    `,
    category: 'Transforming',
    complexity: 'O(n), n is the number of own enumerable properties',
    belongsTo: mapEntries,
    seeAlso: [
      {
        type: 'entity',
        path: 'mapEntries.unique',
        reason: 'Consistently throws when a collision happens.'
      }
    ],
    documentation: `
Transforms pairs of (key, value) own properties in a plain object.

This function is a specialised form of [[mapEntries]] that overwrites
duplicated keys when a collision happens. Because of this, the result
of a transformation where keys collide is not defined in ECMAScript 5
and older, but in ECMAScript 2015 properties that were inserted later
will win over properties that were inserted earlier.
    `
  };

  mapEntries.unique[Symbol.for('@@meta:magical')] = {
    name: 'unique',
    signature: 'unique(object, transform)',
    type: `
(
  { String -> α },
  (#[String, α]) -> #[String, β]
) -> { String -> β } :: throws
    `,
    category: 'Transforming',
    complexity: 'O(n), n is the number of own enumerable properties',
    belongsTo: mapEntries,
    seeAlso: [
      {
        type: 'entity',
        path: 'mapEntries.overwrite',
        reason: 'Safely overwrites keys when a collision happens.'
      }
    ],
    documentation: `
Transforms pairs of (key, value) own properties in a plain object.

This function is a specialised form of [[mapEntries]] that throws
when a key collision happens. Throwing makes this function potentially
unsafe to use, however it guarantees a consistent behaviour across
different ECMAScript versions and VMs.
    `
  };

  module.exports[Symbol.for('@@meta:magical')] = {
    name: 'mapEntries',
    signature: 'mapEntries(object, transform, define)',
    type: `
(
  { String -> α },
  (#[String, α]) -> #[String, β],
  ({ String -> β}, String, β) -> { String -> β } :: mutate?
) -> { String -> β }
    `,
    category: 'Transforming',
    stability: 'stable',
    platforms: ['ECMAScript 5'],
    authors: ['Quildreen Motta'],
    module: 'folktale/core/object/map-entries',
    complexity: 'O(n), n is the number of own enumerable properties',
    documentation: `
Transforms pairs of (key, value) own properties in a plain object.

The transformation takes a [key, value] pair, and is expected to return
a new [key, value] pair. The resulting object has not only its values
transformed, but also its keys:

    const pair = { x: 10, y: 20 };
    mapEntries(
      pair,
      ([k, v]) => [k.toUpperCase(), v * 2],
      (r, k, v) => Object.assign(r, { [k]: v })
    );
    // => { X: 20, Y: 40 }

> **NOTE**
> The function expects you to provide a definition function, which will
> determine how to handle the mapping of the returned pairs. This is
> necessary because no single behaviour is always the right one for
> this. Common behaviours, such as \`overwrite\` older properties,
> and enforcing \`unique\` properties are provided as specialised
> frorms of this function.

> **NOTE**
> The definition function may mutate the object.

> **WARNING**
> [[mapEntries]] will not preserve the same shape of the original object.
> It treats objects as plain maps from String to some value. It ignores
> things like prototypical delegation, symbols, and non-own enumerable
> properties.
    `
  };
}
<commit_msg>docs: Fixes documentation for map-entries<commit_after>//----------------------------------------------------------------------
//
// This source file is part of the Folktale project.
//
// Copyright (C) 2015-2016 Quildreen Motta.
// Licensed under the MIT licence.
//
// See LICENCE for licence information.
// See CONTRIBUTORS for the list of contributors to the project.
//
//----------------------------------------------------------------------

const hasOwnProperty = Object.prototype.hasOwnProperty;

const mapEntries = (object, transform, define) =>
        Object.keys(object).reduce((result, key) => {
          const [newKey, newValue] = transform([key, object[key]]);
          return define(result, newKey, newValue);
        }, {});

mapEntries.overwrite = (object, transform) =>
  mapEntries(object, transform, (result, key, value) => {
    result[key] = value;
    return result;
  });

mapEntries.unique = (object, transform) =>
  mapEntries(object, transform, (result, key, value) => {
    if (result::hasOwnProperty(key)) {
      throw new Error(`The property ${key} already exists in the resulting object.`);
    }
    result[key] = value;
    return result;
  });

module.exports = mapEntries;

// -- Annotations ------------------------------------------------------
if (process.env.NODE_ENV !== "production") {
  mapEntries.overwrite[Symbol.for('@@meta:magical')] = {
    name: 'overwrite',
    signature: 'overwrite(object, transform)',
    type: `
(
  { String -> α },
  (#[String, α]) -> #[String, β]
) -> { String -> β }
    `,
    category: 'Transforming',
    complexity: 'O(n), n is the number of own enumerable properties',
    belongsTo: mapEntries,
    seeAlso: [
      {
        type: 'entity',
        path: 'mapEntries.unique',
        reason: 'Consistently throws when a collision happens.'
      }
    ],
    documentation: `
Transforms pairs of (key, value) own properties in a plain object.

This function is a specialised form of [[mapEntries]] that overwrites
duplicated keys when a collision happens. Because of this, the result
of a transformation where keys collide is not defined in ECMAScript 5
and older, but in ECMAScript 2015 properties that were inserted later
will win over properties that were inserted earlier.
    `
  };

  mapEntries.unique[Symbol.for('@@meta:magical')] = {
    name: 'unique',
    signature: 'unique(object, transform)',
    type: `
(
  { String -> α },
  (#[String, α]) -> #[String, β]
) -> { String -> β } :: throws
    `,
    category: 'Transforming',
    complexity: 'O(n), n is the number of own enumerable properties',
    belongsTo: mapEntries,
    seeAlso: [
      {
        type: 'entity',
        path: 'mapEntries.overwrite',
        reason: 'Safely overwrites keys when a collision happens.'
      }
    ],
    documentation: `
Transforms pairs of (key, value) own properties in a plain object.

This function is a specialised form of [[mapEntries]] that throws
when a key collision happens. Throwing makes this function potentially
unsafe to use, however it guarantees a consistent behaviour across
different ECMAScript versions and VMs.
    `
  };

  module.exports[Symbol.for('@@meta:magical')] = {
    name: 'mapEntries',
    signature: 'mapEntries(object, transform, define)',
    type: `
(
  { String -> α },
  (#[String, α]) -> #[String, β],
  ({ String -> β}, String, β) -> { String -> β } :: mutate?
) -> { String -> β }
    `,
    category: 'Transforming',
    stability: 'stable',
    platforms: ['ECMAScript 5'],
    authors: ['Quildreen Motta'],
    module: 'folktale/core/object/map-entries',
    portability: 'Supported in older ES VMs with es5-shim',
    complexity: 'O(n), n is the number of own enumerable properties',
    documentation: `
Transforms pairs of (key, value) own properties in a plain object.

The transformation takes a [key, value] pair, and is expected to return
a new [key, value] pair. The resulting object has not only its values
transformed, but also its keys:

    const pair = { x: 10, y: 20 };
    mapEntries(
      pair,
      ([k, v]) => [k.toUpperCase(), v * 2],
      (r, k, v) => Object.assign(r, { [k]: v })
    );
    // => { X: 20, Y: 40 }

> **NOTE**
> The function expects you to provide a definition function, which will
> determine how to handle the mapping of the returned pairs. This is
> necessary because no single behaviour is always the right one for
> this. Common behaviours, such as \`overwrite\` older properties,
> and enforcing \`unique\` properties are provided as specialised
> frorms of this function.

> **NOTE**
> The definition function may mutate the object.

> **WARNING**
> [[mapEntries]] will not preserve the shape of the original object.
> It treats objects as plain maps from String to some value. It ignores
> things like prototypical delegation, symbols, and non-enumerable
> properties.
    `
  };
}
<|endoftext|>


<commit_before>var http = require('http'),
  request = require('request'),
  _ = require('underscore')._,
  querystring = require('querystring');

function EchoNestAPI_v4(apiKey, options) {
  this.version = '4';
  this.apiKey = apiKey;
  this.apiUrl = 'http://developer.echonest.com/api/v4/'
}

module.exports = EchoNestAPI_v4;

EchoNestAPI_v4.prototype.howdy = function() {
  return "Howdy!";
};

EchoNestAPI_v4.prototype.execute = function(type, method, availableParams, givenParams, callback) {
  var finalParams = { api_key : this.apiKey, format : 'json' };

  for (var i = 0; i < availableParams.length; i++) {
    currentParam = availableParams[i];
    if (typeof givenParams[currentParam] !== 'undefined')
      finalParams[currentParam] = givenParams[currentParam];
  }
  var uri = this.apiUrl + type + '/' + method + '?' + querystring.stringify(finalParams);
  request(uri, function (error, response, body) {
    var parsedResponse;
    if (error || _.include([404], response.statusCode)) {
      callback({
        error : 'Unable to connect to the Echo Nest API endpoint.',
        code : response.statusCode
      });
    } else {
      try {
        parsedResponse = JSON.parse(body);
      } catch (error) {
        callback({ error : 'Error parsing JSON answer from the Echo Nest API.', code : 'xxx'});
      }
      switch(parsedResponse.response.status.code) {
        case 0:
          callback(parsedResponse.response);
          break;
        case 1:
          callback({ error: 'Missing/Invalid API Key.', code: 'xxx' });
          break;
        case 4:
          callback({ error: parsedResponse.response.status.message, code: 'xxx'})
          break;
        case 5:
          callback({ error: 'Invalid Parameter.', code: 'xxx' });
          break;
        default:
          callback({ error: 'Unrecognized error code', code: 'xxx'});
      }
    }
  });
};

EchoNestAPI_v4.prototype.audio = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'audio', ['id', 'name', 'results', 'start'], params, callback);
};

EchoNestAPI_v4.prototype.biographies = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'biographies', ['id', 'name', 'results', 'start', 'license'], params, callback);
};

EchoNestAPI_v4.prototype.blogs = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'blogs',
    ['id', 'name', 'results', 'start', 'high_relevance'], params, callback);
};

EchoNestAPI_v4.prototype.familiarity = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'familiarity', ['id', 'name'], params, callback);
};

EchoNestAPI_v4.prototype.hotttnesss = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'hotttnesss', ['id', 'name', 'type'], params, callback);
};

EchoNestAPI_v4.prototype.images = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'images',
    ['id', 'name', 'results', 'start', 'license'], params, callback);
};

EchoNestAPI_v4.prototype.list_terms = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'list_terms', ['type'], params, callback);
};

EchoNestAPI_v4.prototype.news = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'news',
    ['id', 'name', 'results', 'start', 'high_relevance'], params, callback);
};

EchoNestAPI_v4.prototype.artist_profile = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'profile', ['id', 'name', 'bucket'], params, callback);
};

EchoNestAPI_v4.prototype.reviews = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'reviews', ['id', 'name', 'results', 'start'],
    params, callback);
};

EchoNestAPI_v4.prototype.artist_search = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'search', [
    'results',
    'bucket',
    'limit',
    'name',
    'description',
    'style',
    'mood',
    'rank_type',
    'fuzzy_match',
    'max_familiarity',
    'min_familiarity',
    'max_hotttnesss',
    'min_hotttnesss',
    'artist_start_year_before',
    'artist_start_year_after',
    'artist_end_year_before',
    'artist_end_year_after',
    'sort',
    'results',
    'start'
  ], params, callback);
};

EchoNestAPI_v4.prototype.extract = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'extract', [
    'results',
    'bucket',
    'limit',
    'name',
    'text',
    'max_familiarity',
    'min_familiarity',
    'max_hotttnesss',
    'min_hotttnesss',
    'sort',
    'results'
  ], params, callback);
};

EchoNestAPI_v4.prototype.songs = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'songs', ['id','name','results','start'], params, callback);
};

EchoNestAPI_v4.prototype.similar = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'similar', ['id',
               'name','results','min_results','start','bucket','max_familiarity',
               'min_familiarity', 'max_hotttnesss', 'min_hotttnesss',
               'artist_start_year_before', 'artist_start_year_after',
               'artist_end_year_before', 'artist_end_year_after', 'reverse', 'limit',
               'seed_catalog' ], params, callback);

};

EchoNestAPI_v4.prototype.suggest = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'suggest', ['results','name','q'], params, callback);
}

EchoNestAPI_v4.prototype.terms = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'terms', ['id','name','sort'], params, callback);
}

EchoNestAPI_v4.prototype.top_hottt = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'top_hottt', ['results','start','bucket', 'limit'], params, callback);
}

EchoNestAPI_v4.prototype.top_terms = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'top_terms', ['results'], params, callback);
}

EchoNestAPI_v4.prototype.urls = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'urls', ['id', 'name'], params, callback);
}

EchoNestAPI_v4.prototype.video = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'video', ['id', 'name', 'results', 'start'], params, callback);
}
<commit_msg>introduce song search method<commit_after>var http = require('http'),
  request = require('request'),
  _ = require('underscore')._,
  querystring = require('querystring');

function EchoNestAPI_v4(apiKey, options) {
  this.version = '4';
  this.apiKey = apiKey;
  this.apiUrl = 'http://developer.echonest.com/api/v4/'
}

module.exports = EchoNestAPI_v4;

EchoNestAPI_v4.prototype.howdy = function() {
  return "Howdy!";
};

EchoNestAPI_v4.prototype.execute = function(type, method, availableParams, givenParams, callback) {
  var finalParams = { api_key : this.apiKey, format : 'json' };

  for (var i = 0; i < availableParams.length; i++) {
    currentParam = availableParams[i];
    if (typeof givenParams[currentParam] !== 'undefined')
      finalParams[currentParam] = givenParams[currentParam];
  }
  var uri = this.apiUrl + type + '/' + method + '?' + querystring.stringify(finalParams);
  request(uri, function (error, response, body) {
    var parsedResponse;
    if (error || _.include([404], response.statusCode)) {
      callback({
        error : 'Unable to connect to the Echo Nest API endpoint.',
        code : response.statusCode
      });
    } else {
      try {
        parsedResponse = JSON.parse(body);
      } catch (error) {
        callback({ error : 'Error parsing JSON answer from the Echo Nest API.', code : 'xxx'});
      }
      switch(parsedResponse.response.status.code) {
        case 0:
          callback(parsedResponse.response);
          break;
        case 1:
          callback({ error: 'Missing/Invalid API Key.', code: 'xxx' });
          break;
        case 4:
          callback({ error: parsedResponse.response.status.message, code: 'xxx'})
          break;
        case 5:
          callback({ error: 'Invalid Parameter.', code: 'xxx' });
          break;
        default:
          callback({ error: 'Unrecognized error code', code: 'xxx'});
      }
    }
  });
};

EchoNestAPI_v4.prototype.audio = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'audio', ['id', 'name', 'results', 'start'], params, callback);
};

EchoNestAPI_v4.prototype.biographies = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'biographies', ['id', 'name', 'results', 'start', 'license'], params, callback);
};

EchoNestAPI_v4.prototype.blogs = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'blogs',
    ['id', 'name', 'results', 'start', 'high_relevance'], params, callback);
};

EchoNestAPI_v4.prototype.familiarity = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'familiarity', ['id', 'name'], params, callback);
};

EchoNestAPI_v4.prototype.hotttnesss = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'hotttnesss', ['id', 'name', 'type'], params, callback);
};

EchoNestAPI_v4.prototype.images = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'images',
    ['id', 'name', 'results', 'start', 'license'], params, callback);
};

EchoNestAPI_v4.prototype.list_terms = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'list_terms', ['type'], params, callback);
};

EchoNestAPI_v4.prototype.news = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'news',
    ['id', 'name', 'results', 'start', 'high_relevance'], params, callback);
};

EchoNestAPI_v4.prototype.artist_profile = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'profile', ['id', 'name', 'bucket'], params, callback);
};

EchoNestAPI_v4.prototype.reviews = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'reviews', ['id', 'name', 'results', 'start'],
    params, callback);
};

EchoNestAPI_v4.prototype.artist_search = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'search', [
    'results',
    'bucket',
    'limit',
    'name',
    'description',
    'style',
    'mood',
    'rank_type',
    'fuzzy_match',
    'max_familiarity',
    'min_familiarity',
    'max_hotttnesss',
    'min_hotttnesss',
    'artist_start_year_before',
    'artist_start_year_after',
    'artist_end_year_before',
    'artist_end_year_after',
    'sort',
    'results',
    'start'
  ], params, callback);
};

EchoNestAPI_v4.prototype.extract = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'extract', [
    'results',
    'bucket',
    'limit',
    'name',
    'text',
    'max_familiarity',
    'min_familiarity',
    'max_hotttnesss',
    'min_hotttnesss',
    'sort',
    'results'
  ], params, callback);
};

EchoNestAPI_v4.prototype.songs = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'songs', ['id','name','results','start'], params, callback);
};

EchoNestAPI_v4.prototype.similar = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'similar', ['id',
               'name','results','min_results','start','bucket','max_familiarity',
               'min_familiarity', 'max_hotttnesss', 'min_hotttnesss',
               'artist_start_year_before', 'artist_start_year_after',
               'artist_end_year_before', 'artist_end_year_after', 'reverse', 'limit',
               'seed_catalog' ], params, callback);

};

EchoNestAPI_v4.prototype.suggest = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'suggest', ['results','name','q'], params, callback);
}

EchoNestAPI_v4.prototype.terms = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'terms', ['id','name','sort'], params, callback);
}

EchoNestAPI_v4.prototype.top_hottt = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'top_hottt', ['results','start','bucket', 'limit'], params, callback);
}

EchoNestAPI_v4.prototype.top_terms = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'top_terms', ['results'], params, callback);
}

EchoNestAPI_v4.prototype.urls = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'urls', ['id', 'name'], params, callback);
}

EchoNestAPI_v4.prototype.video = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('artist', 'video', ['id', 'name', 'results', 'start'], params, callback);
}

EchoNestAPI_v4.prototype.song_search = function(params, callback) {
  if (typeof params == 'function') callback = params, params = {};

  this.execute('song', 'search', [
    'title',
    'artist',
    'combined',
    'description',
    'style',
    'mood',
    'rank_type',
    'artist_id',
    'results',
    'start',
    'max_tempo',
    'min_tempo',
    'max_duration',
    'min_duration',
    'max_loudness',
    'min_loudness',
    'artist_max_familiarity',
    'artist_min_familiarity',
    'artist_start_year_before',
    'artist_start_year_after',
    'artist_max_hotttnesss',
    'artist_min_hotttnesss',
    'min_longitude',
    'max_longitude',
    'min_latitude',
    'max_latitude',
    'max_danceability',
    'min_danceability',
    'max_energy',
    'min_energy',
    'mode',
    'key',
    'bucket',
    'sort',
    'limit'
  ], params, callback);
};
<|endoftext|>


<commit_before>import {View} from '../view';
import {Log} from '../infrastructure';
import {isFunction} from '../utility';

export class ScrollView extends View {
	constructor(model, table, vscroll, gridService) {
		super(model);

		this.table = table;

		const scroll = model.scroll;
		const rowHeight = model.row().height;
		const settings = {
			threshold: model.pagination().size,
		};

		if (rowHeight > 0 || isFunction(rowHeight)) {
			settings.rowHeight = rowHeight;
		}

		this.y = vscroll.factory(settings);

		this.y.container.drawEvent.on(e => {
			scroll({
				cursor: e.position
			}, {
				source: 'scroll.view',
				behavior: 'core'
			});

			const currentPage = Math.floor(e.position / model.pagination().size);
			model.pagination({
				current: currentPage
			}, {
				source: 'scroll.view',
				behavior: 'core'
			});
		});

		switch (scroll().mode) {
			case 'virtual': {

				const triggers = model.data().triggers;
				Object.keys(triggers).forEach(name =>
					this.using(model[name + 'Changed']
						.watch(e => {
							if (e.tag.behavior !== 'core' && e.tag.source !== 'scroll.view') {
								const changes = Object.keys(e.changes);
								if (triggers[name].find(key => changes.indexOf(key) >= 0)) {
									this.y.container.reset();
								}
							}
						})));

				// model.dataChanged.watch(e => {
				// 	if (e.hasChanges('rows')) {
				// 		const total = e.state.rows.length;
				// 		model.pagination({
				// 			count: total
				// 		}, {
				// 			source: 'scroll.view',
				// 			behavior: 'core'
				// 		});
				// 	}
				// });

				this.y.settings.fetch = (skip, take, d) => {
					model.fetch({
						skip: skip
					}, {
						source: 'scroll.view',
						behavior: 'core'
					});

					gridService
						.invalidate('scroll.view')
						.then(() => {
							const total = model.data().rows.length;
							model.pagination({
								count: total
							}, {
								source: 'scroll.view',
								behavior: 'core'
							});

							d.resolve(total);
						});
				};

				break;
			}
			default:
				this.using(model.paginationChanged.watch(() => {
					this.y.container.reset();
				}));
		}

		this.using(model.scrollChanged.watch(e => {
			if (e.hasChanges('left') || e.hasChanges('top')) {
				this.invalidate();
			}
		}));
	}

	invalidate() {
		Log.info('layout', 'invalidate scroll');

		const table = this.table;
		const scroll = this.model.scroll();

		table.view.scrollLeft(scroll.left);
		table.view.scrollTop(scroll.top);
	}

	get mode() {
		return this.model.scroll().mode;
	}
}<commit_msg>fix infinite scrolliing<commit_after>import {View} from '../view';
import {Log} from '../infrastructure';
import {isFunction} from '../utility';

export class ScrollView extends View {
	constructor(model, table, vscroll, gridService) {
		super(model);

		this.table = table;

		const scroll = model.scroll;
		const rowHeight = model.row().height;
		const settings = {
			threshold: model.pagination().size,
		};

		if (rowHeight > 0 || isFunction(rowHeight)) {
			settings.rowHeight = rowHeight;
		}

		this.y = vscroll.factory(settings);

		this.y.container.drawEvent.on(e => {
			scroll({
				cursor: e.position
			}, {
				source: 'scroll.view',
				behavior: 'core'
			});

			const currentPage = Math.floor(e.position / model.pagination().size);
			model.pagination({
				current: currentPage
			}, {
				source: 'scroll.view',
				behavior: 'core'
			});
		});

		switch (scroll().mode) {
			case 'virtual': {

				const triggers = model.data().triggers;
				Object.keys(triggers).forEach(name =>
					this.using(model[name + 'Changed']
						.watch(e => {
							if (e.tag.behavior !== 'core' && e.tag.source !== 'scroll.view') {
								const changes = Object.keys(e.changes);
								if (triggers[name].find(key => changes.indexOf(key) >= 0)) {
									this.y.container.reset();
								}
							}
						})));

				this.y.settings.fetch = (skip, take, d) => {
					model.fetch({
						skip: skip
					}, {
						source: 'scroll.view',
						behavior: 'core'
					});

					gridService
						.invalidate('scroll.view')
						.then(() => {
							const total = model.data().rows.length;
							model.pagination({
								count: total
							}, {
								source: 'scroll.view',
								behavior: 'core'
							});

							d.resolve(total);
						});
				};

				break;
			}
			default:
				this.using(model.paginationChanged.watch(() => {
					this.y.container.reset();
				}));
		}

		this.using(model.scrollChanged.watch(e => {
			if (e.hasChanges('left') || e.hasChanges('top')) {
				this.invalidate();
			}
		}));
	}

	invalidate() {
		Log.info('layout', 'invalidate scroll');

		const table = this.table;
		const scroll = this.model.scroll();

		table.view.scrollLeft(scroll.left);
		table.view.scrollTop(scroll.top);
	}

	get mode() {
		return this.model.scroll().mode;
	}
}<|endoftext|>


<commit_before>import sqlite3 from 'sqlite3';

import lastUpdate from './lastUpdate';

const MAX_CHUNK_SIZE = 100;

function normalizedExportName(string) {
  return string.toLowerCase().replace(/[-_.]/g, '');
}

function inParam(sql, values) {
  // https://github.com/mapbox/node-sqlite3/issues/721
  return sql.replace('?#', values.map(() => '?').join(','));
}

function arrayToChunks(array, chunkSize) {
  const chunks = [];
  for (let i = 0; i < array.length; i += chunkSize) {
    chunks.push(array.slice(i, i + chunkSize));
  }
  return chunks;
}

export default class ExportsStorage {
  init(dbFilename) {
    return new Promise((resolve, reject) => {
      this.db = new sqlite3.Database(dbFilename);
      this.db.all('PRAGMA table_info(exports)', (pragmaErr, result) => {
        if (pragmaErr) {
          reject(pragmaErr);
          return;
        }
        if (result.length) {
          // DB has already been initialized
          resolve({ isFreshInstall: false });
          return;
        }
        this.db.run(
          `
            CREATE TABLE exports (
              name VARCHAR(100),
              isDefault INTEGER,
              path TEXT,
              packageName VARCHAR(100)
            )
          `,
          (err) => {
            if (err) {
              reject(err);
              return;
            }

            this.db.run(
              `
                CREATE VIRTUAL TABLE exports_fts USING fts4(
                  name,
                  path,
                  isDefault,
                  packageName,
                )
              `,
            );

            this.db.run(
              `
                CREATE TABLE mtimes (
                  path TEXT,
                  mtime NUMERIC
                )
              `,
              (err) => {
                if (err) {
                  reject(err);
                  return;
                }

                resolve({ isFreshInstall: true });
              },
            );
          },
        );
      });
    });
  }

  close() {
    return new Promise((resolve, reject) => {
      this.db.close((err) => {
        if (err) {
          reject(err);
          return;
        }
        resolve();
      });
    });
  }

  needsUpdate(files) {
    if (files.length > MAX_CHUNK_SIZE) {
      // sqlite has a max number for arguments passed. We need to execute in
      // chunks if we exceed the max.
      const promises = arrayToChunks(
        files,
        MAX_CHUNK_SIZE,
      ).map(chunk => this.needsUpdate(chunk));
      return Promise.all(promises)
        .then(chunks => chunks.reduce((a, b) => a.concat(b))); // flatten
    }

    return new Promise((resolve, reject) => {
      const filePaths = files.map(({ path: p }) => p);
      this.db.all(
        inParam(
          `
            SELECT path, mtime FROM mtimes
            WHERE (path IN (?#))
          `,
          filePaths,
        ),
        filePaths,
        (err, items) => {
          if (err) {
            reject(err);
            return;
          }
          const mtimes = {};
          items.forEach(({ path: pathToFile, mtime }) => {
            mtimes[pathToFile] = mtime;
          });
          const filtered = files.filter(
            ({ path: pathToFile, mtime }) => mtime !== mtimes[pathToFile],
          );
          resolve(filtered);
        },
      );
    });
  }

  allFiles() {
    return new Promise((resolve, reject) => {
      this.db.all('SELECT path FROM mtimes', (err, files) => {
        if (err) {
          reject(err);
          return;
        }
        resolve(files.map(({ path }) => path));
      });
    });
  }

  updateMtime(pathToFile, mtime) {
    return new Promise((resolve, reject) => {
      this.db.get('SELECT mtime FROM mtimes WHERE (path = ?)', pathToFile, (
        err,
        item,
      ) => {
        if (err) {
          reject(err);
          return;
        }
        if (item) {
          this.db.run(
            'UPDATE mtimes SET mtime = ? WHERE (path = ?)',
            mtime,
            pathToFile,
            (err) => {
              if (err) {
                reject(err);
                return;
              }
              resolve();
            },
          );
        } else {
          this.db.run(
            'INSERT INTO mtimes (mtime, path) VALUES (?, ?)',
            mtime,
            pathToFile,
            (err) => {
              if (err) {
                reject(err);
                return;
              }
              resolve();
            },
          );
        }
      });
    });
  }

  _insert({ name, pathToFile, isDefault, packageName, additional }) {
    const exportName = isDefault ? normalizedExportName(name) : name;
    return new Promise((resolve, reject) => {
      this.db.run(
        'INSERT INTO exports (name, path, isDefault, packageName) VALUES (?, ?, ?, ?)',
        exportName,
        pathToFile,
        isDefault,
        packageName,
        (err) => {
          if (err) {
            reject(err);
            return;
          }

          if (!additional) {
            this.db.run(
              'INSERT INTO exports_fts VALUES (?, ?, ?, ?)',
              name,
              pathToFile,
              isDefault,
              packageName,
            );
          }

          resolve();
        },
      );
    });
  }

  update({ names, defaultNames, pathToFile, mtime, packageName }) {
    return this.remove(pathToFile).then(() =>
      this.updateMtime(pathToFile, mtime, packageName).then(() => {
        const promises = names.map(name =>
          this._insert({ name, pathToFile, isDefault: false, packageName }));
        promises.push(
          ...defaultNames.map(({ name, additional }) =>
            this._insert({ name, pathToFile, isDefault: true, packageName, additional }),
          ),
        );
        return Promise.all(promises);
      }));
  }

  _remove(pattern, operator = '=') {
    return new Promise((resolve, reject) => {
      this.db.run(
        `DELETE FROM exports WHERE (path ${operator} ?)`,
        pattern,
        (err) => {
          if (err) {
            reject(err);
            return;
          }
          this.db.run(
            `DELETE FROM exports_fts WHERE (path ${operator} ?)`,
            pattern,
          );

          this.db.run(
            `DELETE FROM mtimes WHERE (path ${operator} ?)`,
            pattern,
            (mErr) => {
              if (mErr) {
                reject(mErr);
                return;
              }
              resolve();
            },
          );
        },
      );
    });
  }

  remove(pathToFile) {
    return this._remove(pathToFile);
  }

  removeAll(globPattern) {
    return this._remove(globPattern, 'GLOB');
  }

  purgeDeadNodeModules(workingDirectory) {
    return new Promise((resolve, reject) => {
      this.db.all(
        'SELECT path FROM mtimes WHERE (path LIKE "%/node_modules/%")',
        (err, files) => {
          if (err) {
            reject(err);
            return;
          }
          const promises = files.map(
            ({ path: pathToFile }) => new Promise((removeResolve) => {
              lastUpdate(pathToFile, workingDirectory)
                .then(removeResolve)
                .catch(() => this.remove(pathToFile).then(removeResolve));
            }),
          );
          Promise.all(promises).then(resolve).catch(reject);
        },
      );
    });
  }

  get(variableName) {
    return new Promise((resolve, reject) => {
      this.db.all(
        `
          SELECT name, path, isDefault, packageName
          FROM exports WHERE (
            (name = ? AND isDefault = 0) OR
            (name = ? AND isDefault = 1)
          )
        `,
        variableName,
        normalizedExportName(variableName),
        (err, rows) => {
          if (err) {
            reject(err);
            return;
          }
          resolve(
            rows.map(({ name, path, isDefault, packageName }) => ({
              name,
              path,
              isDefault: !!isDefault,
              packageName,
            })),
          );
        },
      );
    });
  }

  search(variableName) {
    return new Promise((resolve, reject) => {
      this.db.all(
        `
          SELECT name, path, isDefault, packageName
          FROM exports_fts WHERE name MATCH ?
          COLLATE NOCASE
        `,
        variableName,
        (err, rows) => {
          if (err) {
            reject(err);
            return;
          }
          resolve(
            rows.map(({ name, path, isDefault, packageName }) => ({
              name,
              path,
              isDefault: !!isDefault,
              packageName,
            })),
          );
        },
      );
    });
  }
}
<commit_msg>Change full text search index table name, add comment to clarify `additional` flag<commit_after>import sqlite3 from 'sqlite3';

import lastUpdate from './lastUpdate';

const MAX_CHUNK_SIZE = 100;

function normalizedExportName(string) {
  return string.toLowerCase().replace(/[-_.]/g, '');
}

function inParam(sql, values) {
  // https://github.com/mapbox/node-sqlite3/issues/721
  return sql.replace('?#', values.map(() => '?').join(','));
}

function arrayToChunks(array, chunkSize) {
  const chunks = [];
  for (let i = 0; i < array.length; i += chunkSize) {
    chunks.push(array.slice(i, i + chunkSize));
  }
  return chunks;
}

export default class ExportsStorage {
  init(dbFilename) {
    return new Promise((resolve, reject) => {
      this.db = new sqlite3.Database(dbFilename);
      this.db.all('PRAGMA table_info(exports)', (pragmaErr, result) => {
        if (pragmaErr) {
          reject(pragmaErr);
          return;
        }
        if (result.length) {
          // DB has already been initialized
          resolve({ isFreshInstall: false });
          return;
        }
        this.db.run(
          `
            CREATE TABLE exports (
              name VARCHAR(100),
              isDefault INTEGER,
              path TEXT,
              packageName VARCHAR(100)
            )
          `,
          (err) => {
            if (err) {
              reject(err);
              return;
            }

            this.db.run(
              `
                CREATE VIRTUAL TABLE exports_full_text_search_index USING fts4(
                  name,
                  path,
                  isDefault,
                  packageName,
                )
              `,
            );

            this.db.run(
              `
                CREATE TABLE mtimes (
                  path TEXT,
                  mtime NUMERIC
                )
              `,
              (err) => {
                if (err) {
                  reject(err);
                  return;
                }

                resolve({ isFreshInstall: true });
              },
            );
          },
        );
      });
    });
  }

  close() {
    return new Promise((resolve, reject) => {
      this.db.close((err) => {
        if (err) {
          reject(err);
          return;
        }
        resolve();
      });
    });
  }

  needsUpdate(files) {
    if (files.length > MAX_CHUNK_SIZE) {
      // sqlite has a max number for arguments passed. We need to execute in
      // chunks if we exceed the max.
      const promises = arrayToChunks(
        files,
        MAX_CHUNK_SIZE,
      ).map(chunk => this.needsUpdate(chunk));
      return Promise.all(promises)
        .then(chunks => chunks.reduce((a, b) => a.concat(b))); // flatten
    }

    return new Promise((resolve, reject) => {
      const filePaths = files.map(({ path: p }) => p);
      this.db.all(
        inParam(
          `
            SELECT path, mtime FROM mtimes
            WHERE (path IN (?#))
          `,
          filePaths,
        ),
        filePaths,
        (err, items) => {
          if (err) {
            reject(err);
            return;
          }
          const mtimes = {};
          items.forEach(({ path: pathToFile, mtime }) => {
            mtimes[pathToFile] = mtime;
          });
          const filtered = files.filter(
            ({ path: pathToFile, mtime }) => mtime !== mtimes[pathToFile],
          );
          resolve(filtered);
        },
      );
    });
  }

  allFiles() {
    return new Promise((resolve, reject) => {
      this.db.all('SELECT path FROM mtimes', (err, files) => {
        if (err) {
          reject(err);
          return;
        }
        resolve(files.map(({ path }) => path));
      });
    });
  }

  updateMtime(pathToFile, mtime) {
    return new Promise((resolve, reject) => {
      this.db.get('SELECT mtime FROM mtimes WHERE (path = ?)', pathToFile, (
        err,
        item,
      ) => {
        if (err) {
          reject(err);
          return;
        }
        if (item) {
          this.db.run(
            'UPDATE mtimes SET mtime = ? WHERE (path = ?)',
            mtime,
            pathToFile,
            (err) => {
              if (err) {
                reject(err);
                return;
              }
              resolve();
            },
          );
        } else {
          this.db.run(
            'INSERT INTO mtimes (mtime, path) VALUES (?, ?)',
            mtime,
            pathToFile,
            (err) => {
              if (err) {
                reject(err);
                return;
              }
              resolve();
            },
          );
        }
      });
    });
  }

  _insert({ name, pathToFile, isDefault, packageName, additional }) {
    const exportName = isDefault ? normalizedExportName(name) : name;
    return Promise.all([
      new Promise((resolve, reject) => {
        this.db.run(
          'INSERT INTO exports (name, path, isDefault, packageName) VALUES (?, ?, ?, ?)',
          exportName,
          pathToFile,
          isDefault,
          packageName,
          (err) => {
            if (err) {
              reject(err);
              return;
            }

            resolve();
          },
        );
      }),
      new Promise((resolve, reject) => {
        // `additional` is a flag used to identify what to add to the full text search index
        // The reason for this is directory paths get appended to module name which results in noisy
        // search results
        if (!additional) {
          this.db.run(
            'INSERT INTO exports_full_text_search_index VALUES (?, ?, ?, ?)',
            name,
            pathToFile,
            isDefault,
            packageName,
            (err) => {
              if (err) {
                reject(err);
              } else {
                resolve();
              }
            },
          );
        } else {
          resolve();
        }
      }),
    ]);
  }

  update({ names, defaultNames, pathToFile, mtime, packageName }) {
    return this.remove(pathToFile).then(() =>
      this.updateMtime(pathToFile, mtime, packageName).then(() => {
        const promises = names.map(name =>
          this._insert({ name, pathToFile, isDefault: false, packageName }));
        promises.push(
          ...defaultNames.map(({ name, additional }) =>
            this._insert({ name, pathToFile, isDefault: true, packageName, additional }),
          ),
        );
        return Promise.all(promises);
      }));
  }

  _remove(pattern, operator = '=') {
    return new Promise((resolve, reject) => {
      this.db.run(
        `DELETE FROM exports WHERE (path ${operator} ?)`,
        pattern,
        (err) => {
          if (err) {
            reject(err);
            return;
          }
          this.db.run(
            `DELETE FROM exports_full_text_search_index WHERE (path ${operator} ?)`,
            pattern,
          );

          this.db.run(
            `DELETE FROM mtimes WHERE (path ${operator} ?)`,
            pattern,
            (mErr) => {
              if (mErr) {
                reject(mErr);
                return;
              }
              resolve();
            },
          );
        },
      );
    });
  }

  remove(pathToFile) {
    return this._remove(pathToFile);
  }

  removeAll(globPattern) {
    return this._remove(globPattern, 'GLOB');
  }

  purgeDeadNodeModules(workingDirectory) {
    return new Promise((resolve, reject) => {
      this.db.all(
        'SELECT path FROM mtimes WHERE (path LIKE "%/node_modules/%")',
        (err, files) => {
          if (err) {
            reject(err);
            return;
          }
          const promises = files.map(
            ({ path: pathToFile }) => new Promise((removeResolve) => {
              lastUpdate(pathToFile, workingDirectory)
                .then(removeResolve)
                .catch(() => this.remove(pathToFile).then(removeResolve));
            }),
          );
          Promise.all(promises).then(resolve).catch(reject);
        },
      );
    });
  }

  get(variableName) {
    return new Promise((resolve, reject) => {
      this.db.all(
        `
          SELECT name, path, isDefault, packageName
          FROM exports WHERE (
            (name = ? AND isDefault = 0) OR
            (name = ? AND isDefault = 1)
          )
        `,
        variableName,
        normalizedExportName(variableName),
        (err, rows) => {
          if (err) {
            reject(err);
            return;
          }
          resolve(
            rows.map(({ name, path, isDefault, packageName }) => ({
              name,
              path,
              isDefault: !!isDefault,
              packageName,
            })),
          );
        },
      );
    });
  }

  search(variableName) {
    return new Promise((resolve, reject) => {
      this.db.all(
        `
          SELECT name, path, isDefault, packageName
          FROM exports_full_text_search_index WHERE name MATCH ?
          COLLATE NOCASE
        `,
        variableName,
        (err, rows) => {
          if (err) {
            reject(err);
            return;
          }
          resolve(
            rows.map(({ name, path, isDefault, packageName }) => ({
              name,
              path,
              isDefault: !!isDefault,
              packageName,
            })),
          );
        },
      );
    });
  }
}
<|endoftext|>